Compare commits

...

132 Commits

Author SHA1 Message Date
aravind sirsikar 9ddc3c7845 PDCH allocation across two TRX
Implementation:
PCU keeps track of number PDCH across all the TRXs at BTS context and also keeps number
of active PDCHs in each TRX context. This ratio gives the CAPACITY of each TRX.

1st Phase:
	While selection of TRX, functions get_possible_trxs(for multiple TS)
	and get_possible_trxs_sba(Single block allocation, Ex: during RACH)
	gets possible TRXs based on TFIs availability.
2nd Phase: outcome of TRXs(from get_possible_trxs, get_possible_trxs_sba) will
	be fed to get_suitable_trx function. Which does actual load balancing on each TRX.
	And selects the best fit TRX, based on below equation
	PROBABILITY = MAX_PROBABILITY – ((LOAD * 100)/CAPACITY);

	If same PROBABILITY
		Select the one with higher capacity
	Else
		Find the TRX with higher PROBABILITY

	MAX_PROBABILITY  = 65535;
	LOAD: Initialized to 0, Later gets incremented with number of TS allocated.
	and decremented whle TBF deletion, based on number of TS allocated

Below are the test executed on Unit test environment
1)	2 TRXs, with same Capacity
2)	2 TRX with ½ capacity
3)	2 TRXs with ¼ capacity
4)	2 TRX with UL and DL allocation to ensure Tbfs on both direction is hosted on same TRX
5)	2 TRX with same Capacity and  DL TBF creation and deletion scenarios to ensure proper
	load balancing criteria for actual Load.

Below are the Test executed on Integration setup with Ettus B200 setup with 2
	TRXs with same CAPACITY.
1)	TRX allocation during RACH procedure(SBA allocation)
2)	TRX allocation during DL TBF creation with multi time slots(4 time slots)
3)	TRX allocation while Ping Test
4)	TRX allocation while UDP test
5)	TRX allocation for web browsing.
6)	2 MS test with each TRX sharing 1 MS. Received aggregate throughput of
	446 kbps(223 Kbps each)

Limitation:
1)	 BSSGP flow control needs to be tuned since test with 2 MS with each MS on different TRX
	runs for 20 mins
2)	USF resource availibility is not checked while selecting the TRX as existing implementation

Change-Id: Ifb61a7862d55af828383b6dfe728628e546ed12b
Related: OS# 1775
2016-12-30 12:24:38 +05:30
aravind sirsikar d3ae14c18f Sanitizer build fix for invalid value of egprs_puncturing_values
This patch is probable fix for "tbf_dl.cpp:766:65: runtime error: load
of value 32766, which is not a valid value for type
'egprs_puncturing_values'"

Change-Id: Ice54edc7e4a936eb2f2dd8a243673a30dceef542
2016-12-30 11:15:08 +05:30
aravind sirsikar fcf2af400a Handle packet access reject during packet resource request
When Packet resource request is received, PCU will generate the
packet access reject if no resources are present. The encoding is done
based on section 7.1.3.2.1 and 8.1.2.5 of 44.060 version 7.27.0 Release 7.
This patch also includes the test case to validate the generated
packet access reject message.

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

Change-Id: I05ff25124b58905586caa0c0c37023d69724f121
2016-12-30 11:05:00 +05:30
Pravin Kumarvel f595a4eadb Update README with working commit versions for openbsc.
Necessary changes for enabling new feature in openbsc.
2016-12-29 20:23:15 +05:30
Pravin Kumarvel ac82ca15e6 Array indexing for SPB counters in bts statistics.
Array indexing mismatch is corrected for SPB counters.
 (bts_ctr_description with the bts counter declaration).
2016-12-29 20:14:00 +05:30
Pravin Kumarvel 1b465dbadd Will enable padding support for MCS8 to MCS6/MCS3 transition in EGPRS DL. 2016-12-29 20:12:16 +05:30
Pravin Kumarvel 77b5da51ac Update README with working commit versions of osmo components
Necessary changes for build this pcu.
2016-12-29 20:10:51 +05:30
Pravin Kumarvel 22529354e1 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-12-29 20:09:32 +05:30
Pravin Kumarvel 17ee617032 Add compression support in EGPRS PUAN
This adds compression of bitmap in PUAN. The compressed bitmap
is used only if the number of bits in the bitmap does not fit in
the message and there is a gain after compression.
The algorithm is part of libosmocore and so there is dependency
on the libosmocore for compilation.
The algorithm is tested on integration setup by forcing compression.
2016-12-29 20:05:28 +05:30
Pravin Kumarvel 366931baa6 Refactoring write_packet_ack_nack_desc_egprs to prepare for CRBB support 2016-12-29 19:55:22 +05:30
sivasankari ee78bf0882 Adds rate_ctr_init in the startup of osmo-pcu
Issue:Though the rate_ctr framework is used in osmo-pcu for bts statistics,
      the interval counters are always 0.
Fix:rate_ctr_init is added in the startup which arms the timer and hence
    the rate ctr intervals is displayed with proper values.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Related: OS#1789

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

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

Related: OS#1789

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

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

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

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

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

Conflicts:
	tests/tbf/TbfTest.err

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

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

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

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

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

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

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

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

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

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

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

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

To reproduce the error avoided by this patch:

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

This will produce an error like

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

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

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

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

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

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

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

Related: OS#1808

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

This issue has been fixed in this patch.

Related: OS#1811

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

Related: OS#1811

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

Related: OS#1792

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

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

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

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

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

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

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

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

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

Related: OS#1806

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

Related: OS#1806

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

Related: OS#1808

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

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

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

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

Related: OS#1793

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

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

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

Related: OS#1793

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

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

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

The EGPRS resegmentation feature allows PCU to retransmit
RLC blocks of HeaderType1, HeaderType2 by segmenting
them to 2 HeaderType3 blocks(Example MCS5 will be
retransmitted as 2 MCS2 blocks). Table 10.4.8b.2 of 44.060
explains the possible values of SPB in HeadrType3 for DL
direction. The PCU decides to retransmit the
blocks by resegmenting it based on Table 8.1.1.1 of 44.060.
The retransmission MCS is calculated based on current MCS of
the Block and demanded MCS by PCU. Section 10.3a.3.3 of 44.060
shows the HeadrType3 with SPB field present in it

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

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

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

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

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

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

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

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

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

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

This feature provides provision for MS to retransmit
RLC blocks of HeaderType1, HeaderType2 by segmenting
them to 2 HeaderType3 blocks(Example MCS5 will be
retransmitted as 2 MCS2 blocks). Table 10.4.8b.1 of 44.060
explains the possible values of SPB in HeadrType3 for UL
direction. When the MCS is changed at the PCU, PCU directs the
changed MCS to MS by PUAN or UPLINK ASSIGNMENT message along
with RESEGMENT flag, Then MS may decide to retransmit the
blocks by resegmenting it based on Table 8.1.1.1 of 44.060.
The retransmission MCS is calculated based on current MCS of
the Block and demanded MCS by PCU. Section 10.3a.4.3 of 44.060
shows the HeadrType3 with SPB field present in it.

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

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

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

The rationale behind this from GP-091955:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Fixes: Coverity: CID 1351733
2016-02-25 14:03:49 +01:00
80 changed files with 14501 additions and 1085 deletions

17
.gitignore vendored
View File

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

3
.gitreview Normal file
View File

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

View File

@ -1,5 +1,5 @@
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
SUBDIRS = src examples tests
SUBDIRS = include src examples tests
EXTRA_DIST = osmoappdesc.py

13
README
View File

@ -1,7 +1,17 @@
This is an implementation of Packet Control Unit (PCU) according to TS 04.60
his is an implementation of Packet Control Unit (PCU) according to TS 04.60
The PCU is part of BSS, so it connects directly to SGSN.
For this PCU the dependent libosmocore patch is in contrib which has to be
applied on top of libosmocore 2ae5f186929a720b5604e2bd19ef54606b37fb87.
This PCU enables
* Support for EPDAN
* Support for PUAN with CRBB
Feature support for Deactivate PDP context Request from SGSN VTY.
which has two patches are present in contrib has to be
applied on top of openbsc 80abe522e2ddc979d994530f21b103808fc465d7.
== Current limitations ==
@ -9,7 +19,6 @@ The PCU is part of BSS, so it connects directly to SGSN.
* No fixed allocation support
* No extended dynamic allocation support
* No unacknowledged mode operation
* No PCCCH/PBCCH support
* Only single slot assignment on uplink direction
* No half-duplex class support (only semi-duplex)
* No handover support

View File

@ -3,6 +3,9 @@ AC_INIT([osmo-pcu],
m4_esyscmd([./git-version-gen .tarball-version]),
[osmocom-net-gprs@lists.osmocom.org])
dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
AC_CONFIG_AUX_DIR([.])
AM_INIT_AUTOMAKE([dist-bzip2])
AC_CONFIG_TESTDIR(tests)
@ -16,6 +19,13 @@ AC_PROG_CXX
AC_PROG_INSTALL
LT_INIT
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
AC_MSG_WARN([You need to install pkg-config])
fi
PKG_PROG_PKG_CONFIG([0.20])
dnl checks for header files
AC_HEADER_STDC
@ -35,6 +45,25 @@ AC_ARG_ENABLE(sysmocom-dsp,
AC_MSG_RESULT([$enable_sysmocom_dsp])
AM_CONDITIONAL(ENABLE_SYSMODSP, test "x$enable_sysmocom_dsp" = "xyes")
AC_MSG_CHECKING([whether to enable direct PHY access for PDCH of NuRAN Wireless Litecell 1.5 BTS])
AC_ARG_ENABLE(lc15bts-phy,
AC_HELP_STRING([--enable-lc15bts-phy],
[enable code for Litecell 1.5 PHY [default=no]]),
[enable_lc15bts_phy="$enableval"],[enable_lc15bts_phy="no"])
AC_ARG_WITH([litecell15], [AS_HELP_STRING([--with-litecell15=INCLUDE_DIR], [Location of the litecell 1.5 API header files])],
[litecell15_incdir="$withval"],[litecell15_incdir="$incdir"])
AC_SUBST([LITECELL15_INCDIR], $litecell15_incdir)
AC_MSG_RESULT([$enable_lc15bts_phy])
AM_CONDITIONAL(ENABLE_LC15BTS_PHY, test "x$enable_lc15bts_phy" = "xyes")
if test "$enable_litecell15" = "yes"; then
oldCPPFLAGS=$CPPFLAGS
CPPFLAGS="$CPPFLAGS -I$LITECELL15_INCDIR -I$srcdir/include $LIBOSMOCORE_CFLAGS"
AC_CHECK_HEADER([nrw/litecell15/litecell15.h],[],
[AC_MSG_ERROR([nrw/litecell15/litecell15.h can not be found in $litecell15_incdir])],
[#include <nrw/litecell15/litecell15.h>])
CPPFLAGS=$oldCPPFLAGS
fi
AC_ARG_ENABLE([vty_tests],
AC_HELP_STRING([--enable-vty-tests],
[Include the VTY tests in make check [default=no]]),
@ -50,7 +79,11 @@ AC_MSG_CHECKING([whether to enable VTY tests])
AC_MSG_RESULT([$enable_vty_tests])
AM_CONDITIONAL(ENABLE_VTY_TESTS, test "x$enable_vty_tests" = "xyes")
STD_DEFINES_AND_INCLUDES="-Wall"
AC_SUBST(STD_DEFINES_AND_INCLUDES)
AC_OUTPUT(
include/Makefile
src/Makefile
examples/Makefile
tests/Makefile

View File

@ -0,0 +1,157 @@
From df53ee248964652e64ced971ad5ef7ce954f5bbd Mon Sep 17 00:00:00 2001
From: Pravin Kumarvel <pmanohar@radisys.com>
Date: Thu, 22 Dec 2016 12:06:07 +0530
Subject: [PATCH] Add function to get uninterrupted bit run
Function bitvec_rl_curbit added to get number of uninterrupted
bits run in vector starting from the current bit till max number
of bits.
Test case is added to check bitvec_rl_curbit.
---
include/osmocom/core/bitvec.h | 1 +
src/bitvec.c | 45 +++++++++++++++++++++++++++++++++++++++++++
tests/bitvec/bitvec_test.c | 41 +++++++++++++++++++++++++++++++++++++++
tests/bitvec/bitvec_test.ok | 2 ++
4 files changed, 89 insertions(+)
diff --git a/include/osmocom/core/bitvec.h b/include/osmocom/core/bitvec.h
index 19e2af8..0e17ba7 100644
--- a/include/osmocom/core/bitvec.h
+++ b/include/osmocom/core/bitvec.h
@@ -89,6 +89,7 @@ char bit_value_to_char(enum bit_value v);
void bitvec_to_string_r(const struct bitvec *bv, char *str);
void bitvec_zero(struct bitvec *bv);
unsigned bitvec_rl(const struct bitvec *bv, bool b);
+unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits);
void bitvec_shiftl(struct bitvec *bv, unsigned int n);
int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits);
unsigned int bitvec_add_array(struct bitvec *bv, const uint32_t *array,
diff --git a/src/bitvec.c b/src/bitvec.c
index 38148ac..c895cff 100644
--- a/src/bitvec.c
+++ b/src/bitvec.c
@@ -575,6 +575,51 @@ unsigned bitvec_rl(const struct bitvec *bv, bool b)
return bv->cur_bit;
}
+/*! \brief Return number (bits) of uninterrupted bit run in vector
+ * starting from the current bit
+ * \param[in] bv The boolean vector to work on
+ * \param[in] b The boolean, sequence of 1's or 0's to be checked
+ * \param[in] max_bits Total Number of Uncmopresed bits
+ * \returns Number of consecutive bits of \p b in \p bv and cur_bit will
+ * \go to cur_bit + number of consecutive bit
+ */
+unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits)
+{
+ unsigned i = 0;
+ unsigned j = 8;
+ int temp_res = 0;
+ int count = 0;
+ unsigned readIndex = bv->cur_bit;
+ unsigned remaining_bits = max_bits % 8;
+ unsigned remaining_bytes = max_bits / 8;
+ unsigned byte_mask = 0xFF;
+
+ if (readIndex % 8) {
+ for (j -= (readIndex % 8) ; j > 0 ; j--) {
+ if (readIndex < max_bits && bitvec_read_field(bv, &readIndex, 1) == b)
+ temp_res++;
+ else {
+ bv->cur_bit--;
+ return temp_res;
+ }
+ }
+ }
+ for (i = (readIndex / 8);
+ i < (remaining_bits ? remaining_bytes + 1 : remaining_bytes);
+ i++, count++) {
+ if ((b ? byte_mask : 0) != bv->data[i]) {
+ bv->cur_bit = (count * 8 +
+ leading_bits(bv->data[i], b) + readIndex);
+ return count * 8 +
+ leading_bits(bv->data[i], b) + temp_res;
+ }
+ }
+ bv->cur_bit = (temp_res + (count * 8)) + readIndex;
+ if (bv->cur_bit > max_bits)
+ bv->cur_bit = max_bits;
+ return (bv->cur_bit - readIndex + temp_res);
+}
+
/*! \brief Shifts bitvec to the left, n MSB bits lost */
void bitvec_shiftl(struct bitvec *bv, unsigned n)
{
diff --git a/tests/bitvec/bitvec_test.c b/tests/bitvec/bitvec_test.c
index a98a91c..afcc942 100644
--- a/tests/bitvec/bitvec_test.c
+++ b/tests/bitvec/bitvec_test.c
@@ -150,6 +150,18 @@ static inline void test_array_item(unsigned t, struct bitvec *b, unsigned int n,
}
}
+static inline void test_bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits,
+ int result )
+{
+ int num = 0;
+ int readIndex = bv->cur_bit;
+ OSMO_ASSERT(bv->cur_bit < max_bits);
+ num = bitvec_rl_curbit(bv, b, max_bits);
+ readIndex += num;
+ OSMO_ASSERT(bv->cur_bit == readIndex);
+ OSMO_ASSERT(num == result);
+}
+
static void test_array()
{
struct bitvec b;
@@ -245,6 +257,35 @@ int main(int argc, char **argv)
test_array();
+ printf("\nbitvec_runlength....\n");
+
+ bitvec_zero(&bv);
+ bitvec_set_uint(&bv, 0xff, 8);
+ bv.cur_bit -= 8;
+ test_bitvec_rl_curbit(&bv, 1, 64, 8);
+
+ bitvec_zero(&bv);
+ bitvec_set_uint(&bv, 0xfc, 8);
+ bv.cur_bit -= 8;
+ test_bitvec_rl_curbit(&bv, 1, 64, 6);
+
+ bitvec_zero(&bv);
+ test_bitvec_rl_curbit(&bv, 0, 52, 52);
+
+ bitvec_zero(&bv);
+ bitvec_set_uint(&bv, 0xfc, 8);
+ bv.cur_bit -= 2;
+ test_bitvec_rl_curbit(&bv, 0, 64, 58);
+
+ bitvec_zero(&bv);
+ bitvec_set_uint(&bv, 0x07, 8);
+ bitvec_set_uint(&bv, 0xf8, 8);
+ bv.cur_bit -= 11;
+ test_bitvec_rl_curbit(&bv, 1, 64, 8);
+
+ bitvec_zero(&bv);
+ test_bitvec_rl_curbit(&bv, 1, 64, 0);
+
printf("\nbitvec ok.\n");
return 0;
diff --git a/tests/bitvec/bitvec_test.ok b/tests/bitvec/bitvec_test.ok
index e256108..6281973 100644
--- a/tests/bitvec/bitvec_test.ok
+++ b/tests/bitvec/bitvec_test.ok
@@ -166,4 +166,6 @@ bits: 17, est: 1153, real: 1153, x: 0, y: 0
........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........
........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........
........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........
+bitvec_runlength....
+
bitvec ok.
--
1.9.1

View File

@ -0,0 +1,259 @@
From 4ce4f62ae647a8ff852f29815011186f15ee1d16 Mon Sep 17 00:00:00 2001
From: Pravin Kumarvel <pmanohar@radisys.com>
Date: Thu, 29 Dec 2016 20:17:14 +0530
Subject: [PATCH 1/2] Support Deactivate PDP Context Request from network
Enable Deactivate PDP context based on the IMSI of the subscriber.
When there are PDP contexts present for a MM context,
PDP context will be deactivated along with GMM Detach(MM context deletion).
If there are no PDP present, MM context will be deleted to avoid
further PDP context request from the MS.
Test cases is added to check this functionality.
---
openbsc/include/openbsc/gprs_sgsn.h | 2 +
openbsc/src/gprs/gprs_sgsn.c | 36 ++++++++++
openbsc/tests/sgsn/sgsn_test.c | 132 ++++++++++++++++++++++++++++++++++++
openbsc/tests/sgsn/sgsn_test.ok | 4 ++
4 files changed, 174 insertions(+)
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h
index 24e286c..b3f250d 100644
--- a/openbsc/include/openbsc/gprs_sgsn.h
+++ b/openbsc/include/openbsc/gprs_sgsn.h
@@ -369,6 +369,8 @@ void sgsn_inst_init(void);
* ottherwise lost state (recovery procedure) */
int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn);
+void drop_gmm_ctx_for_ms(const char *imsi);
+
char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len);
/*
diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c
index e85e1a9..94fcd5f 100644
--- a/openbsc/src/gprs/gprs_sgsn.c
+++ b/openbsc/src/gprs/gprs_sgsn.c
@@ -669,6 +669,42 @@ static void drop_one_pdp(struct sgsn_pdp_ctx *pdp)
}
}
+/*
+ * High-level function to be called for PDP deactivation initiated from SGSN VTY.
+ * When there are PDP contexts present for a MM context, PDP context will be
+ * deactivated along with GMM Detach(MM context deletion).
+ * If there are no PDP present, MM context will be deleted to avoid further
+ * PDP context activation for that MS.
+ */
+void drop_gmm_ctx_for_ms(const char *imsi)
+{
+ OSMO_ASSERT(imsi != NULL);
+ struct sgsn_mm_ctx *mm;
+ struct sgsn_pdp_ctx *pdp;
+
+ /* Search the MM context subscriber */
+ mm = sgsn_mm_ctx_by_imsi(imsi);
+ LOGMMCTXP(LOGL_INFO, mm, "SGSN intiated Deactivate PDP request\n");
+ if (mm) {
+ /* Search the PDP for this subscriber */
+ if (llist_empty(&mm->pdp_list)) {
+ /*
+ * Deleting mm context for the subscriber when no PDP
+ * context is present.
+ */
+ LOGMMCTXP(LOGL_NOTICE, mm, "No PDP context to deactivate\n");
+ gsm0408_gprs_access_cancelled(mm, GMM_CAUSE_GPRS_NOTALLOWED);
+ } else {
+ llist_for_each_entry(pdp, &mm->pdp_list, list) {
+ gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_DEACT_REGULAR);
+ LOGPDPCTXP(LOGL_INFO, pdp, "PDP Deactivation "
+ "Successful\n");
+ }
+ }
+ } else
+ LOGMMCTXP(LOGL_NOTICE, mm, "No MM context to deactivate\n");
+}
+
/* High-level function to be called in case a GGSN has disappeared or
* otherwise lost state (recovery procedure) */
int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn)
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
index b4bcaf6..f8bf186 100644
--- a/openbsc/tests/sgsn/sgsn_test.c
+++ b/openbsc/tests/sgsn/sgsn_test.c
@@ -38,14 +38,17 @@
#include <osmocom/core/rate_ctr.h>
#include <stdio.h>
+#include <gtp.h>
void *tall_bsc_ctx;
+struct gsn_t gsn_ctx;
static struct sgsn_instance sgsn_inst = {
.config_file = "osmo_sgsn.cfg",
.cfg = {
.gtp_statedir = "./",
.auth_policy = SGSN_AUTH_POLICY_CLOSED,
},
+ .gsn = &gsn_ctx,
};
struct sgsn_instance *sgsn = &sgsn_inst;
unsigned sgsn_tx_counter = 0;
@@ -2363,6 +2366,134 @@ static void test_ggsn_selection(void)
cleanup_test();
}
+static void test_pdp_deactivation_with_pdp_ctx(void)
+{
+ struct apn_ctx *actxs[4];
+ struct sgsn_ggsn_ctx *ggc, *ggcs[3];
+ struct gsm_subscriber *s1;
+ const char *imsi1 = "12345678901";
+ struct sgsn_mm_ctx *ctx;
+ struct gprs_ra_id raid = { 0, };
+ uint32_t local_tlli = 0xffeeddcc;
+ enum gsm48_gsm_cause gsm_cause;
+ struct tlv_parsed tp;
+ uint8_t apn_enc[GSM_APN_LENGTH + 10];
+ struct sgsn_subscriber_pdp_data *pdp_data;
+ char apn_str[GSM_APN_LENGTH];
+
+ printf("Testing Pdp deactivation for MS with pdp ctx\n");
+
+ /* Check for emptiness */
+ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL);
+
+ /* Create a context */
+ OSMO_ASSERT(count(gprs_llme_list()) == 0);
+ ctx = alloc_mm_ctx(local_tlli, &raid);
+ strncpy(ctx->imsi, imsi1, sizeof(ctx->imsi) - 1);
+
+ /* Allocate and attach a subscriber */
+ s1 = gprs_subscr_get_or_create_by_mmctx(ctx);
+ assert_subscr(s1, imsi1);
+
+ struct sgsn_pdp_ctx *pdp;
+
+ tp.lv[GSM48_IE_GSM_APN].len = 0;
+ tp.lv[GSM48_IE_GSM_APN].val = apn_enc;
+
+ tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].len = 2;
+ tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].val = apn_enc;
+
+ tp.lv[OSMO_IE_GSM_REQ_QOS].len = 14;
+ tp.lv[OSMO_IE_GSM_REQ_QOS].val = apn_enc;
+
+ ggcs[0] = sgsn_ggsn_ctx_find_alloc(0);
+
+ actxs[0] = sgsn_apn_ctx_find_alloc("test.apn", "123456");
+ actxs[0]->ggsn = ggcs[0];
+
+ pdp_data = sgsn_subscriber_pdp_data_alloc(s1->sgsn_data);
+ pdp_data->context_id = 1;
+ pdp_data->pdp_type = 0x0121;
+ strncpy(pdp_data->apn_str, "*", sizeof(pdp_data->apn_str)-1);
+
+ /* Resolve GGSNs */
+ tp.lv[GSM48_IE_GSM_APN].len =
+ gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Test.Apn");
+
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str);
+
+ OSMO_ASSERT(ggc != NULL);
+ OSMO_ASSERT(ggc->id == 0);
+
+ ggc = sgsn_ggsn_ctx_alloc(ggc->id);
+ /* Create a pdp context */
+ pdp = sgsn_create_pdp_ctx(ggc, ctx, 5, &tp);
+
+ /* Intiate PDP deactivation for imsi1 */
+ drop_gmm_ctx_for_ms(imsi1);
+ gsm48_tx_gsm_deact_pdp_acc(pdp);
+ gsm0408_gprs_access_cancelled(ctx, GMM_CAUSE_GPRS_NOTALLOWED);
+
+ /* Cleanup */
+
+ subscr_put(s1);
+
+ sgsn_apn_ctx_free(actxs[0]);
+ sgsn_ggsn_ctx_free(ggcs[0]);
+ sgsn_ggsn_ctx_free(ggc);
+ talloc_free(pdp);
+
+ cleanup_test();
+}
+
+static void test_pdp_deactivation_with_only_mm_ctx(void)
+{
+ struct gsm_subscriber *s1;
+ const char *imsi1 = "1234567890";
+ struct sgsn_mm_ctx *ctx;
+ struct gprs_ra_id raid = { 0, };
+ uint32_t local_tlli = 0xffeeddcc;
+
+ printf("Testing Pdp deactivation for MS with only MM ctx\n");
+
+ /* Check for emptiness */
+ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL);
+
+ /* Create a context */
+ OSMO_ASSERT(count(gprs_llme_list()) == 0);
+ ctx = alloc_mm_ctx(local_tlli, &raid);
+ strncpy(ctx->imsi, imsi1, sizeof(ctx->imsi) - 1);
+
+ /* Allocate and attach a subscriber */
+ s1 = gprs_subscr_get_or_create_by_mmctx(ctx);
+ assert_subscr(s1, imsi1);
+
+ /* Intiate PDP deactivation for imsi1 */
+ drop_gmm_ctx_for_ms(imsi1);
+
+ cleanup_test();
+}
+
+static void test_pdp_deactivation_without_mm_ctx(void)
+{
+ const char *imsi1 = "1234567890";
+
+ printf("Testing Pdp deactivation for MS without MM ctx\n");
+
+ /* Intiate PDP deactivation for imsi1 */
+ drop_gmm_ctx_for_ms(imsi1);
+
+ cleanup_test();
+}
+
+static void test_pdp_deactivation(void)
+{
+ printf("Testing pdp deactivation\n");
+
+ test_pdp_deactivation_with_only_mm_ctx();
+ test_pdp_deactivation_with_pdp_ctx();
+ test_pdp_deactivation_without_mm_ctx();
+}
static struct log_info_cat gprs_categories[] = {
[DMM] = {
.name = "DMM",
@@ -2454,6 +2585,7 @@ int main(int argc, char **argv)
test_gmm_routing_areas();
test_apn_matching();
test_ggsn_selection();
+ test_pdp_deactivation();
printf("Done\n");
talloc_report_full(osmo_sgsn_ctx, stderr);
diff --git a/openbsc/tests/sgsn/sgsn_test.ok b/openbsc/tests/sgsn/sgsn_test.ok
index c7a53b9..6159bc9 100644
--- a/openbsc/tests/sgsn/sgsn_test.ok
+++ b/openbsc/tests/sgsn/sgsn_test.ok
@@ -34,4 +34,8 @@ Testing routing area changes
- RA Update Request (RA 2 -> RA 2)
Testing APN matching
Testing GGSN selection
+Testing pdp deactivation
+Testing Pdp deactivation for MS with only MM ctx
+Testing Pdp deactivation for MS with pdp ctx
+Testing Pdp deactivation for MS without MM ctx
Done
--
1.9.1

View File

@ -0,0 +1,49 @@
From 29f7bd1023fc698ebdb19b93f8029b2559392f17 Mon Sep 17 00:00:00 2001
From: Pravin Kumarvel <pmanohar@radisys.com>
Date: Thu, 29 Dec 2016 20:18:49 +0530
Subject: [PATCH 2/2] Trigger Deactivate PDP context Request from SGSN VTY
In SGSN for acl based authorization IMSI values of all registered
MS are maintained.
Through imsi_acl_del Deactivate PDP context Request from network
can be triggered.
Hence, It will remove all PDP context related to that MS.
---
openbsc/src/gprs/gprs_gmm.c | 2 ++
openbsc/src/gprs/sgsn_vty.c | 6 ++++--
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
index 363b457..77f94f8 100644
--- a/openbsc/src/gprs/gprs_gmm.c
+++ b/openbsc/src/gprs/gprs_gmm.c
@@ -2535,6 +2535,8 @@ static int gsm0408_rcv_gsm(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
break;
case GSM48_MT_GSM_DEACT_PDP_ACK:
rc = gsm48_rx_gsm_deact_pdp_ack(mmctx, msg);
+ if (sgsn_auth_state(mmctx) != SGSN_AUTH_ACCEPTED)
+ gsm0408_gprs_access_cancelled(mmctx, GMM_CAUSE_GPRS_NOTALLOWED);
break;
case GSM48_MT_GSM_STATUS:
rc = gsm48_rx_gsm_status(mmctx, msg);
diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c
index 21c865b..feeb0ff 100644
--- a/openbsc/src/gprs/sgsn_vty.c
+++ b/openbsc/src/gprs/sgsn_vty.c
@@ -573,9 +573,11 @@ DEFUN(imsi_acl, cfg_imsi_acl_cmd,
if (!strcmp(op, "add"))
rc = sgsn_acl_add(imsi, g_cfg);
- else
+ else {
+ vty_out(vty, "%% Network initiated PDP deactivate%s", VTY_NEWLINE);
+ drop_gmm_ctx_for_ms(imsi);
rc = sgsn_acl_del(imsi, g_cfg);
-
+ }
if (rc < 0) {
vty_out(vty, "%% unable to %s ACL%s", op, VTY_NEWLINE);
--
1.9.1

71
contrib/jenkins.sh Executable file
View File

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

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

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

@ -0,0 +1,3 @@
etc/osmocom/osmo-pcu.cfg
usr/bin/osmo-pcu
usr/include/osmocom/pcu/pcuif_proto.h

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)

2
include/Makefile.am Normal file
View File

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

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

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

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>
@ -65,12 +66,19 @@ static const struct rate_ctr_desc bts_ctr_description[] = {
{ "rlc.restarted", "RLC Restarted "},
{ "rlc.stalled", "RLC Stalled "},
{ "rlc.nacked", "RLC Nacked "},
{ "rlc.final_block_resent", "RLC Final Blk resent "},
{ "rlc.ass.timedout", "RLC Assign Timeout "},
{ "rlc.ass.failed", "RLC Assign Failed "},
{ "rlc.ack.timedout", "RLC Ack Timeout "},
{ "rlc.ack.failed", "RLC Ack Failed "},
{ "rlc.rel.timedout", "RLC Release Timeout "},
{ "rlc.late-block", "RLC Late Block "},
{ "rlc.sent-dummy", "RLC Sent Dummy "},
{ "rlc.sent-control", "RLC Sent Control "},
{ "rlc.dl_bytes", "RLC DL Bytes "},
{ "rlc.dl_payload_bytes", "RLC DL Payload Bytes "},
{ "rlc.ul_bytes", "RLC UL Bytes "},
{ "rlc.ul_payload_bytes", "RLC UL Payload Bytes "},
{ "decode.errors", "Decode Errors "},
{ "sba.allocated", "SBA Allocated "},
{ "sba.freed", "SBA Freed "},
@ -78,7 +86,56 @@ static const struct rate_ctr_desc bts_ctr_description[] = {
{ "llc.timeout", "Timedout Frames "},
{ "llc.dropped", "Dropped Frames "},
{ "llc.scheduled", "Scheduled Frames "},
{ "llc.dl_bytes", "RLC encapsulated PDUs"},
{ "llc.ul_bytes", "full PDUs received "},
{ "rach.requests", "RACH requests "},
{ "11bit_rach.requests", "11BIT_RACH requests "},
{ "spb.uplink_first_segment", "First seg of UL SPB "},
{ "spb.uplink_second_segment", "Second seg of UL SPB "},
{ "spb.downlink_first_segment", "First seg of DL SPB "},
{ "spb.downlink_second_segment","Second seg of DL SPB "},
{ "immediate.assignment_UL", "Immediate Assign UL "},
{ "immediate.assignment_rej", "Immediate Assign Rej "},
{ "immediate.assignment_DL", "Immediate Assign DL "},
{ "channel.request_description","Channel Request Desc "},
{ "pkt.ul_assignment", "Packet UL Assignment "},
{ "pkt.access_reject", "Packet Access Reject "},
{ "pkt.dl_assignment", "Packet DL Assignment "},
{ "ul.control", "UL control Block "},
{ "ul.assignment_poll_timeout", "UL Assign Timeout "},
{ "ul.assignment_failed", "UL Assign Failed "},
{ "dl.assignment_timeout", "DL Assign Timeout "},
{ "dl.assignment_failed", "DL Assign Failed "},
{ "pkt.ul_ack_nack_timeout", "PUAN Poll Timeout "},
{ "pkt.ul_ack_nack_failed", "PUAN poll Failed "},
{ "pkt.dl_ack_nack_timeout", "PDAN poll Timeout "},
{ "pkt.dl_ack_nack_failed", "PDAN poll Failed "},
{ "gprs.downlink_cs1", "CS1 downlink "},
{ "gprs.downlink_cs2", "CS2 downlink "},
{ "gprs.downlink_cs3", "CS3 downlink "},
{ "gprs.downlink_cs4", "CS4 downlink "},
{ "egprs.downlink_mcs1", "MCS1 downlink "},
{ "egprs.downlink_mcs2", "MCS2 downlink "},
{ "egprs.downlink_mcs3", "MCS3 downlink "},
{ "egprs.downlink_mcs4", "MCS4 downlink "},
{ "egprs.downlink_mcs5", "MCS5 downlink "},
{ "egprs.downlink_mcs6", "MCS6 downlink "},
{ "egprs.downlink_mcs7", "MCS7 downlink "},
{ "egprs.downlink_mcs8", "MCS8 downlink "},
{ "egprs.downlink_mcs9", "MCS9 downlink "},
{ "gprs.uplink_cs1", "CS1 Uplink "},
{ "gprs.uplink_cs2", "CS2 Uplink "},
{ "gprs.uplink_cs3", "CS3 Uplink "},
{ "gprs.uplink_cs4", "CS4 Uplink "},
{ "egprs.uplink_mcs1", "MCS1 Uplink "},
{ "egprs.uplink_mcs2", "MCS2 Uplink "},
{ "egprs.uplink_mcs3", "MCS3 Uplink "},
{ "egprs.uplink_mcs4", "MCS4 Uplink "},
{ "egprs.uplink_mcs5", "MCS5 Uplink "},
{ "egprs.uplink_mcs6", "MCS6 Uplink "},
{ "egprs.uplink_mcs7", "MCS7 Uplink "},
{ "egprs.uplink_mcs8", "MCS8 Uplink "},
{ "egprs.uplink_mcs9", "MCS9 Uplink "},
};
static const struct rate_ctr_group_desc bts_ctrg_desc = {
@ -131,11 +188,13 @@ BTS::BTS()
{
memset(&m_bts, 0, sizeof(m_bts));
m_bts.bts = this;
m_total_pdch = 0;
/* initialize back pointers */
for (size_t trx_no = 0; trx_no < ARRAY_SIZE(m_bts.trx); ++trx_no) {
struct gprs_rlcmac_trx *trx = &m_bts.trx[trx_no];
trx->trx_no = trx_no;
trx->num_pdch = 0;
trx->current_load = 0;
trx->bts = this;
for (size_t ts_no = 0; ts_no < ARRAY_SIZE(trx->pdch); ++ts_no) {
@ -360,9 +419,82 @@ int BTS::tfi_find_free(enum gprs_rlcmac_tbf_direction dir,
uint8_t *_trx, int8_t use_trx)
{
struct gprs_rlcmac_pdch *pdch;
uint32_t free_tfis;
uint32_t free_tfis = 0xffffffff;
uint8_t ts, tfi;
bool possible_trx[8];
int ret;
/* This function will get list of possible TRXs */
ret = get_possible_trxs(dir, possible_trx, use_trx);
if (ret < 0)
return -EBUSY;
if (use_trx >= 0)
*_trx = use_trx;
else
*_trx = get_suitable_trx(possible_trx);
LOGP(DRLCMAC, LOGL_DEBUG,
"Searching for first unallocated TFI: TRX=%d\n", *_trx);
for (ts = 0; ts < 8; ts++) {
pdch = &m_bts.trx[*_trx].pdch[ts];
free_tfis &= ~pdch->assigned_tfi(dir);
}
/* find the first */
for (tfi = 0; tfi < 32; tfi++) {
if (free_tfis & 1 << tfi)
break;
}
OSMO_ASSERT(tfi < 32);
LOGP(DRLCMAC, LOGL_DEBUG, " Found TFI=%d.\n", tfi);
return tfi;
}
/*
* Search for free TFI and return TFI, TRX.
* This method returns the first TFI that is currently not used in any PDCH of
* a TRX. The first TRX that contains such an TFI is returned. Negative values
* indicate errors.
*/
int BTS::get_possible_trxs_sba(
bool *_trx)
{
uint8_t trx;
bool has_pdch = false;
uint8_t trx_from, trx_to, trx, ts, tfi;
for (trx = 0; trx < 8; trx++) {
if (bts_data()->trx[trx].num_pdch) {
_trx[trx] = true;
has_pdch = true;
LOGP(DRLCMAC, LOGL_DEBUG, " Valid TRX=%d.\n", trx);
} else {
_trx[trx] = false;
LOGP(DRLCMAC, LOGL_DEBUG, " Not valid TRX=%d.\n", trx);
}
}
if (has_pdch)
return 0;
return -EINVAL;
}
/*
* This method loops through all possible TRX and
* returns subset of possible TRXs based on availability.
*/
int BTS::get_possible_trxs(enum gprs_rlcmac_tbf_direction dir,
bool *_trx, int8_t use_trx)
{
struct gprs_rlcmac_pdch *pdch;
uint32_t free_tfis;
uint32_t is_tfis_available = false;
bool has_pdch = false;
uint8_t trx_from, trx_to, trx, ts;
if (use_trx >= 0 && use_trx < 8)
trx_from = trx_to = use_trx;
@ -385,8 +517,12 @@ int BTS::tfi_find_free(enum gprs_rlcmac_tbf_direction dir,
trx_has_pdch = true;
has_pdch = true;
}
if (trx_has_pdch && free_tfis)
break;
if (trx_has_pdch && free_tfis) {
_trx[trx] = true;
is_tfis_available = true;
} else {
_trx[trx] = false;
}
free_tfis = 0;
}
@ -395,26 +531,69 @@ int BTS::tfi_find_free(enum gprs_rlcmac_tbf_direction dir,
return -EINVAL;
}
if (!free_tfis) {
if (!is_tfis_available) {
LOGP(DRLCMAC, LOGL_NOTICE, "No TFI available.\n");
return -EBUSY;
}
return 0;
}
/*
* This method loops through all subset of TRXs provided by
* get_possible_trxs and does the load balancing algorithm. and
* selects best TRX possible based on load and capacity
*/
int BTS::get_suitable_trx(bool *avail_trx)
{
uint8_t trx_from, trx_to, trx;
uint32_t select_probability = 0;
uint32_t temp_probability = 0;
int selected_trx = -1;
bool is_better_trx = false;
LOGP(DRLCMAC, LOGL_DEBUG,
"Searching for first unallocated TFI: TRX=%d\n", trx);
trx_from = 0;
trx_to = 7;
/* find the first */
for (tfi = 0; tfi < 32; tfi++) {
if (free_tfis & 1 << tfi)
break;
for (trx = trx_from; trx <= trx_to; trx++) {
/* Check if this TRX is in possible list */
if (!avail_trx[trx])
continue;
is_better_trx = false;
temp_probability = MAX_LOAD_PROBABILITY -
(get_num_pdch() * 100 *
m_bts.trx[trx].current_load)
/ m_bts.trx[trx].num_pdch;
LOGP(DRLCMAC, LOGL_DEBUG, "trx(%d) cur load(%d)"
" numpdch(%d) prob1(%u) seleprob(%u)"
" btsnumpdch(%d)\n", trx,
m_bts.trx[trx].current_load,
m_bts.trx[trx].num_pdch,
temp_probability, select_probability,
get_num_pdch());
if (temp_probability >= select_probability) {
if (temp_probability > select_probability)
is_better_trx = true;
else if (temp_probability == select_probability)
if (selected_trx >= 0 || selected_trx < 8)
if (m_bts.trx[selected_trx].num_pdch
< m_bts.trx[trx].num_pdch)
is_better_trx = true;
}
if (is_better_trx) {
selected_trx = trx;
select_probability =
temp_probability;
LOGP(DRLCMAC, LOGL_DEBUG, "selected pro(%u)"
"selected_trx(%d)\n",
select_probability, selected_trx);
}
}
OSMO_ASSERT(tfi < 32);
LOGP(DRLCMAC, LOGL_DEBUG, " Found TFI=%d.\n", tfi);
*_trx = trx;
return tfi;
return selected_trx;
}
int BTS::rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn)
@ -459,100 +638,191 @@ int BTS::rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn)
return 0;
}
int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
int BTS::rcv_rach(uint16_t ra, uint32_t Fn, int16_t qta, uint8_t is_11bit,
enum ph_burst_type burst_type)
{
struct gprs_rlcmac_ul_tbf *tbf = NULL;
uint8_t trx_no, ts_no = 0;
uint8_t sb = 0;
uint32_t sb_fn = 0;
int rc;
int rc = 0;
int plen;
uint8_t usf = 7;
uint8_t tsc;
uint16_t ta;
uint8_t tsc = 0, ta = qta2ta(qta);
uint16_t ms_class = 0;
uint16_t priority = 0;
bool failure = false;
rach_frame();
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF on RACH, so we provide "
"one:\n");
if ((ra & 0xf8) == 0x70) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block "
"allocation\n");
sb = 1;
} else if (m_bts.force_two_phase) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single phase access, "
"but we force two phase access\n");
sb = 1;
}
if (qta < 0)
qta = 0;
if (qta > 252)
qta = 252;
if (is_11bit)
rach_frame_11bit();
ta = qta >> 2;
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF on RACH, "
"so we provide one \n"
"ra=0x%02x Fn=%u qta=%d is_11bit=%d:\n", ra, Fn, qta, is_11bit);
sb = is_single_block(ra, burst_type, is_11bit, &ms_class, &priority);
if (sb) {
rc = sba()->alloc(&trx_no, &ts_no, &sb_fn, ta);
if (rc < 0)
return rc;
LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] RACH qbit-ta=%d "
"ra=0x%02x, Fn=%d (%d,%d,%d), SBFn=%d\n",
qta, ra,
Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26,
sb_fn);
LOGP(DRLCMAC, LOGL_INFO, "TX: Immediate Assignment Uplink "
"(AGCH)\n");
tsc = m_bts.trx[trx_no].pdch[ts_no].tsc;
if (rc < 0) {
failure = true;
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource for "
"single block allocation."
"sending Immediate "
"Assignment Uplink (AGCH) reject\n");
} else {
tsc = m_bts.trx[trx_no].pdch[ts_no].tsc;
LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] RACH "
" qbit-ta=%d ra=0x%02x, Fn=%d (%d,%d,%d),"
" SBFn=%d\n",
qta, ra,
Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26,
sb_fn);
LOGP(DRLCMAC, LOGL_INFO, "TX: Immediate Assignment "
"Uplink (AGCH)\n");
}
} else {
// Create new TBF
#warning "Copy and pate with other routines.."
/* set class to 0, since we don't know the multislot class yet */
tbf = tbf_alloc_ul_tbf(&m_bts, NULL, -1, 0, 0, 1);
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
/* FIXME: send reject */
return -EBUSY;
#warning "Copy and paste with other routines.."
if (is_11bit) {
tbf = tbf_alloc_ul_tbf(&m_bts, NULL, -1, 0,
ms_class, 1);
} else {
/* set class to 0, since we don't know the multislot
* class yet */
tbf = tbf_alloc_ul_tbf(&m_bts, NULL, -1, 0, 0, 1);
}
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource sending "
"Immediate Assignment Uplink (AGCH) "
"reject\n");
rc = -EBUSY;
failure = true;
} else {
tbf->set_ta(ta);
tbf->set_state(GPRS_RLCMAC_FLOW);
tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH);
tbf_timer_start(tbf, 3169, m_bts.t3169, 0);
LOGP(DRLCMAC, LOGL_DEBUG, "%s [UPLINK] START\n",
tbf_name(tbf));
LOGP(DRLCMAC, LOGL_DEBUG, "%s RX: [PCU <- BTS] RACH "
"qbit-ta=%d ra=0x%02x, Fn=%d "
" (%d,%d,%d)\n",
tbf_name(tbf),
qta, ra, Fn, (Fn / (26 * 51)) % 32,
Fn % 51, Fn % 26);
LOGP(DRLCMAC, LOGL_INFO, "%s TX: START Immediate "
"Assignment Uplink (AGCH)\n",
tbf_name(tbf));
trx_no = tbf->trx->trx_no;
ts_no = tbf->first_ts;
usf = tbf->m_usf[ts_no];
tsc = tbf->tsc();
}
tbf->set_ta(ta);
tbf->set_state(GPRS_RLCMAC_FLOW);
tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH);
tbf_timer_start(tbf, 3169, m_bts.t3169, 0);
LOGP(DRLCMAC, LOGL_DEBUG, "%s [UPLINK] START\n",
tbf_name(tbf));
LOGP(DRLCMAC, LOGL_DEBUG, "%s RX: [PCU <- BTS] RACH "
"qbit-ta=%d ra=0x%02x, Fn=%d (%d,%d,%d)\n",
tbf_name(tbf),
qta, ra, Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26);
LOGP(DRLCMAC, LOGL_INFO, "%s TX: START Immediate "
"Assignment Uplink (AGCH)\n", tbf_name(tbf));
trx_no = tbf->trx->trx_no;
ts_no = tbf->first_ts;
usf = tbf->m_usf[ts_no];
tsc = tbf->tsc();
}
bitvec *immediate_assignment = bitvec_alloc(22) /* without plen */;
bitvec_unhex(immediate_assignment,
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
LOGP(DRLCMAC, LOGL_DEBUG,
" - TRX=%d (%d) TS=%d TA=%d TSC=%d TFI=%d USF=%d\n",
trx_no, m_bts.trx[trx_no].arfcn, ts_no, ta, tsc,
tbf ? tbf->tfi() : -1, usf);
plen = Encoding::write_immediate_assignment(
tbf, immediate_assignment, 0, ra, Fn, ta,
m_bts.trx[trx_no].arfcn, ts_no, tsc, usf, 0, sb_fn,
m_bts.alpha, m_bts.gamma, -1);
if (failure) {
plen = Encoding::write_immediate_assignment_reject(
immediate_assignment, ra, Fn,
burst_type);
immediate_assignment_reject();
}
else {
LOGP(DRLCMAC, LOGL_DEBUG,
" - TRX=%d (%d) TS=%d TA=%d TSC=%d TFI=%d USF=%d\n",
trx_no, m_bts.trx[trx_no].arfcn, ts_no, ta, tsc,
tbf ? tbf->tfi() : -1, usf);
plen = Encoding::write_immediate_assignment(
tbf, immediate_assignment, 0, ra, Fn, ta,
m_bts.trx[trx_no].arfcn, ts_no, tsc, usf, 0, sb_fn,
m_bts.alpha, m_bts.gamma, -1, burst_type, sb);
}
if (plen >= 0) {
immediate_assignment_ul_tbf();
pcu_l1if_tx_agch(immediate_assignment, plen);
if (tbf)
tbf->set_state(GPRS_RLCMAC_WAIT_ASSIGN);
}
bitvec_free(immediate_assignment);
return 0;
return rc;
}
uint8_t BTS::is_single_block(uint16_t ra, enum ph_burst_type burst_type,
uint8_t is_11bit, uint16_t *ms_class, uint16_t *priority)
{
uint8_t sb = 0, val = 0;
if (!is_11bit && (burst_type == GSM_L1_BURST_TYPE_ACCESS_0)) {
if ((ra & 0xf8) == 0x70) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block "
"allocation\n");
sb = 1;
} else if (m_bts.force_two_phase) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single "
"phase access, but we force two phase "
"access\n");
sb = 1;
}
} else if (is_11bit &&
((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
val = !!(ra & (1 << 10));
if (!val) {
if (m_bts.force_two_phase) {
LOGP(DRLCMAC, LOGL_DEBUG, "EGPRS 11 bit RACH "
"received. MS requests single phase "
"access but we force two phase "
"access\n");
sb = 1;
} else {
sb = 0;
*ms_class = (ra & 0x3e0) >> 5;
*priority = (ra & 0x18) >> 3;
}
} else {
LOGP(DRLCMAC, LOGL_DEBUG, "EGPRS 11 bit RACH received."
"MS requests single block allocation\n");
sb = 1;
}
} else if (is_11bit &&
(burst_type == GSM_L1_BURST_TYPE_ACCESS_0)) {
LOGP(DRLCMAC, LOGL_ERROR,
"Error: GPRS 11 bit RACH not supported\n");
} else if (burst_type == GSM_L1_BURST_TYPE_NONE) {
LOGP(DRLCMAC, LOGL_DEBUG, "pcu has not received burst type "
"from bts \n");
if ((ra & 0xf8) == 0x70) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block "
"allocation\n");
sb = 1;
} else if (m_bts.force_two_phase) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single "
"phase access, but we force two phase "
"access\n");
sb = 1;
}
}
return sb;
}
/* depending on the current TBF, we assign on PACCH or AGCH */
@ -607,9 +877,10 @@ void BTS::snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi)
tbf->trx->arfcn, ts, tbf->tsc(), 7, poll,
tbf->poll_fn, m_bts.alpha, m_bts.gamma, -1);
if (plen >= 0) {
immediate_assignment_dl_tbf();
pcu_l1if_tx_pch(immediate_assignment, plen, imsi);
tbf->set_state(GPRS_RLCMAC_WAIT_ASSIGN);
}
bitvec_free(immediate_assignment);
}
@ -634,13 +905,17 @@ void gprs_rlcmac_pdch::enable()
{
/* TODO: Check if there are still allocated resources.. */
INIT_LLIST_HEAD(&paging_list);
trx->num_pdch++;
m_is_enabled = 1;
bts()->increment_num_pdch();
}
void gprs_rlcmac_pdch::disable()
{
/* TODO.. kick free_resources once we know the TRX/TS we are on */
m_is_enabled = 0;
trx->num_pdch--;
bts()->decrement_num_pdch();
}
void gprs_rlcmac_pdch::free_resources()
@ -986,16 +1261,25 @@ void gprs_rlcmac_pdch::rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *ack_n
}
/* check for channel request */
if (ack_nack->Exist_Channel_Request_Description) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
"message, so we provide one:\n");
bts()->channel_request_description();
/* This call will register the new TBF with the MS on success */
tbf_alloc_ul(bts_data(), tbf->trx->trx_no,
gprs_rlcmac_ul_tbf *ul_tbf = tbf_alloc_ul(bts_data(),
tbf->trx->trx_no,
tbf->ms_class(), tbf->ms()->egprs_ms_class(),
tbf->tlli(), tbf->ta(), tbf->ms());
/* schedule uplink assignment */
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
/* schedule uplink assignment or reject*/
if (ul_tbf) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
"message, so we provide one:\n");
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
} else {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
"message, so we pacekt access reject:\n");
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ;
}
}
/* get measurements */
if (tbf->ms()) {
@ -1088,16 +1372,25 @@ void gprs_rlcmac_pdch::rcv_control_egprs_dl_ack_nack(EGPRS_PD_AckNack_t *ack_nac
/* check for channel request */
if (ack_nack->Exist_ChannelRequestDescription) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
"message, so we provide one:\n");
bts()->channel_request_description();
/* This call will register the new TBF with the MS on success */
tbf_alloc_ul(bts_data(), tbf->trx->trx_no,
gprs_rlcmac_ul_tbf *ul_tbf = tbf_alloc_ul(bts_data(),
tbf->trx->trx_no,
tbf->ms_class(), tbf->ms()->egprs_ms_class(),
tbf->tlli(), tbf->ta(), tbf->ms());
/* schedule uplink assignment */
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
/* schedule uplink assignment or reject*/
if (ul_tbf) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
"message, so we provide one:\n");
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
} else {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
"message, so we send packet access reject:\n");
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ;
}
}
/* get measurements */
@ -1120,7 +1413,7 @@ void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request,
uint32_t tlli = request->ID.u.TLLI;
uint8_t ms_class = 0;
uint8_t egprs_ms_class = 0;
uint8_t ta = 0;
uint8_t ta = GSM48_TA_INVALID;
struct pcu_l1_meas meas;
GprsMs *ms = bts()->ms_by_tlli(tlli);
@ -1180,8 +1473,12 @@ void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request,
egprs_ms_class);
ul_tbf = tbf_alloc_ul(bts_data(), trx_no(), ms_class,
egprs_ms_class, tlli, ta, ms);
if (!ul_tbf)
if (!ul_tbf) {
handle_tbf_reject(bts_data(), ms, tlli,
trx_no(), ts_no);
return;
}
/* set control ts to current MS's TS, until assignment complete */
LOGP(DRLCMAC, LOGL_DEBUG, "Change control TS to %d until assinment is complete.\n", ts_no);
@ -1258,6 +1555,7 @@ int gprs_rlcmac_pdch::rcv_control_block(
decode_gsm_rlcmac_uplink(rlc_block, ul_control_block);
LOGPC(DCSN1, LOGL_NOTICE, "\n");
LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- RX : Uplink Control Block -------------------------\n");
bts()->rlc_rcvd_control();
switch (ul_control_block->u.MESSAGE_TYPE) {
case MT_PACKET_CONTROL_ACK:
rcv_control_ack(&ul_control_block->u.Packet_Control_Acknowledgement, fn);
@ -1300,6 +1598,8 @@ int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
return -EINVAL;
}
bts()->rlc_ul_bytes(len);
LOGP(DRLCMACUL, LOGL_DEBUG, "Got RLC block, coding scheme: %s, "
"length: %d (%d))\n", cs.name(), len, cs.usedSizeUL());
@ -1315,6 +1615,7 @@ int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
return -EINVAL;
}
/*! \brief process egprs and gprs data blocks */
int gprs_rlcmac_pdch::rcv_data_block(uint8_t *data, uint32_t fn,
struct pcu_l1_meas *meas, GprsCodingScheme cs)
{
@ -1333,15 +1634,6 @@ int gprs_rlcmac_pdch::rcv_data_block(uint8_t *data, uint32_t fn,
cs.name());
return -EINVAL;
}
if (!cs.isEgprsGmsk()) {
LOGP(DRLCMACUL, LOGL_ERROR,
"Got %s RLC block but EGPRS is not implemented "
"for 8PSK yet\n",
cs.name());
bts()->decode_error();
return -EINVAL;
}
}
LOGP(DRLCMACUL, LOGL_DEBUG, " UL data: %s\n", osmo_hexdump(data, len));
@ -1405,6 +1697,23 @@ int gprs_rlcmac_pdch::rcv_block_gprs(uint8_t *data, uint32_t fn,
return rc;
}
void bts_update_tbf_ta(const char *p, uint32_t fn, uint8_t trx_no, uint8_t ts,
uint8_t ta)
{
struct gprs_rlcmac_ul_tbf *tbf =
bts_main_data()->bts->ul_tbf_by_poll_fn(fn, trx_no, ts);
if (!tbf)
LOGP(DL1IF, LOGL_DEBUG, "[%s] update TA = %u ignored due to "
"unknown UL TBF on TRX = %d, TS = %d, FN = %d\n",
p, ta, trx_no, ts, fn);
else if (tbf->ta() != ta) {
LOGP(DL1IF, LOGL_INFO, "[%s] Updating TA %u -> %u on "
"TRX = %d, TS = %d, FN = %d\n",
p, tbf->ta(), ta, trx_no, ts, fn);
tbf->set_ta(ta);
}
}
gprs_rlcmac_tbf *gprs_rlcmac_pdch::tbf_from_list_by_tfi(
LListHead<gprs_rlcmac_tbf> *tbf_list, uint8_t tfi,
enum gprs_rlcmac_tbf_direction dir)

224
src/bts.h
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,9 @@ extern "C" {
#define LLC_CODEL_DISABLE 0
#define LLC_CODEL_USE_DEFAULT (-1)
#define MAX_GPRS_CS 9
#define MAX_LOAD_PROBABILITY 0xffffffff
struct BTS;
struct GprsMs;
@ -133,13 +137,23 @@ struct gprs_rlcmac_trx {
/* back pointers */
struct BTS *bts;
uint8_t trx_no;
uint8_t current_load;
uint8_t num_pdch;
#ifdef __cplusplus
void reserve_slots(enum gprs_rlcmac_tbf_direction dir, uint8_t slots);
void unreserve_slots(enum gprs_rlcmac_tbf_direction dir, uint8_t slots);
#endif
};
#ifdef __cplusplus
extern "C" {
#endif
void bts_update_tbf_ta(const char *p, uint32_t fn, uint8_t trx_no, uint8_t ts,
uint8_t ta);
#ifdef __cplusplus
}
#endif
/**
* This is the data from C. As soon as our minimal compiler is gcc 4.7
* we can start to compile pcu_vty.c with c++ and remove the split.
@ -184,11 +198,15 @@ struct gprs_rlcmac_bts {
uint8_t alpha, gamma;
uint8_t egprs_enabled;
uint32_t dl_tbf_idle_msec; /* hold time for idle DL TBFs */
/* 0 to support resegmentation in DL, 1 for no reseg */
uint8_t dl_arq_type;
uint32_t ms_idle_sec;
uint8_t cs_adj_enabled;
uint8_t cs_adj_upper_limit;
uint8_t cs_adj_lower_limit;
struct {int16_t low; int16_t high;} cs_lqual_ranges[4];
struct {int16_t low; int16_t high; } cs_lqual_ranges[MAX_GPRS_CS];
uint16_t cs_downgrade_threshold; /* downgrade if less packets left (DL) */
uint16_t ws_base;
uint16_t ws_pdch; /* increase WS by this value per PDCH */
@ -227,12 +245,19 @@ public:
CTR_RLC_RESTARTED,
CTR_RLC_STALLED,
CTR_RLC_NACKED,
CTR_RLC_FINAL_BLOCK_RESENT,
CTR_RLC_ASS_TIMEDOUT,
CTR_RLC_ASS_FAILED,
CTR_RLC_ACK_TIMEDOUT,
CTR_RLC_ACK_FAILED,
CTR_RLC_REL_TIMEDOUT,
CTR_RLC_LATE_BLOCK,
CTR_RLC_SENT_DUMMY,
CTR_RLC_SENT_CONTROL,
CTR_RLC_DL_BYTES,
CTR_RLC_DL_PAYLOAD_BYTES,
CTR_RLC_UL_BYTES,
CTR_RLC_UL_PAYLOAD_BYTES,
CTR_DECODE_ERRORS,
CTR_SBA_ALLOCATED,
CTR_SBA_FREED,
@ -240,7 +265,56 @@ public:
CTR_LLC_FRAME_TIMEDOUT,
CTR_LLC_FRAME_DROPPED,
CTR_LLC_FRAME_SCHED,
CTR_LLC_DL_BYTES,
CTR_LLC_UL_BYTES,
CTR_RACH_REQUESTS,
CTR_11BIT_RACH_REQUESTS,
CTR_SPB_UL_FIRST_SEGMENT,
CTR_SPB_UL_SECOND_SEGMENT,
CTR_SPB_DL_FIRST_SEGMENT,
CTR_SPB_DL_SECOND_SEGMENT,
CTR_IMMEDIATE_ASSIGN_UL_TBF,
CTR_IMMEDIATE_ASSIGN_REJ,
CTR_IMMEDIATE_ASSIGN_DL_TBF,
CTR_CHANNEL_REQUEST_DESCRIPTION,
CTR_PKT_UL_ASSIGNMENT,
CTR_PKT_ACCESS_REJ,
CTR_PKT_DL_ASSIGNMENT,
CTR_RLC_RECV_CONTROL,
CTR_PUA_POLL_TIMEDOUT,
CTR_PUA_POLL_FAILED,
CTR_PDA_POLL_TIMEDOUT,
CTR_PDA_POLL_FAILED,
CTR_PUAN_POLL_TIMEDOUT,
CTR_PUAN_POLL_FAILED,
CTR_PDAN_POLL_TIMEDOUT,
CTR_PDAN_POLL_FAILED,
CTR_GPRS_DL_CS1,
CTR_GPRS_DL_CS2,
CTR_GPRS_DL_CS3,
CTR_GPRS_DL_CS4,
CTR_EGPRS_DL_MCS1,
CTR_EGPRS_DL_MCS2,
CTR_EGPRS_DL_MCS3,
CTR_EGPRS_DL_MCS4,
CTR_EGPRS_DL_MCS5,
CTR_EGPRS_DL_MCS6,
CTR_EGPRS_DL_MCS7,
CTR_EGPRS_DL_MCS8,
CTR_EGPRS_DL_MCS9,
CTR_GPRS_UL_CS1,
CTR_GPRS_UL_CS2,
CTR_GPRS_UL_CS3,
CTR_GPRS_UL_CS4,
CTR_EGPRS_UL_MCS1,
CTR_EGPRS_UL_MCS2,
CTR_EGPRS_UL_MCS3,
CTR_EGPRS_UL_MCS4,
CTR_EGPRS_UL_MCS5,
CTR_EGPRS_UL_MCS6,
CTR_EGPRS_UL_MCS7,
CTR_EGPRS_UL_MCS8,
CTR_EGPRS_UL_MCS9,
};
enum {
@ -273,9 +347,15 @@ public:
gprs_rlcmac_ul_tbf *ul_tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts);
int tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx);
int get_possible_trxs(enum gprs_rlcmac_tbf_direction dir,
bool *_trx, int8_t use_trx);
int get_suitable_trx(bool *suitable_trx);
int rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn);
int rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
uint8_t is_single_block(uint16_t ra, enum ph_burst_type burst_type,
uint8_t is_11bit, uint16_t *ms_class, uint16_t *priority);
int rcv_rach(uint16_t ra, uint32_t Fn, int16_t qta, uint8_t is_11bit,
enum ph_burst_type burst_type);
void trigger_dl_ass(gprs_rlcmac_dl_tbf *tbf, gprs_rlcmac_tbf *old_tbf);
void snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi);
@ -303,12 +383,19 @@ public:
void rlc_restarted();
void rlc_stalled();
void rlc_nacked();
void rlc_final_block_resent();
void rlc_ass_timedout();
void rlc_ass_failed();
void rlc_ack_timedout();
void rlc_ack_failed();
void rlc_rel_timedout();
void rlc_late_block();
void rlc_sent_dummy();
void rlc_sent_control();
void rlc_dl_bytes(int bytes);
void rlc_dl_payload_bytes(int bytes);
void rlc_ul_bytes(int bytes);
void rlc_ul_payload_bytes(int bytes);
void decode_error();
void sba_allocated();
void sba_freed();
@ -316,10 +403,63 @@ public:
void llc_timedout_frame();
void llc_dropped_frame();
void llc_frame_sched();
void llc_dl_bytes(int bytes);
void llc_ul_bytes(int bytes);
void rach_frame();
void rach_frame_11bit();
void spb_uplink_first_segment();
void spb_uplink_second_segment();
void spb_downlink_first_segment();
void spb_downlink_second_segment();
void immediate_assignment_ul_tbf();
void immediate_assignment_reject();
void immediate_assignment_dl_tbf();
void channel_request_description();
void pkt_ul_assignment();
void pkt_access_reject();
void pkt_dl_assignemnt();
void rlc_rcvd_control();
void pua_poll_timedout();
void pua_poll_failed();
void pda_poll_timedout();
void pda_poll_failed();
void pkt_ul_ack_nack_poll_timedout();
void pkt_ul_ack_nack_poll_failed();
void pkt_dl_ack_nack_poll_timedout();
void pkt_dl_ack_nack_poll_failed();
void gprs_dl_cs1();
void gprs_dl_cs2();
void gprs_dl_cs3();
void gprs_dl_cs4();
void egprs_dl_mcs1();
void egprs_dl_mcs2();
void egprs_dl_mcs3();
void egprs_dl_mcs4();
void egprs_dl_mcs5();
void egprs_dl_mcs6();
void egprs_dl_mcs7();
void egprs_dl_mcs8();
void egprs_dl_mcs9();
void gprs_ul_cs1();
void gprs_ul_cs2();
void gprs_ul_cs3();
void gprs_ul_cs4();
void egprs_ul_mcs1();
void egprs_ul_mcs2();
void egprs_ul_mcs3();
void egprs_ul_mcs4();
void egprs_ul_mcs5();
void egprs_ul_mcs6();
void egprs_ul_mcs7();
void egprs_ul_mcs8();
void egprs_ul_mcs9();
void ms_present(int32_t n);
int32_t ms_present_get();
void increment_num_pdch();
void decrement_num_pdch();
uint8_t get_num_pdch() const;
int get_possible_trxs_sba(bool *_trx);
/*
* Below for C interface for the VTY
@ -345,6 +485,8 @@ private:
/* list of downlink TBFs */
LListHead<gprs_rlcmac_tbf> m_dl_tbfs;
/* The summation of all the PDCH across all TRX for this BTS*/
uint8_t m_total_pdch;
private:
/* disable copying to avoid slicing */
BTS(const BTS&);
@ -356,6 +498,21 @@ inline int BTS::current_frame_number() const
return m_cur_fn;
}
inline void BTS::increment_num_pdch()
{
m_total_pdch++;
}
inline void BTS::decrement_num_pdch()
{
m_total_pdch--;
}
inline uint8_t BTS::get_num_pdch() const
{
return m_total_pdch;
}
inline SBAController *BTS::sba()
{
return &m_sba;
@ -423,6 +580,11 @@ inline struct osmo_stat_item_group *BTS::stat_items() const
return m_statg;
}
#define CREATE_COUNT_ADD_INLINE(func_name, ctr_name) \
inline void BTS::func_name(int inc) {\
rate_ctr_add(&m_ratectrs->ctr[ctr_name], inc); \
}
#define CREATE_COUNT_INLINE(func_name, ctr_name) \
inline void BTS::func_name() {\
rate_ctr_inc(&m_ratectrs->ctr[ctr_name]); \
@ -443,12 +605,19 @@ CREATE_COUNT_INLINE(rlc_resent, CTR_RLC_RESENT)
CREATE_COUNT_INLINE(rlc_restarted, CTR_RLC_RESTARTED)
CREATE_COUNT_INLINE(rlc_stalled, CTR_RLC_STALLED)
CREATE_COUNT_INLINE(rlc_nacked, CTR_RLC_NACKED)
CREATE_COUNT_INLINE(rlc_final_block_resent, CTR_RLC_FINAL_BLOCK_RESENT);
CREATE_COUNT_INLINE(rlc_ass_timedout, CTR_RLC_ASS_TIMEDOUT);
CREATE_COUNT_INLINE(rlc_ass_failed, CTR_RLC_ASS_FAILED);
CREATE_COUNT_INLINE(rlc_ack_timedout, CTR_RLC_ACK_TIMEDOUT);
CREATE_COUNT_INLINE(rlc_ack_failed, CTR_RLC_ACK_FAILED);
CREATE_COUNT_INLINE(rlc_rel_timedout, CTR_RLC_REL_TIMEDOUT);
CREATE_COUNT_INLINE(rlc_late_block, CTR_RLC_LATE_BLOCK);
CREATE_COUNT_INLINE(rlc_sent_dummy, CTR_RLC_SENT_DUMMY);
CREATE_COUNT_INLINE(rlc_sent_control, CTR_RLC_SENT_CONTROL);
CREATE_COUNT_ADD_INLINE(rlc_dl_bytes, CTR_RLC_DL_BYTES);
CREATE_COUNT_ADD_INLINE(rlc_dl_payload_bytes, CTR_RLC_DL_PAYLOAD_BYTES);
CREATE_COUNT_ADD_INLINE(rlc_ul_bytes, CTR_RLC_UL_BYTES);
CREATE_COUNT_ADD_INLINE(rlc_ul_payload_bytes, CTR_RLC_UL_PAYLOAD_BYTES);
CREATE_COUNT_INLINE(decode_error, CTR_DECODE_ERRORS)
CREATE_COUNT_INLINE(sba_allocated, CTR_SBA_ALLOCATED)
CREATE_COUNT_INLINE(sba_freed, CTR_SBA_FREED)
@ -456,7 +625,56 @@ CREATE_COUNT_INLINE(sba_timedout, CTR_SBA_TIMEDOUT)
CREATE_COUNT_INLINE(llc_timedout_frame, CTR_LLC_FRAME_TIMEDOUT);
CREATE_COUNT_INLINE(llc_dropped_frame, CTR_LLC_FRAME_DROPPED);
CREATE_COUNT_INLINE(llc_frame_sched, CTR_LLC_FRAME_SCHED);
CREATE_COUNT_ADD_INLINE(llc_dl_bytes, CTR_LLC_DL_BYTES);
CREATE_COUNT_ADD_INLINE(llc_ul_bytes, CTR_LLC_UL_BYTES);
CREATE_COUNT_INLINE(rach_frame, CTR_RACH_REQUESTS);
CREATE_COUNT_INLINE(rach_frame_11bit, CTR_11BIT_RACH_REQUESTS);
CREATE_COUNT_INLINE(spb_uplink_first_segment, CTR_SPB_UL_FIRST_SEGMENT);
CREATE_COUNT_INLINE(spb_uplink_second_segment, CTR_SPB_UL_SECOND_SEGMENT);
CREATE_COUNT_INLINE(spb_downlink_first_segment, CTR_SPB_DL_FIRST_SEGMENT);
CREATE_COUNT_INLINE(spb_downlink_second_segment, CTR_SPB_DL_SECOND_SEGMENT);
CREATE_COUNT_INLINE(immediate_assignment_ul_tbf, CTR_IMMEDIATE_ASSIGN_UL_TBF);
CREATE_COUNT_INLINE(immediate_assignment_reject, CTR_IMMEDIATE_ASSIGN_REJ);
CREATE_COUNT_INLINE(immediate_assignment_dl_tbf, CTR_IMMEDIATE_ASSIGN_DL_TBF);
CREATE_COUNT_INLINE(channel_request_description, CTR_CHANNEL_REQUEST_DESCRIPTION);
CREATE_COUNT_INLINE(pkt_ul_assignment, CTR_PKT_UL_ASSIGNMENT);
CREATE_COUNT_INLINE(pkt_access_reject, CTR_PKT_ACCESS_REJ);
CREATE_COUNT_INLINE(pkt_dl_assignemnt, CTR_PKT_DL_ASSIGNMENT);
CREATE_COUNT_INLINE(rlc_rcvd_control, CTR_RLC_RECV_CONTROL);
CREATE_COUNT_INLINE(pua_poll_timedout, CTR_PUA_POLL_TIMEDOUT);
CREATE_COUNT_INLINE(pua_poll_failed, CTR_PUA_POLL_FAILED);
CREATE_COUNT_INLINE(pda_poll_timedout, CTR_PDA_POLL_TIMEDOUT);
CREATE_COUNT_INLINE(pda_poll_failed, CTR_PDA_POLL_FAILED);
CREATE_COUNT_INLINE(pkt_ul_ack_nack_poll_timedout, CTR_PUAN_POLL_TIMEDOUT);
CREATE_COUNT_INLINE(pkt_ul_ack_nack_poll_failed, CTR_PUAN_POLL_FAILED);
CREATE_COUNT_INLINE(pkt_dl_ack_nack_poll_timedout, CTR_PDAN_POLL_TIMEDOUT);
CREATE_COUNT_INLINE(pkt_dl_ack_nack_poll_failed, CTR_PDAN_POLL_FAILED);
CREATE_COUNT_INLINE(gprs_dl_cs1, CTR_GPRS_DL_CS1);
CREATE_COUNT_INLINE(gprs_dl_cs2, CTR_GPRS_DL_CS2);
CREATE_COUNT_INLINE(gprs_dl_cs3, CTR_GPRS_DL_CS3);
CREATE_COUNT_INLINE(gprs_dl_cs4, CTR_GPRS_DL_CS4);
CREATE_COUNT_INLINE(egprs_dl_mcs1, CTR_EGPRS_DL_MCS1);
CREATE_COUNT_INLINE(egprs_dl_mcs2, CTR_EGPRS_DL_MCS2);
CREATE_COUNT_INLINE(egprs_dl_mcs3, CTR_EGPRS_DL_MCS3);
CREATE_COUNT_INLINE(egprs_dl_mcs4, CTR_EGPRS_DL_MCS4);
CREATE_COUNT_INLINE(egprs_dl_mcs5, CTR_EGPRS_DL_MCS5);
CREATE_COUNT_INLINE(egprs_dl_mcs6, CTR_EGPRS_DL_MCS6);
CREATE_COUNT_INLINE(egprs_dl_mcs7, CTR_EGPRS_DL_MCS7);
CREATE_COUNT_INLINE(egprs_dl_mcs8, CTR_EGPRS_DL_MCS8);
CREATE_COUNT_INLINE(egprs_dl_mcs9, CTR_EGPRS_DL_MCS9);
CREATE_COUNT_INLINE(gprs_ul_cs1, CTR_GPRS_UL_CS1);
CREATE_COUNT_INLINE(gprs_ul_cs2, CTR_GPRS_UL_CS2);
CREATE_COUNT_INLINE(gprs_ul_cs3, CTR_GPRS_UL_CS3);
CREATE_COUNT_INLINE(gprs_ul_cs4, CTR_GPRS_UL_CS4);
CREATE_COUNT_INLINE(egprs_ul_mcs1, CTR_EGPRS_UL_MCS1);
CREATE_COUNT_INLINE(egprs_ul_mcs2, CTR_EGPRS_UL_MCS2);
CREATE_COUNT_INLINE(egprs_ul_mcs3, CTR_EGPRS_UL_MCS3);
CREATE_COUNT_INLINE(egprs_ul_mcs4, CTR_EGPRS_UL_MCS4);
CREATE_COUNT_INLINE(egprs_ul_mcs5, CTR_EGPRS_UL_MCS5);
CREATE_COUNT_INLINE(egprs_ul_mcs6, CTR_EGPRS_UL_MCS6);
CREATE_COUNT_INLINE(egprs_ul_mcs7, CTR_EGPRS_UL_MCS7);
CREATE_COUNT_INLINE(egprs_ul_mcs8, CTR_EGPRS_UL_MCS8);
CREATE_COUNT_INLINE(egprs_ul_mcs9, CTR_EGPRS_UL_MCS9);
#undef CREATE_COUNT_INLINE

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

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

View File

@ -0,0 +1,33 @@
/* egprs_rlc_compression.h
* Routines for EGPRS RLC bitmap compression handling
*/
#pragma once
struct egprs_compress_node;
#define MOD64(X) (((X) + 64) & 0x3F)
/* Singleton to manage the EGPRS compression algorithm. */
class egprs_compress
{
public:
static int decompress_crbb(int8_t compress_bmap_len,
bool start, const uint8_t *orig_buf,
bitvec *dest);
egprs_compress();
int osmo_t4_compress(struct bitvec *bv);
static int compress_rbb(struct bitvec *urbb_vec, struct bitvec *crbb_vec,
uint8_t *uclen_crbb, uint8_t max_bits);
private:
egprs_compress_node *ones_list;
egprs_compress_node *zeros_list;
void decode_tree_init(void);
static egprs_compress *s_instance;
static egprs_compress*instance();
egprs_compress_node *create_tree_node(void *);
void build_codewords(egprs_compress_node *root, const char *cdwd[]);
/* singleton class, so this private destructor is left unimplemented. */
~egprs_compress();
};

View File

@ -24,14 +24,59 @@
#include <bts.h>
#include <tbf.h>
#include <gprs_debug.h>
#include <egprs_rlc_compression.h>
extern "C" {
#include <osmocom/gprs/protocol/gsm_04_60.h>
}
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <stdbool.h>
#include <errno.h>
#include <string.h>
/* { 0 | 1 < TIMING_ADVANCE_INDEX : bit (4) > } */
static inline bool write_tai(bitvec *dest, unsigned& wp, int8_t tai)
{
if (tai < 0) { /* No TIMING_ADVANCE_INDEX: */
bitvec_write_field(dest, wp, 0, 1);
return false;
}
/* TIMING_ADVANCE_INDEX: */
bitvec_write_field(dest, wp, 1, 1);
bitvec_write_field(dest, wp, tai, 4);
return true;
}
/* { 0 | 1 < TIMING_ADVANCE_VALUE : bit (6) > } */
static inline void write_ta(bitvec *dest, unsigned& wp, int8_t ta)
{
if (ta < 0) /* No TIMING_ADVANCE_VALUE: */
bitvec_write_field(dest, wp, 0, 1);
else { /* TIMING_ADVANCE_VALUE: */
bitvec_write_field(dest, wp, 1, 1);
bitvec_write_field(dest, wp, ta, 6);
}
}
/* 3GPP TS 44.060 § 12.12:
{ 0 | 1 < TIMING_ADVANCE_VALUE : bit (6) > }
{ 0 | 1 < TIMING_ADVANCE_INDEX : bit (4) >
< TIMING_ADVANCE_TIMESLOT_NUMBER : bit (3) > }
*/
static inline void write_ta_ie(bitvec *dest, unsigned& wp,
int8_t ta, int8_t tai, uint8_t ts)
{
write_ta(dest, wp, ta);
if (write_tai(dest, wp, tai)) /* TIMING_ADVANCE_TIMESLOT_NUMBER: */
bitvec_write_field(dest, wp, ts, 3);
}
static int write_ia_rest_downlink(
gprs_rlcmac_dl_tbf *tbf,
bitvec * dest, unsigned& wp,
uint8_t polling, uint32_t fn,
uint8_t polling, bool ta_valid, uint32_t fn,
uint8_t alpha, uint8_t gamma, int8_t ta_idx)
{
if (!tbf) {
@ -54,13 +99,8 @@ static int write_ia_rest_downlink(
}
bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
bitvec_write_field(dest, wp,polling,1); // Polling Bit
bitvec_write_field(dest, wp,!polling,1); // TA_VALID ???
if (ta_idx < 0) {
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
} else {
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX
}
bitvec_write_field(dest, wp, ta_valid, 1); // N. B: NOT related to TAI!
write_tai(dest, wp, ta_idx);
if (polling) {
bitvec_write_field(dest, wp,0x1,1); // TBF Starting TIME present
bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1'
@ -103,12 +143,7 @@ static int write_ia_rest_uplink(
} else
bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present
bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
if (ta_idx < 0) {
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
} else {
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX
}
write_tai(dest, wp, ta_idx);
bitvec_write_field(dest, wp, 1, 1); // TBF_STARTING_TIME_FLAG
bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1'
bitvec_write_field(dest, wp,fn % 51,6); // T3
@ -140,11 +175,149 @@ static int write_ia_rest_egprs_uplink(
gprs_rlcmac_ul_tbf *tbf,
bitvec * dest, unsigned& wp,
uint8_t usf, uint32_t fn,
uint8_t alpha, uint8_t gamma, int8_t ta_idx)
uint8_t alpha, uint8_t gamma, int8_t ta_idx,
enum ph_burst_type burst_type, uint16_t ra)
{
LOGP(DRLCMACUL, LOGL_ERROR,
"EGPRS Packet Uplink Assignment is not yet implemented\n");
return -EINVAL;
unsigned int ws_enc = 0;
uint8_t extended_ra = 0;
extended_ra = (ra & 0x1F);
bitvec_write_field(dest, wp, 1, 2); /* LH */
bitvec_write_field(dest, wp, 0, 2); /* 0 EGPRS Uplink Assignment */
bitvec_write_field(dest, wp, extended_ra, 5); /* Extended RA */
bitvec_write_field(dest, wp, 0, 1); /* Access technology Request */
if (tbf == NULL) {
bitvec_write_field(dest, wp, 0, 1); /* multiblock allocation */
if (alpha) {
bitvec_write_field(dest, wp, 0x1, 1); /* ALPHA =yes */
bitvec_write_field(dest, wp, alpha, 4); /* ALPHA */
} else {
bitvec_write_field(dest, wp, 0x0, 1); /* ALPHA = no */
}
bitvec_write_field(dest, wp, gamma, 5); /* GAMMA power contrl */
bitvec_write_field(dest, wp, (fn / (26 * 51)) % 32, 5);/* T1' */
bitvec_write_field(dest, wp, fn % 51, 6); /* T3 */
bitvec_write_field(dest, wp, fn % 26, 5); /* T2 */
bitvec_write_field(dest, wp, 0, 2); /* Radio block allocation */
bitvec_write_field(dest, wp, 0, 1);
} else {
ws_enc = (tbf->m_window.ws() - 64) / 32;
bitvec_write_field(dest, wp, 1, 1); /* single block alloc */
bitvec_write_field(dest, wp, tbf->tfi(), 5);/* TFI assignment */
bitvec_write_field(dest, wp, 0, 1); /* polling bit */
bitvec_write_field(dest, wp, 0, 1); /* constant */
bitvec_write_field(dest, wp, usf, 3); /* USF bit */
bitvec_write_field(dest, wp, 0, 1); /* USF granularity */
bitvec_write_field(dest, wp, 0, 1); /* P0 */
/* MCS */
bitvec_write_field(dest, wp, tbf->current_cs().to_num()-1, 4);
/* tlli channel block */
bitvec_write_field(dest, wp, tbf->tlli(), 1);
bitvec_write_field(dest, wp, 0, 1); /* BEP period present */
bitvec_write_field(dest, wp, 0, 1); /* resegmentation */
bitvec_write_field(dest, wp, ws_enc, 5);/* egprs window_size */
if (alpha) {
bitvec_write_field(dest, wp, 0x1, 1); /* ALPHA =yes */
bitvec_write_field(dest, wp, alpha, 4); /* ALPHA */
} else {
bitvec_write_field(dest, wp, 0x0, 1); /* ALPHA = no */
}
bitvec_write_field(dest, wp, gamma, 5); /* GAMMA power contrl */
bitvec_write_field(dest, wp, 0, 1); /* TIMING_ADVANCE_INDEX */
bitvec_write_field(dest, wp, 0, 1); /* TBF_STARTING_TIME_FLAG */
bitvec_write_field(dest, wp, 0, 1); /* NULL */
}
return 0;
}
/*
* Immediate assignment reject, sent on the CCCH/AGCH
* see GSM 44.018, 9.1.20 + 10.5.2.30
*/
int Encoding::write_immediate_assignment_reject(
bitvec *dest, uint16_t ra,
uint32_t ref_fn,
enum ph_burst_type burst_type)
{
unsigned wp = 0;
int plen;
int i;
bitvec_write_field(dest, wp, 0x0, 4); // Skip Indicator
bitvec_write_field(dest, wp, 0x6, 4); // Protocol Discriminator
bitvec_write_field(dest, wp, 0x3A, 8); // Immediate Assign Message Type
// feature indicator
bitvec_write_field(dest, wp, 0x0, 1); // spare
bitvec_write_field(dest, wp, 0x0, 1); // spare
bitvec_write_field(dest, wp, 0x0, 1); // no cs
bitvec_write_field(dest, wp, 0x1, 1); // implicit detach for PS
bitvec_write_field(dest, wp, 0x0, 4); // Page Mode
/*
* 9.1.20.2 of 44.018 version 11.7.0 Release 11
* Filling of the message
* If necessary the request reference information element and the
* wait indication information element should be duplicated to
* fill the message.
* TODO: group rejection for multiple MS
*/
for (i = 0; i < 4; i++) {
//10.5.2.30 Request Reference
if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
//9.1.20.2a of 44.018 version 11.7.0 Release 11
bitvec_write_field(dest, wp, 0x7f, 8); /* RACH value */
} else {
bitvec_write_field(dest, wp, ra, 8); /* RACH value */
}
bitvec_write_field(dest, wp,
(ref_fn / (26 * 51)) % 32, 5); // T1'
bitvec_write_field(dest, wp, ref_fn % 51, 6); // T3
bitvec_write_field(dest, wp, ref_fn % 26, 5); // T2
/* TODO: Make it configurable */
bitvec_write_field(dest, wp, 20, 8); //Wait Indication 1
}
plen = wp / 8;
if ((wp % 8)) {
LOGP(DRLCMACUL, LOGL_ERROR, "Length of IMM.ASS.Rej without"
"rest octets is not multiple of 8 bits, PLEASE FIX!\n");
return -1;
}
// Extended RA
else if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
//9.1.20.2a of 44.018 version 11.7.0 Release 11
uint8_t extended_ra = 0;
extended_ra = (ra & 0x1F);
bitvec_write_field(dest, wp, 0x1, 1);
bitvec_write_field(dest, wp, extended_ra, 5); /* Extended RA */
} else {
bitvec_write_field(dest, wp, 0x0, 1);
}
bitvec_write_field(dest, wp, 0x0, 1);
bitvec_write_field(dest, wp, 0x0, 1);
bitvec_write_field(dest, wp, 0x0, 1);
return plen;
}
/*
@ -153,10 +326,10 @@ static int write_ia_rest_egprs_uplink(
*/
int Encoding::write_immediate_assignment(
struct gprs_rlcmac_tbf *tbf,
bitvec * dest, uint8_t downlink, uint8_t ra,
bitvec * dest, uint8_t downlink, uint16_t ra,
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
uint8_t usf, uint8_t polling, uint32_t fn, uint8_t alpha,
uint8_t gamma, int8_t ta_idx)
uint8_t gamma, int8_t ta_idx, enum ph_burst_type burst_type, uint8_t sb)
{
unsigned wp = 0;
int plen;
@ -182,7 +355,13 @@ int Encoding::write_immediate_assignment(
bitvec_write_field(dest, wp,arfcn,10); // ARFCN
//10.5.2.30 Request Reference
bitvec_write_field(dest, wp,ra,8); // RA
if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
bitvec_write_field(dest, wp, 0x7f, 8); /* RACH value */
} else {
bitvec_write_field(dest, wp, ra, 8); /* RACH value */
}
bitvec_write_field(dest, wp,(ref_fn / (26 * 51)) % 32,5); // T1'
bitvec_write_field(dest, wp,ref_fn % 51,6); // T3
bitvec_write_field(dest, wp,ref_fn % 26,5); // T2
@ -204,12 +383,13 @@ int Encoding::write_immediate_assignment(
if (downlink)
rc = write_ia_rest_downlink(as_dl_tbf(tbf), dest, wp,
polling, fn,
polling, gsm48_ta_is_valid(ta), fn,
alpha, gamma, ta_idx);
else if (as_ul_tbf(tbf) && as_ul_tbf(tbf)->is_egprs_enabled())
else if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2)))
rc = write_ia_rest_egprs_uplink(as_ul_tbf(tbf), dest, wp,
usf, fn,
alpha, gamma, ta_idx);
alpha, gamma, ta_idx, burst_type, ra);
else
rc = write_ia_rest_uplink(as_ul_tbf(tbf), dest, wp,
usf, fn,
@ -237,6 +417,8 @@ void Encoding::write_packet_uplink_assignment(
// TODO We should use our implementation of encode RLC/MAC Control messages.
unsigned wp = 0;
uint8_t ts;
/* timeslot assigned for the Continuous Timing Advance procedure */
uint8_t ta_ts = 0; /* FIXME: supply it as parameter from caller */
bitvec_write_field(dest, wp,0x1,2); // Payload Type
bitvec_write_field(dest, wp,0x0,2); // Uplink block with TDMA framenumber (N+13)
@ -260,15 +442,7 @@ void Encoding::write_packet_uplink_assignment(
bitvec_write_field(dest, wp,0x0,1); // Message escape
bitvec_write_field(dest, wp,tbf->current_cs().to_num()-1, 2); // CHANNEL_CODING_COMMAND
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on
bitvec_write_field(dest, wp,tbf->ta(),6); // TIMING_ADVANCE_VALUE
if (ta_idx < 0) {
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
} else {
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX
}
write_ta_ie(dest, wp,tbf->ta(), ta_idx, ta_ts);
} else { /* EPGRS */
unsigned int ws_enc = (tbf->m_window.ws() - 64) / 32;
bitvec_write_field(dest, wp,0x1,1); // Message escape
@ -276,22 +450,14 @@ void Encoding::write_packet_uplink_assignment(
bitvec_write_field(dest, wp,0x0,1); // No CONTENTION_RESOLUTION_TLLI
bitvec_write_field(dest, wp,0x0,1); // No COMPACT reduced MA
bitvec_write_field(dest, wp,tbf->current_cs().to_num()-1, 4); // EGPRS Modulation and Coding IE
bitvec_write_field(dest, wp,0x0,1); // No RESEGMENT
/* 0: no RESEGMENT, 1: Segmentation*/
bitvec_write_field(dest, wp, 0x1, 1);
bitvec_write_field(dest, wp,ws_enc,5); // EGPRS Window Size
bitvec_write_field(dest, wp,0x0,1); // No Access Technologies Request
bitvec_write_field(dest, wp,0x0,1); // No ARAC RETRANSMISSION REQUEST
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
bitvec_write_field(dest, wp,0x0,1); // No BEP_PERIOD2
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on
bitvec_write_field(dest, wp,tbf->ta(),6); // TIMING_ADVANCE_VALUE
if (ta_idx < 0) {
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
} else {
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX
}
write_ta_ie(dest, wp,tbf->ta(), ta_idx, ta_ts);
bitvec_write_field(dest, wp,0x0,1); // No Packet Extended Timing Advance
}
@ -534,17 +700,44 @@ static void write_packet_uplink_ack_gprs(
static void write_packet_ack_nack_desc_egprs(
struct gprs_rlcmac_bts *bts, bitvec * dest, unsigned& wp,
gprs_rlc_ul_window *window, bool is_final)
gprs_rlc_ul_window *window, bool is_final, unsigned& rest_bits)
{
int urbb_len = 0;
int crbb_len = 0;
int len;
unsigned int urbb_len = 0;
uint8_t crbb_len = 0;
uint8_t len;
bool bow = true;
bool eow = true;
int ssn = window->mod_sns(window->v_q() + 1);
int num_blocks = window->mod_sns(window->v_r() - window->v_q());
unsigned int num_blocks = window->mod_sns(window->v_r() - window->v_q());
int esn_crbb = window->mod_sns(ssn - 1);
int rest_bits = dest->data_len * 8 - wp;
static uint8_t rbb[RLC_EGPRS_MAX_WS] = {'\0'};
uint8_t iter = 0;
int is_compressed = 0;
bool try_compression = false;
uint8_t ucmp_bmplen;
uint8_t crbb_bitmap[23] = {'\0'};
bitvec ucmp_vec;
bitvec crbb_vec;
uint8_t uclen_crbb = 0;
bool len_coded = true;
uint8_t crbb_start_clr_code;
uint8_t i;
#if 0
/* static size of 16 bits*/
..0. .... = ACKNACK: (Union)
Desc
...0 .... = FINAL_ACK_INDICATION: False
.... 1... = BEGINNING_OF_WINDOW: 1
.... .1.. = END_OF_WINDOW: 1
.... ..10 0101 0001 1... .... = STARTING_SEQUENCE_NUMBER: 1187
.0.. .... = CRBB Exist: 0
#endif
rest_bits -= 16;
if (num_blocks > 0)
/* V(Q) is NACK and omitted -> SSN = V(Q) + 1 */
@ -552,32 +745,71 @@ static void write_packet_ack_nack_desc_egprs(
if (num_blocks > window->ws())
num_blocks = window->ws();
/* Try Compression as number of blocks does not fit */
if (num_blocks > rest_bits) {
eow = false;
urbb_len = rest_bits;
/* TODO: use compression, start encoding bits and stop when the
* space is exhausted. Use the first combination that encodes
* all bits. If there is none, use the combination that encodes
* the largest number of bits (e.g. by setting num_blocks to the
* max and repeating the construction).
*/
} else if (num_blocks > rest_bits - 9) {
/* union bit and length field take 9 bits */
eow = false;
urbb_len = rest_bits - 9;
/* TODO: use compression (see above) */
try_compression = true;
}
if (try_compression == true) {
ucmp_bmplen = window->update_egprs_rbb(rbb);
ucmp_vec.data = rbb;
ucmp_vec.cur_bit = ucmp_bmplen;
ucmp_vec.data_len = 127;
crbb_vec.data = crbb_bitmap;
crbb_vec.cur_bit = 0;
crbb_vec.data_len = 127;
LOGP(DRLCMACUL, LOGL_DEBUG,
"rest_bits=%d uncompressed len %d and uncompressed bitmap = %s\n",
rest_bits, ucmp_bmplen,
osmo_hexdump(ucmp_vec.data, (ucmp_bmplen+7)/8));
is_compressed = egprs_compress::compress_rbb(&ucmp_vec, /* Uncompressed bitmap*/
&crbb_vec, /*Compressed bitmap vector */
&uclen_crbb,
(rest_bits - 16));/* CRBBlength:7 colourcode:1 dissector length:8*/
LOGP(DRLCMACUL, LOGL_DEBUG,
"the ucmp len=%d uclen_crbb=%d num_blocks=%d crbb length %d, "
"and the CRBB bitmap = %s\n",
ucmp_bmplen, uclen_crbb, num_blocks, crbb_vec.cur_bit,
osmo_hexdump(crbb_bitmap, (crbb_vec.cur_bit+7)/8));
crbb_len = crbb_vec.cur_bit;
}
if (urbb_len + crbb_len == rest_bits)
len = -1;
else if (crbb_len == 0)
if (is_compressed == 0) {
/* length field takes 8 bits*/
if (num_blocks > rest_bits - 8) {
eow = false;
urbb_len = rest_bits;
len_coded = false;
} else if (num_blocks == rest_bits) {
urbb_len = rest_bits;
len_coded = false;
} else
urbb_len = num_blocks;
len = urbb_len + 15;
else
} else {
if (num_blocks > uclen_crbb) {
eow = false;
urbb_len = num_blocks - uclen_crbb;
}
/* Union bit takes 1 bit */
/* Other fields in descr of compresed bitmap takes 23 bits
* -8 = CRBB_STARTING_COLOR_CODE + CRBB_LENGTH */
if (urbb_len > (rest_bits - crbb_len - 8)) {
eow = false;
len_coded = false;
urbb_len = rest_bits - crbb_len - 8;
/* -16 = ACKNACK Dissector length + CRBB_STARTING_COLOR_CODE + CRBB_LENGTH */
} else if (urbb_len > (rest_bits - crbb_len - 16)) {
eow = false;
len_coded = false;
urbb_len = rest_bits - crbb_len - 16;
}
len = urbb_len + crbb_len + 23;
}
/* EGPRS Ack/Nack Description IE */
if (len < 0) {
if (len_coded == false) {
bitvec_write_field(dest, wp, 0, 1); // 0: don't have length
} else {
bitvec_write_field(dest, wp, 1, 1); // 1: have length
@ -588,17 +820,37 @@ static void write_packet_ack_nack_desc_egprs(
bitvec_write_field(dest, wp, bow, 1); // BEGINNING_OF_WINDOW
bitvec_write_field(dest, wp, eow, 1); // END_OF_WINDOW
bitvec_write_field(dest, wp, ssn, 11); // STARTING_SEQUENCE_NUMBER
bitvec_write_field(dest, wp, 0, 1); // 0: don't have CRBB
/* TODO: Add CRBB support */
if (is_compressed) {
bitvec_write_field(dest, wp, 1, 1); // CRBB_Exist
bitvec_write_field(dest, wp, crbb_len, 7); // CRBB_LENGTH
crbb_start_clr_code = (0x80 & ucmp_vec.data[0])>>7;
bitvec_write_field(dest, wp, crbb_start_clr_code, 1); // CRBB_clr_code
LOGP(DRLCMACUL, LOGL_DEBUG,
"EGPRS CRBB, crbb_len = %d, crbb_start_clr_code = %d\n",
crbb_len, crbb_start_clr_code);
while (crbb_len != 0) {
if (crbb_len > 8) {
bitvec_write_field(dest, wp, crbb_bitmap[iter], 8);
crbb_len = crbb_len - 8;
iter++;
} else {
bitvec_write_field(dest, wp, crbb_bitmap[iter], crbb_len);
crbb_len = 0;
}
}
esn_crbb = window->mod_sns(esn_crbb + uclen_crbb);
} else {
bitvec_write_field(dest, wp, 0, 1); // CRBB_Exist
}
LOGP(DRLCMACUL, LOGL_DEBUG,
" - EGPRS URBB, len = %d, SSN = %d, ESN_CRBB = %d, "
"EGPRS URBB, urbb len = %d, SSN = %d, ESN_CRBB = %d, "
"len present = %s,desc len = %d, "
"SNS = %d, WS = %d, V(Q) = %d, V(R) = %d%s%s\n",
urbb_len, ssn, esn_crbb,
urbb_len, ssn, esn_crbb, len_coded ? "yes" : "No" , len,
window->sns(), window->ws(), window->v_q(), window->v_r(),
bow ? ", BOW" : "", eow ? ", EOW" : "");
for (int i = urbb_len; i > 0; i--) {
for (i = urbb_len; i > 0; i--) {
/* Set bit at the appropriate position (see 3GPP TS 04.60 12.3.1) */
bool is_ack = window->m_v_n.is_received(esn_crbb + i);
bitvec_write_field(dest, wp, is_ack, 1);
@ -610,9 +862,11 @@ static void write_packet_uplink_ack_egprs(
struct gprs_rlcmac_ul_tbf *tbf, bool is_final)
{
bitvec_write_field(dest, wp, 0, 2); // fixed 00
bitvec_write_field(dest, wp, 2, 4); // CHANNEL_CODING_COMMAND: MCS-3
// bitvec_write_field(dest, wp, tbf->current_cs() - 1, 4); // CHANNEL_CODING_COMMAND
bitvec_write_field(dest, wp, 0, 1); // 0: no RESEGMENT (nyi)
/* CHANNEL_CODING_COMMAND */
bitvec_write_field(dest, wp,
tbf->current_cs().to_num() - 1, 4);
/* 0: no RESEGMENT, 1: Segmentation*/
bitvec_write_field(dest, wp, 1, 1);
bitvec_write_field(dest, wp, 1, 1); // PRE_EMPTIVE_TRANSMISSION, TODO: This resembles GPRS, change it?
bitvec_write_field(dest, wp, 0, 1); // 0: no PRR_RETRANSMISSION_REQUEST, TODO: clarify
bitvec_write_field(dest, wp, 0, 1); // 0: no ARAC_RETRANSMISSION_REQUEST, TODO: clarify
@ -624,7 +878,9 @@ static void write_packet_uplink_ack_egprs(
bitvec_write_field(dest, wp, 0, 1); // 0: don't have Power Control Parameters
bitvec_write_field(dest, wp, 0, 1); // 0: don't have Extension Bits
write_packet_ack_nack_desc_egprs(bts, dest, wp, &tbf->m_window, is_final);
/* -2 for last bit 0 mandatory and REL5 not supported */
unsigned bits_ack_nack = dest->data_len * 8 - wp - 2;
write_packet_ack_nack_desc_egprs(bts, dest, wp, &tbf->m_window, is_final, bits_ack_nack);
bitvec_write_field(dest, wp, 0, 1); // fixed 0
bitvec_write_field(dest, wp, 0, 1); // 0: don't have REL 5
@ -744,20 +1000,20 @@ int Encoding::rlc_write_dl_data_header(const struct gprs_rlc_data_info *rlc,
egprs1->usf = rlc->usf;
egprs1->es_p = rlc->es_p;
egprs1->rrbp = rlc->rrbp;
egprs1->tfi_a = rlc->tfi >> 0; /* 1 bit LSB */
egprs1->tfi_b = rlc->tfi >> 1; /* 4 bits */
egprs1->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
egprs1->tfi_lo = rlc->tfi >> 1; /* 4 bits */
egprs1->pr = rlc->pr;
egprs1->cps = rlc->cps;
egprs1->bsn1_a = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
egprs1->bsn1_b = rlc->block_info[0].bsn >> 2; /* 8 bits */
egprs1->bsn1_c = rlc->block_info[0].bsn >> 10; /* 1 bit */
egprs1->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
egprs1->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
egprs1->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
bsn_delta = (rlc->block_info[1].bsn - rlc->block_info[0].bsn) &
(RLC_EGPRS_SNS - 1);
egprs1->bsn2_a = bsn_delta >> 0; /* 7 bits LSB */
egprs1->bsn2_b = bsn_delta >> 7; /* 3 bits */
egprs1->bsn2_hi = bsn_delta >> 0; /* 7 bits LSB */
egprs1->bsn2_lo = bsn_delta >> 7; /* 3 bits */
/* first FBI/E header */
e_fbi_header = rlc->block_info[0].e ? 0x01 : 0;
@ -783,14 +1039,14 @@ int Encoding::rlc_write_dl_data_header(const struct gprs_rlc_data_info *rlc,
egprs2->usf = rlc->usf;
egprs2->es_p = rlc->es_p;
egprs2->rrbp = rlc->rrbp;
egprs2->tfi_a = rlc->tfi >> 0; /* 1 bit LSB */
egprs2->tfi_b = rlc->tfi >> 1; /* 4 bits */
egprs2->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
egprs2->tfi_lo = rlc->tfi >> 1; /* 4 bits */
egprs2->pr = rlc->pr;
egprs2->cps = rlc->cps;
egprs2->bsn1_a = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
egprs2->bsn1_b = rlc->block_info[0].bsn >> 2; /* 8 bits */
egprs2->bsn1_c = rlc->block_info[0].bsn >> 10; /* 1 bit */
egprs2->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
egprs2->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
egprs2->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
e_fbi_header = rlc->block_info[0].e ? 0x01 : 0;
e_fbi_header |= rlc->block_info[0].cv == 0 ? 0x02 : 0; /* FBI */
@ -807,14 +1063,14 @@ int Encoding::rlc_write_dl_data_header(const struct gprs_rlc_data_info *rlc,
egprs3->usf = rlc->usf;
egprs3->es_p = rlc->es_p;
egprs3->rrbp = rlc->rrbp;
egprs3->tfi_a = rlc->tfi >> 0; /* 1 bit LSB */
egprs3->tfi_b = rlc->tfi >> 1; /* 4 bits */
egprs3->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
egprs3->tfi_lo = rlc->tfi >> 1; /* 4 bits */
egprs3->pr = rlc->pr;
egprs3->cps = rlc->cps;
egprs3->bsn1_a = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
egprs3->bsn1_b = rlc->block_info[0].bsn >> 2; /* 8 bits */
egprs3->bsn1_c = rlc->block_info[0].bsn >> 10; /* 1 bit */
egprs3->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
egprs3->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
egprs3->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
egprs3->spb = rlc->block_info[0].spb;
@ -891,11 +1147,21 @@ unsigned int Encoding::rlc_copy_from_aligned_buffer(
return rdbi->data_len;
}
/*!
* \brief (GPRS) put llc pdu into an rlc/mac block. fragment the llc pdu if needed
* \param rdbi rlc/mac block info
* \param llc llc pdu
* \param offset given offset within the rlc/mac block
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
* \param data_block buffer holds rlc/mac data
* \param is_final if this is the last rlc/mac within a TBF
* \param count_payload if not NULL save the written size of payload in bytes into it
* \return the state of the rlc/mac like if there is more space for another chunk
*/
static Encoding::AppendResult rlc_data_to_dl_append_gprs(
struct gprs_rlc_data_block_info *rdbi,
gprs_llc *llc, int *offset, int *num_chunks,
uint8_t *data_block,
bool is_final)
uint8_t *data_block, bool is_final, int *count_payload)
{
int chunk;
int space;
@ -920,6 +1186,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
*e_pointer |= 0x01;
/* fill only space */
llc->consume(data, space);
if (count_payload)
*count_payload = space;
/* return data block as message */
*offset = rdbi->data_len;
(*num_chunks)++;
@ -937,6 +1205,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
*e_pointer |= 0x01;
/* fill space */
llc->consume(data, space);
if (count_payload)
*count_payload = space;
*offset = rdbi->data_len;
(*num_chunks)++;
rdbi->cv = 0;
@ -964,6 +1234,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
// no need to set e_pointer nor increase delimiter
/* fill only space, which is 1 octet less than chunk */
llc->consume(data, space);
if (count_payload)
*count_payload = space;
/* return data block as message */
*offset = rdbi->data_len;
(*num_chunks)++;
@ -989,6 +1261,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
(*num_chunks)++;
/* copy (rest of) LLC frame to space and reset later */
llc->consume(data, chunk);
if (count_payload)
*count_payload = chunk;
data += chunk;
space -= chunk;
(*offset) += chunk;
@ -1012,11 +1286,22 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
return Encoding::AR_COMPLETED_BLOCK_FILLED;
}
/*!
* \brief (EGPRS) put llc pdu into an rlc/mac block. fragment the llc pdu if needed
* \param rdbi rlc/mac block info
* \param llc llc pdu
* \param offset given offset within the rlc/mac block
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
* \param data_block buffer holds rlc/mac data
* \param is_final if this is the last rlc/mac within a TBF
* \param count_payload if not NULL save the written size of payload in bytes into it
* \return the state of the rlc/mac like if there is more space for another chunk
*/
static Encoding::AppendResult rlc_data_to_dl_append_egprs(
struct gprs_rlc_data_block_info *rdbi,
gprs_llc *llc, int *offset, int *num_chunks,
uint8_t *data_block,
bool is_final)
bool is_final, int *count_payload)
{
int chunk;
int space;
@ -1040,6 +1325,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
chunk, space);
/* fill only space */
llc->consume(data, space);
if (count_payload)
*count_payload = space;
/* return data block as message */
*offset = rdbi->data_len;
(*num_chunks)++;
@ -1054,6 +1341,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
"header, and we are done\n", chunk, space);
/* fill space */
llc->consume(data, space);
if (count_payload)
*count_payload = space;
*offset = rdbi->data_len;
(*num_chunks)++;
rdbi->cv = 0;
@ -1068,6 +1357,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
chunk, space);
/* fill space */
llc->consume(data, space);
if (count_payload)
*count_payload = space;
*offset = rdbi->data_len;
(*num_chunks)++;
return Encoding::AR_NEED_MORE_BLOCKS;
@ -1098,6 +1389,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
(*num_chunks)++;
/* copy (rest of) LLC frame to space and reset later */
llc->consume(data, chunk);
if (count_payload)
*count_payload = chunk;
data += chunk;
space -= chunk;
(*offset) += chunk;
@ -1154,22 +1447,60 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
return Encoding::AR_COMPLETED_BLOCK_FILLED;
}
/*!
* \brief Encoding::rlc_data_to_dl_append
* \param rdbi rlc/mac block info
* \param cs the coding scheme to use
* \param llc llc pdu
* \param offset given offset within the rlc/mac block
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
* \param data_block buffer holds rlc/mac data
* \param is_final if this is the last rlc/mac within a TBF
* \param count_payload if not NULL save the written size of payload in bytes into it
* \return the state of the rlc/mac like if there is more space for another chunk
*/
Encoding::AppendResult Encoding::rlc_data_to_dl_append(
struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
gprs_llc *llc, int *offset, int *num_chunks,
uint8_t *data_block,
bool is_final)
uint8_t *data_block, bool is_final, int *count_payload)
{
if (cs.isGprs())
return rlc_data_to_dl_append_gprs(rdbi,
llc, offset, num_chunks, data_block, is_final);
llc, offset, num_chunks, data_block, is_final,
count_payload);
if (cs.isEgprs())
return rlc_data_to_dl_append_egprs(rdbi,
llc, offset, num_chunks, data_block, is_final);
llc, offset, num_chunks, data_block, is_final,
count_payload);
LOGP(DRLCMACDL, LOGL_ERROR, "%s data block encoding not implemented\n",
cs.name());
return AR_NEED_MORE_BLOCKS;
}
/*
* Refer 44.060 version 7.27.0 Release 7
* section 7.1.3.2.1 On receipt of a PACKET RESOURCE REQUEST message
* 8.1.2.5 Establishment of uplink TBF
*/
void Encoding::write_packet_access_reject(
bitvec * dest, uint32_t tlli)
{
unsigned wp = 0;
bitvec_write_field(dest, wp, 0x1, 2); // Payload Type
bitvec_write_field(dest, wp, 0x0, 2); // Uplink block with TDMA FN
bitvec_write_field(dest, wp, 0, 1); // No Polling Bit
bitvec_write_field(dest, wp, 0x0, 3); // Uplink state flag
bitvec_write_field(dest, wp,
MT_PACKET_ACCESS_REJECT, 6); // MESSAGE TYPE
bitvec_write_field(dest, wp, 0, 2); // fixed 00
bitvec_write_field(dest, wp, 0x0, 1); // TLLI / G-RNTI : bit (32)
bitvec_write_field(dest, wp, tlli, 32); // CONTENTION_RESOLUTION_TLLI
bitvec_write_field(dest, wp, 1, 1); // WAIT_INDICATION size in seconds
/* TODO: make it configurable */
bitvec_write_field(dest, wp, 5, 8); // WAIT_INDICATION value
bitvec_write_field(dest, wp, 0, 1); // WAIT_INDICATION size in seconds
}

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,20 @@ class Encoding {
public:
static int write_immediate_assignment(
struct gprs_rlcmac_tbf *tbf,
bitvec * dest, uint8_t downlink, uint8_t ra,
bitvec * dest, uint8_t downlink, uint16_t ra,
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts,
uint8_t tsc, uint8_t usf, uint8_t polling,
uint32_t fn, uint8_t alpha, uint8_t gamma,
int8_t ta_idx);
int8_t ta_idx,
enum ph_burst_type burst_type =
GSM_L1_BURST_TYPE_ACCESS_0,
uint8_t sb = 1);
static int write_immediate_assignment_reject(
bitvec *dest, uint16_t ra,
uint32_t ref_fn,
enum ph_burst_type burst_type
);
static void write_packet_uplink_assignment(
struct gprs_rlcmac_bts *bts,
@ -62,6 +74,9 @@ public:
static void encode_rbb(const char *show_rbb, uint8_t *rbb);
static void write_packet_access_reject(
bitvec * dest, uint32_t tlli);
static void write_packet_uplink_ack(
struct gprs_rlcmac_bts *bts, bitvec * dest,
struct gprs_rlcmac_ul_tbf *tbf, bool is_final,
@ -91,6 +106,5 @@ public:
static AppendResult rlc_data_to_dl_append(
struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
gprs_llc *llc, int *offset, int *num_chunks,
uint8_t *data,
bool is_final);
uint8_t *data, bool is_final, int *count_payload);
};

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),
@ -107,7 +108,8 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
m_reserved_ul_slots(0),
m_current_trx(NULL),
m_codel_state(NULL),
m_mode(GprsCodingScheme::GPRS)
m_mode(GprsCodingScheme::GPRS),
m_dl_ctrl_msg(0)
{
int codel_interval = LLC_CODEL_USE_DEFAULT;
@ -237,7 +239,7 @@ void GprsMs::set_mode(GprsCodingScheme::Mode mode)
if (!m_current_cs_ul.isEgprs()) {
m_current_cs_ul = GprsCodingScheme::getEgprsByNum(
m_bts->bts_data()->initial_mcs_ul);
if (!m_current_cs_dl.isValid())
if (!m_current_cs_ul.isValid())
m_current_cs_ul = GprsCodingScheme::MCS1;
}
if (!m_current_cs_dl.isEgprs()) {
@ -464,11 +466,15 @@ void GprsMs::set_ta(uint8_t ta_)
if (ta_ == m_ta)
return;
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
tlli(), m_ta, ta_);
m_ta = ta_;
if (gsm48_ta_is_valid(ta_)) {
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
tlli(), m_ta, ta_);
m_ta = ta_;
} else
LOGP(DRLCMAC, LOGL_NOTICE,
"MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
"value %d kept)\n", tlli(), ta_, m_ta);
}
void GprsMs::set_ms_class(uint8_t ms_class_)
@ -574,6 +580,11 @@ GprsCodingScheme GprsMs::max_cs_ul() const
return GprsCodingScheme(GprsCodingScheme::MCS4);
}
void GprsMs::set_current_cs_dl(GprsCodingScheme::Scheme scheme)
{
m_current_cs_dl = scheme;
}
GprsCodingScheme GprsMs::max_cs_dl() const
{
struct gprs_rlcmac_bts *bts_data;
@ -632,8 +643,8 @@ void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
} else if (m_current_cs_ul.isEgprs()) {
/* TODO, use separate table */
if (current_cs_num > 4)
current_cs_num = 4;
if (current_cs_num > MAX_GPRS_CS)
current_cs_num = MAX_GPRS_CS;
low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
} else {

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;
@ -129,6 +130,8 @@ public:
void update_l1_meas(const pcu_l1_meas *meas);
const pcu_l1_meas* l1_meas() const {return &m_l1_meas;};
unsigned nack_rate_dl() const;
unsigned dl_ctrl_msg() const;
void update_dl_ctrl_msg();
/* internal use */
static void timeout(void *priv_);
@ -179,6 +182,8 @@ private:
struct gprs_codel *m_codel_state;
GprsCodingScheme::Mode m_mode;
unsigned m_dl_ctrl_msg;
};
inline bool GprsMs::is_idle() const
@ -262,6 +267,16 @@ inline unsigned GprsMs::nack_rate_dl() const
return m_nack_rate_dl;
}
inline unsigned GprsMs::dl_ctrl_msg() const
{
return m_dl_ctrl_msg;
}
inline void GprsMs::update_dl_ctrl_msg()
{
m_dl_ctrl_msg++;
}
inline uint8_t GprsMs::reserved_dl_slots() const
{
return m_reserved_dl_slots;

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

@ -179,6 +179,8 @@ int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets)
if (elapsed < 128)
return 0;
tbf->m_bw.dl_throughput = (tbf->m_bw.dl_bw_octets/elapsed);
LOGP(DRLCMACMEAS, LOGL_INFO, "DL Bandwitdh of IMSI=%s / TLLI=0x%08x: "
"%d KBits/s\n", tbf->imsi(), tbf->tlli(),
tbf->m_bw.dl_bw_octets / elapsed);

View File

@ -56,7 +56,9 @@ static uint32_t sched_poll(BTS *bts,
*ul_ack_tbf = ul_tbf;
if (ul_tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
*dl_ass_tbf = ul_tbf;
if (ul_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
if (ul_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS
|| ul_tbf->ul_ass_state ==
GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ)
*ul_ass_tbf = ul_tbf;
#warning "Is this supposed to be fair? The last TBF for each wins? Maybe use llist_add_tail and skip once we have all states?"
}
@ -72,7 +74,8 @@ static uint32_t sched_poll(BTS *bts,
*poll_tbf = dl_tbf;
if (dl_tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
*dl_ass_tbf = dl_tbf;
if (dl_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
if (dl_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS
|| dl_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ)
*ul_ass_tbf = dl_tbf;
}
@ -132,12 +135,19 @@ static struct msgb *sched_select_ctrl_msg(
/*
* Assignments for the same direction have lower precedence,
* because they may kill the TBF when the CONTOL ACK is
* because they may kill the TBF when the CONTROL ACK is
* received, thus preventing the others from being processed.
*/
if (tbf == ul_ass_tbf && tbf->direction == GPRS_RLCMAC_DL_TBF)
msg = ul_ass_tbf->create_ul_ass(fn, ts);
if (tbf == ul_ass_tbf && tbf->ul_ass_state ==
GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ)
msg = ul_ass_tbf->create_packet_access_reject();
else if (tbf == ul_ass_tbf && tbf->direction ==
GPRS_RLCMAC_DL_TBF)
if (tbf->ul_ass_state ==
GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ)
msg = ul_ass_tbf->create_packet_access_reject();
else
msg = ul_ass_tbf->create_ul_ass(fn, ts);
else if (tbf == dl_ass_tbf && tbf->direction == GPRS_RLCMAC_UL_TBF)
msg = dl_ass_tbf->create_dl_ass(fn, ts);
else if (tbf == ul_ack_tbf)
@ -174,6 +184,8 @@ static struct msgb *sched_select_ctrl_msg(
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
"message at RTS for %s (TRX=%d, TS=%d)\n",
tbf_name(tbf), trx, ts);
/* Updates the dl ctrl msg counter for ms */
tbf->ms()->update_dl_ctrl_msg();
return msg;
}
/* schedule PACKET PAGING REQUEST */
@ -181,6 +193,9 @@ static struct msgb *sched_select_ctrl_msg(
if (msg) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling paging request "
"message at RTS for (TRX=%d, TS=%d)\n", trx, ts);
/* Updates the dl ctrl msg counter for ms */
tbf->ms()->update_dl_ctrl_msg();
return msg;
}
@ -289,7 +304,7 @@ static struct msgb *sched_dummy(void)
}
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts, uint16_t arfcn,
uint8_t trx, uint8_t ts,
uint32_t fn, uint8_t block_nr)
{
struct gprs_rlcmac_pdch *pdch;
@ -300,8 +315,6 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
struct msgb *msg = NULL;
uint32_t poll_fn, sba_fn;
#warning "ARFCN... it is already in the TRX..... is it consistent with it?"
if (trx >= 8 || ts >= 8)
return -EINVAL;
pdch = &bts->trx[trx].pdch[ts];
@ -339,18 +352,28 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
/* Prio 1: select control message */
msg = sched_select_ctrl_msg(trx, ts, fn, block_nr, pdch, ul_ass_tbf,
dl_ass_tbf, ul_ack_tbf);
if (msg)
bts->bts->rlc_sent_control();
/* Prio 2: select data message for downlink */
if (!msg)
if (!msg) {
msg = sched_select_downlink(bts, trx, ts, fn, block_nr, pdch);
if (msg)
bts->bts->rlc_sent();
}
/* Prio 3: send dummy contol message */
if (!msg)
if (!msg) {
/* increase counter */
msg = sched_dummy();
if (msg)
bts->bts->rlc_sent_dummy();
}
if (!msg)
return -ENOMEM;
/* msg is now available */
bts->bts->rlc_dl_bytes(msg->data_len);
/* set USF */
OSMO_ASSERT(msgb_length(msg) > 0);
@ -360,7 +383,7 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
gprs_bssgp_update_frames_sent();
/* send PDTCH/PACCH to L1 */
pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);
pcu_l1if_tx_pdtch(msg, trx, ts, bts->trx[trx].arfcn, fn, block_nr);
return 0;
}

View File

@ -383,8 +383,11 @@ static int tfi_find_free(BTS *bts, const GprsMs *ms,
int tfi;
uint8_t trx_no;
if (use_trx == -1 && ms->current_trx())
LOGP(DRLCMAC, LOGL_DEBUG, " USE trx = %d \n",use_trx);
if (use_trx == -1 && ms->current_trx()) {
use_trx = ms->current_trx()->trx_no;
LOGP(DRLCMAC, LOGL_DEBUG, " MS alive = %d \n",use_trx);
}
tfi = bts->tfi_find_free(dir, &trx_no, use_trx);
if (tfi < 0)
@ -393,6 +396,8 @@ static int tfi_find_free(BTS *bts, const GprsMs *ms,
if (trx_no_)
*trx_no_ = trx_no;
LOGP(DRLCMAC, LOGL_DEBUG, " TREE trx = %d TFI = %d\n", *trx_no_, tfi);
return tfi;
}
@ -479,6 +484,7 @@ int alloc_algorithm_a(struct gprs_rlcmac_bts *bts,
}
tbf_->trx = trx;
trx->current_load++;
/* the only one TS is the common TS */
tbf_->first_ts = tbf_->first_common_ts = ts;
ms_->set_reserved_slots(trx, 1 << ts, 1 << ts);
@ -1000,6 +1006,7 @@ int alloc_algorithm_b(struct gprs_rlcmac_bts *bts,
if (!(dl_slots & (1 << ts)))
continue;
trx->current_load++;
LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning DL TS "
"%d\n", ts);
assign_dlink_tbf(&trx->pdch[ts], dl_tbf, tfi);
@ -1012,6 +1019,7 @@ int alloc_algorithm_b(struct gprs_rlcmac_bts *bts,
continue;
OSMO_ASSERT(usf[ts] >= 0);
trx->current_load++;
LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning UL TS "
"%d\n", ts);

View File

@ -127,10 +127,10 @@ inline bool gprs_llc::fits_in_current_frame(uint8_t chunk_size) const
inline size_t gprs_llc_queue::size() const
{
return this ? m_queue_size : 0;
return m_queue_size;
}
inline size_t gprs_llc_queue::octets() const
{
return this ? m_queue_octets : 0;
return m_queue_octets;
}

View File

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

View File

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

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

@ -13,6 +13,7 @@
#include <sysmo_l1_if.h>
#include <gprs_debug.h>
#include <pcu_l1_if.h>
#include <bts.h>
extern void *tall_pcu_ctx;
@ -109,23 +110,6 @@ static void *prim_init(GsmL1_Prim_t *prim, GsmL1_PrimId_t id, struct femtol1_hdl
return &prim->u;
}
struct sapi_dir {
GsmL1_Sapi_t sapi;
GsmL1_Dir_t dir;
};
static const struct sapi_dir pdtch_sapis[] = {
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_RxUplink },
{ GsmL1_Sapi_Ptcch, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_Prach, GsmL1_Dir_RxUplink },
#if 0
{ GsmL1_Sapi_Ptcch, GsmL1_Dir_RxUplink },
{ GsmL1_Sapi_Pacch, GsmL1_Dir_TxDownlink },
#endif
};
/* connect PDTCH */
int l1if_connect_pdch(void *obj, uint8_t ts)
{
@ -155,8 +139,8 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1h,
switch (rts_ind->sapi) {
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
rc = pcu_rx_rts_req_pdtch((long)fl1h->priv, rts_ind->u8Tn,
rts_ind->u16Arfcn, rts_ind->u32Fn, rts_ind->u8BlockNbr);
rc = pcu_rx_rts_req_pdtch(fl1h->trx_no, rts_ind->u8Tn,
rts_ind->u32Fn, rts_ind->u8BlockNbr);
case GsmL1_Sapi_Ptcch:
// FIXME
default:
@ -206,6 +190,8 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
data_ind->msgUnitParam.u8Size-1);
get_meas(&meas, &data_ind->measParam);
bts_update_tbf_ta("PH-DATA", data_ind->u32Fn, fl1h->trx_no,
data_ind->u8Tn, qta2ta(meas.bto));
switch (data_ind->sapi) {
case GsmL1_Sapi_Pdtch:
@ -215,7 +201,7 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
!= GsmL1_PdtchPlType_Full)
break;
/* PDTCH / PACCH frame handling */
pcu_rx_data_ind_pdtch((long)fl1h->priv, data_ind->u8Tn,
pcu_rx_data_ind_pdtch(fl1h->trx_no, data_ind->u8Tn,
data_ind->msgUnitParam.u8Buffer + 1,
data_ind->msgUnitParam.u8Size - 1,
data_ind->u32Fn,
@ -237,33 +223,14 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
static int handle_ph_ra_ind(struct femtol1_hdl *fl1h, GsmL1_PhRaInd_t *ra_ind)
{
uint8_t acc_delay;
pcu_rx_ra_time(ra_ind->u16Arfcn, ra_ind->u32Fn, ra_ind->u8Tn);
if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
return 0;
DEBUGP(DL1IF, "Rx PH-RA.ind");
/* check for under/overflow / sign */
if (ra_ind->measParam.i16BurstTiming < 0)
acc_delay = 0;
else
acc_delay = ra_ind->measParam.i16BurstTiming >> 2;
LOGP(DL1IF, LOGL_NOTICE, "got (P)RACH request, TA = %u (ignored)\n",
acc_delay);
#warning "The (P)RACH request is just dropped here"
#if 0
if (acc_delay > bts->max_ta) {
LOGP(DL1C, LOGL_INFO, "ignoring RACH request %u > max_ta(%u)\n",
acc_delay, btsb->max_ta);
return 0;
}
#endif
bts_update_tbf_ta("PH-RA", ra_ind->u32Fn, fl1h->trx_no, ra_ind->u8Tn,
qta2ta(ra_ind->measParam.i16BurstTiming));
return 0;
}
@ -357,7 +324,7 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
return 0;
}
void *l1if_open_pdch(void *priv, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
{
struct femtol1_hdl *fl1h;
int rc;
@ -367,7 +334,7 @@ void *l1if_open_pdch(void *priv, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
return NULL;
fl1h->hLayer1 = hlayer1;
fl1h->priv = priv;
fl1h->trx_no = trx_no;
fl1h->clk_cal = 0;
/* default clock source: OCXO */
fl1h->clk_src = SuperFemto_ClkSrcId_Ocxo;

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

@ -35,7 +35,7 @@ extern "C" {
#include <pcu_l1_if.h>
#include <gprs_debug.h>
#include <gprs_bssgp_pcu.h>
#include <pcuif_proto.h>
#include <osmocom/pcu/pcuif_proto.h>
#include <bts.h>
#include <tbf.h>
@ -100,7 +100,7 @@ static void pcu_sock_close(struct pcu_sock_state *state, int lost)
/* disable all slots, kick all TBFs */
for (trx = 0; trx < 8; trx++) {
#ifdef ENABLE_SYSMODSP
#ifdef ENABLE_DIRECT_PHY
if (bts->trx[trx].fl1h) {
l1if_close_pdch(bts->trx[trx].fl1h);
bts->trx[trx].fl1h = NULL;

View File

@ -38,13 +38,14 @@ extern "C" {
#include <pcu_l1_if.h>
#include <gprs_debug.h>
#include <gprs_bssgp_pcu.h>
#include <pcuif_proto.h>
#include <osmocom/pcu/pcuif_proto.h>
#include <bts.h>
#include <tbf.h>
// FIXME: move this, when changed from c++ to c.
extern "C" {
void *l1if_open_pdch(void *priv, uint32_t hlayer1, struct gsmtap_inst *gsmtap);
void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1,
struct gsmtap_inst *gsmtap);
int l1if_connect_pdch(void *obj, uint8_t ts);
int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
@ -128,7 +129,8 @@ void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
{
struct gprs_rlcmac_bts *bts = bts_main_data();
#ifdef ENABLE_SYSMODSP
gsmtap_send(bts->gsmtap, arfcn, ts, GSMTAP_CHANNEL_PACCH, 0, fn, 0, 0, msg->data, msg->len);
#ifdef ENABLE_DIRECT_PHY
if (bts->trx[trx].fl1h) {
l1if_pdch_req(bts->trx[trx].fl1h, ts, 0, fn, arfcn, block_nr,
msg->data, msg->len);
@ -136,7 +138,6 @@ void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
return;
}
#endif
gsmtap_send(bts->gsmtap, arfcn, ts, GSMTAP_CHANNEL_PACCH, 0, fn, 0, 0, msg->data, msg->len);
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr,
msg->data, msg->len);
msgb_free(msg);
@ -147,7 +148,8 @@ void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
{
struct gprs_rlcmac_bts *bts = bts_main_data();
#ifdef ENABLE_SYSMODSP
gsmtap_send(bts->gsmtap, arfcn, ts, GSMTAP_CHANNEL_PACCH, 0, fn, 0, 0, msg->data, msg->len);
#ifdef ENABLE_DIRECT_PHY
if (bts->trx[trx].fl1h) {
l1if_pdch_req(bts->trx[trx].fl1h, ts, 1, fn, arfcn, block_nr,
msg->data, msg->len);
@ -155,7 +157,6 @@ void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
return;
}
#endif
gsmtap_send(bts->gsmtap, arfcn, ts, GSMTAP_CHANNEL_PACCH, 0, fn, 0, 0, msg->data, msg->len);
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
msg->data, msg->len);
msgb_free(msg);
@ -213,7 +214,15 @@ static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind, struct gsmtap_inst
int rc;
pcu_l1_meas meas;
meas.set_rssi(data_ind->rssi);
#ifndef ENABLE_DIRECT_PHY
/* convert BER to % value */
meas.set_ber(data_ind->ber10k / 100);
meas.set_bto(data_ind->ta_offs_qbits);
meas.set_link_qual(data_ind->lqual_cb / 10);
LOGP(DL1IF, LOGL_DEBUG, "Data indication with raw measurements "
"received: BER10k = %d, BTO = %d, Q = %d\n", data_ind->ber10k,
data_ind->ta_offs_qbits, data_ind->lqual_cb);
#endif
LOGP(DL1IF, LOGL_DEBUG, "Data indication received: sapi=%d arfcn=%d "
"block=%d data=%s\n", data_ind->sapi,
data_ind->arfcn, data_ind->block_nr,
@ -263,11 +272,11 @@ static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
}
// FIXME: remove this, when changed from c++ to c.
extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts,
uint32_t fn, uint8_t block_nr)
{
return gprs_rlcmac_rcv_rts_block(bts_main_data(),
trx, ts, arfcn, fn, block_nr);
trx, ts, fn, block_nr);
}
static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
@ -281,7 +290,7 @@ static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
switch (rts_req->sapi) {
case PCU_IF_SAPI_PDTCH:
pcu_rx_rts_req_pdtch(rts_req->trx_nr, rts_req->ts_nr,
rts_req->arfcn, rts_req->fn, rts_req->block_nr);
rts_req->fn, rts_req->block_nr);
break;
case PCU_IF_SAPI_PTCCH:
/* FIXME */
@ -313,7 +322,8 @@ static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
case PCU_IF_SAPI_RACH:
rc = BTS::main_bts()->rcv_rach(
rach_ind->ra, rach_ind->fn,
rach_ind->qta);
rach_ind->qta, rach_ind->is_11bit,
(ph_burst_type)rach_ind->burst_type);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received PCU rach request with "
@ -331,7 +341,7 @@ static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
struct gprs_rlcmac_pdch *pdch;
struct in_addr ia;
int rc = 0;
int trx, ts;
unsigned int trx, ts;
int i;
if (info_ind->version != PCU_IF_VERSION) {
@ -347,9 +357,9 @@ static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
LOGP(DL1IF, LOGL_NOTICE, "BTS not available\n");
bssgp_failed:
/* free all TBF */
for (trx = 0; trx < 8; trx++) {
for (trx = 0; trx < ARRAY_SIZE(bts->trx); trx++) {
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
for (ts = 0; ts < 8; ts++)
for (ts = 0; ts < ARRAY_SIZE(bts->trx[0].pdch); ts++)
bts->trx[trx].pdch[ts].free_resources();
}
gprs_bssgp_destroy();
@ -441,16 +451,16 @@ bssgp_failed:
bts->initial_cs_ul = bts->initial_cs_dl;
}
for (trx = 0; trx < 8; trx++) {
for (trx = 0; trx < ARRAY_SIZE(bts->trx); trx++) {
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
if ((info_ind->flags & PCU_IF_FLAG_SYSMO)
&& info_ind->trx[trx].hlayer1) {
#ifdef ENABLE_SYSMODSP
#ifdef ENABLE_DIRECT_PHY
LOGP(DL1IF, LOGL_DEBUG, " TRX %d hlayer1=%x\n", trx,
info_ind->trx[trx].hlayer1);
if (!bts->trx[trx].fl1h)
bts->trx[trx].fl1h = l1if_open_pdch(
(void *)trx,
trx,
info_ind->trx[trx].hlayer1,
bts->gsmtap);
if (!bts->trx[trx].fl1h) {
@ -466,12 +476,12 @@ bssgp_failed:
#endif
}
for (ts = 0; ts < 8; ts++) {
for (ts = 0; ts < ARRAY_SIZE(bts->trx[0].pdch); ts++) {
pdch = &bts->trx[trx].pdch[ts];
if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
/* FIXME: activate dynamically at RLCMAC */
if (!pdch->is_enabled()) {
#ifdef ENABLE_SYSMODSP
#ifdef ENABLE_DIRECT_PHY
if ((info_ind->flags &
PCU_IF_FLAG_SYSMO))
l1if_connect_pdch(

View File

@ -33,6 +33,15 @@ extern "C" {
}
#endif
static inline uint8_t qta2ta(int16_t qta)
{
if (qta < 0)
return 0;
if (qta > 252)
qta = 252;
return qta >> 2;
}
/*
* L1 Measurement values
*/
@ -48,7 +57,8 @@ struct pcu_l1_meas_ts {
}
pcu_l1_meas_ts() :
have_ms_i_level(0)
have_ms_i_level(0),
ms_i_level(0)
{}
#endif
};
@ -100,7 +110,14 @@ struct pcu_l1_meas {
have_ms_rx_qual(0),
have_ms_c_value(0),
have_ms_sign_var(0),
have_ms_i_level(0)
have_ms_i_level(0),
rssi(0),
ber(0),
bto(0),
link_qual(0),
ms_rx_qual(0),
ms_c_value(0),
ms_sign_var(0)
{}
#endif
};
@ -124,7 +141,7 @@ int pcu_sock_send(struct msgb *msg);
#ifdef __cplusplus
extern "C" {
#endif
int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts,
uint32_t fn, uint8_t block_nr);
int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data,

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,10 +211,17 @@ int main(int argc, char *argv[])
bts->dl_tbf_idle_msec = 2000;
bts->llc_idle_ack_csec = 10;
/*
* By default resegmentation is supported in DL
* can also be configured through VTY
*/
bts->dl_arq_type = EGPRS_ARQ1;
msgb_set_talloc_ctx(tall_pcu_ctx);
osmo_init_logging(&gprs_log_info);
osmo_stats_init(tall_pcu_ctx);
rate_ctr_init(tall_pcu_ctx);
gprs_ns_set_log_ss(DNS);
bssgp_set_log_ss(DBSSGP);
@ -244,7 +252,8 @@ int main(int argc, char *argv[])
fprintf(stderr, "No config file: '%s' Using default config.\n",
config_file);
rc = telnet_init(tall_pcu_ctx, NULL, 4240);
rc = telnet_init_dynif(tall_pcu_ctx, NULL, vty_get_bind_addr(),
OSMO_VTY_PORT_PCU);
if (rc < 0) {
fprintf(stderr, "Error initializing telnet\n");
exit(1);

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,25 @@ DEFUN(cfg_pcu_no_mcs_max,
return CMD_SUCCESS;
}
#define DL_STR "downlink specific configuration\n"
DEFUN(cfg_pcu_dl_arq_type,
cfg_pcu_dl_arq_cmd,
"egprs dl arq-type (spb|arq2)",
EGPRS_STR DL_STR "ARQ options\n"
"enable SPB(ARQ1) support\n"
"enable ARQ2 support")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
if (!strcmp(argv[0], "arq2"))
bts->dl_arq_type = 1;
else
bts->dl_arq_type = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_window_size,
cfg_pcu_window_size_cmd,
"window-size <0-1024> [<0-256>]",
@ -954,6 +971,7 @@ int pcu_vty_init(const struct log_info *cat)
install_element(PCU_NODE, &cfg_pcu_no_cs_downgrade_thrsh_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_lqual_ranges_cmd);
install_element(PCU_NODE, &cfg_pcu_mcs_cmd);
install_element(PCU_NODE, &cfg_pcu_dl_arq_cmd);
install_element(PCU_NODE, &cfg_pcu_no_mcs_cmd);
install_element(PCU_NODE, &cfg_pcu_mcs_max_cmd);
install_element(PCU_NODE, &cfg_pcu_no_mcs_max_cmd);

View File

@ -61,6 +61,8 @@ static void tbf_print_vty_info(struct vty *vty, gprs_rlcmac_tbf *tbf)
if (tbf->pdch[i])
vty_out(vty, "%d%s ", i, is_ctrl ? "!" : "");
}
if (tbf->trx != NULL)
vty_out(vty, " TRX_ID=%d", tbf->trx->trx_no);
vty_out(vty, " CS=%s WS=%d",
tbf->current_cs().name(), tbf->window()->ws());
@ -68,12 +70,26 @@ static void tbf_print_vty_info(struct vty *vty, gprs_rlcmac_tbf *tbf)
gprs_rlc_ul_window *win = &ul_tbf->m_window;
vty_out(vty, " V(Q)=%d V(R)=%d",
win->v_q(), win->v_r());
vty_out(vty, "%s", VTY_NEWLINE);
vty_out(vty, " TBF Statistics:%s", VTY_NEWLINE);
if(GprsCodingScheme::GPRS == tbf->ms()->mode()) {
vty_out_rate_ctr_group(vty, " ", ul_tbf->m_ul_gprs_ctrs);
} else {
vty_out_rate_ctr_group(vty, " ", ul_tbf->m_ul_egprs_ctrs);
}
}
if (dl_tbf) {
gprs_rlc_dl_window *win = &dl_tbf->m_window;
vty_out(vty, " V(A)=%d V(S)=%d nBSN=%d%s",
win->v_a(), win->v_s(), win->resend_needed(),
win->window_stalled() ? " STALLED" : "");
vty_out(vty, "%s", VTY_NEWLINE);
vty_out_rate_ctr_group(vty, " ", tbf->m_ctrs);
if(GprsCodingScheme::GPRS == tbf->ms()->mode()) {
vty_out_rate_ctr_group(vty, " ", dl_tbf->m_dl_gprs_ctrs);
} else {
vty_out_rate_ctr_group(vty, " ", dl_tbf->m_dl_egprs_ctrs);
}
}
vty_out(vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
}
@ -102,7 +118,7 @@ int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
llist_for_each(ms_iter, &bts->ms_store().ms_list()) {
GprsMs *ms = ms_iter->entry();
vty_out(vty, "MS TLLI=%08x, TA=%d, CS-UL=%s, CS-DL=%s, LLC=%d, "
vty_out(vty, "MS TLLI=%08x, TA=%d, CS-UL=%s, CS-DL=%s, LLC=%zd, "
"IMSI=%s%s",
ms->tlli(),
ms->ta(), ms->current_cs_ul().name(),
@ -136,9 +152,9 @@ static int show_ms(struct vty *vty, GprsMs *ms)
if (slots & (1 << i))
vty_out(vty, "%d ", i);
vty_out(vty, "%s", VTY_NEWLINE);
vty_out(vty, " LLC queue length: %d%s", ms->llc_queue()->size(),
vty_out(vty, " LLC queue length: %zd%s", ms->llc_queue()->size(),
VTY_NEWLINE);
vty_out(vty, " LLC queue octets: %d%s", ms->llc_queue()->octets(),
vty_out(vty, " LLC queue octets: %zd%s", ms->llc_queue()->octets(),
VTY_NEWLINE);
if (ms->l1_meas()->have_rssi)
vty_out(vty, " RSSI: %d dBm%s",
@ -169,16 +185,22 @@ static int show_ms(struct vty *vty, GprsMs *ms)
vty_out(vty, " MS I level (slot %d): %d dB%s",
i, ms->l1_meas()->ts[i].ms_i_level, VTY_NEWLINE);
}
vty_out(vty, " RLC/MAC DL Control Msg: %d%s", ms->dl_ctrl_msg(),
VTY_NEWLINE);
if (ms->ul_tbf())
vty_out(vty, " Uplink TBF: TFI=%d, state=%s%s",
ms->ul_tbf()->tfi(),
ms->ul_tbf()->state_name(),
VTY_NEWLINE);
if (ms->dl_tbf())
if (ms->dl_tbf()) {
vty_out(vty, " Downlink TBF: TFI=%d, state=%s%s",
ms->dl_tbf()->tfi(),
ms->dl_tbf()->state_name(),
VTY_NEWLINE);
vty_out(vty, " Current DL Throughput: %d Kbps %s",
ms->dl_tbf()->m_bw.dl_throughput,
VTY_NEWLINE);
}
llist_for_each(i_tbf, &ms->old_tbfs())
vty_out(vty, " Old %-19s TFI=%d, state=%s%s",

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

212
src/rlc.h
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.
@ -185,7 +300,7 @@ struct gprs_rlc_dl_window: public gprs_rlc_window {
const uint16_t v_s() const;
const uint16_t v_s_mod(int offset) const;
const uint16_t v_a() const;
const int16_t distance() const;
const uint16_t distance() const;
/* Methods to manage reception */
int resend_needed();
@ -226,12 +341,16 @@ struct gprs_rlc_ul_window: public gprs_rlc_window {
const uint16_t v_r() const;
const uint16_t v_q() const;
const void set_v_r(int);
const void set_v_q(int);
const uint16_t ssn() const;
bool is_in_window(uint16_t bsn) const;
bool is_received(uint16_t bsn) const;
void update_rbb(char *rbb);
uint16_t update_egprs_rbb(uint8_t *rbb);
void raise_v_r_to(int moves);
void raise_v_r(const uint16_t bsn);
uint16_t raise_v_q();
@ -287,67 +406,6 @@ struct rlc_li_field_egprs {
uint8_t e:1,
li:7;
} __attribute__ ((packed));
struct gprs_rlc_ul_header_egprs_3 {
uint8_t r:1,
si:1,
cv:4,
tfi_a:2;
uint8_t tfi_b:3,
bsn1_a:5;
uint8_t bsn1_b:6,
cps_a:2;
uint8_t cps_b:2,
spb:2,
rsb:1,
pi:1,
spare:1,
dummy:1;
} __attribute__ ((packed));
struct gprs_rlc_dl_header_egprs_1 {
uint8_t usf:3,
es_p:2,
rrbp:2,
tfi_a:1;
uint8_t tfi_b:4,
pr:2,
bsn1_a:2;
uint8_t bsn1_b:8;
uint8_t bsn1_c:1,
bsn2_a:7;
uint8_t bsn2_b:3,
cps:5;
} __attribute__ ((packed));
struct gprs_rlc_dl_header_egprs_2 {
uint8_t usf:3,
es_p:2,
rrbp:2,
tfi_a:1;
uint8_t tfi_b:4,
pr:2,
bsn1_a:2;
uint8_t bsn1_b:8;
uint8_t bsn1_c:1,
cps:3,
dummy:4;
} __attribute__ ((packed));
struct gprs_rlc_dl_header_egprs_3 {
uint8_t usf:3,
es_p:2,
rrbp:2,
tfi_a:1;
uint8_t tfi_b:4,
pr:2,
bsn1_a:2;
uint8_t bsn1_b:8;
uint8_t bsn1_c:1,
cps:4,
spb:2,
dummy:1;
} __attribute__ ((packed));
#else
# error "Only little endian headers are supported yet. TODO: add missing structs"
#endif
@ -485,7 +543,7 @@ inline void gprs_rlc_dl_window::raise(int moves)
m_v_a = (m_v_a + moves) & mod_sns();
}
inline const int16_t gprs_rlc_dl_window::distance() const
inline const uint16_t gprs_rlc_dl_window::distance() const
{
return (m_v_s - m_v_a) & mod_sns();
}
@ -516,6 +574,16 @@ inline bool gprs_rlc_ul_window::is_received(uint16_t bsn) const
return is_in_window(bsn) && m_v_n.is_received(bsn) && offset_v_r < ws();
}
inline const void gprs_rlc_ul_window::set_v_r(int v_r)
{
m_v_r = v_r;
}
inline const void gprs_rlc_ul_window::set_v_q(int v_q)
{
m_v_q = v_q;
}
inline const uint16_t gprs_rlc_ul_window::v_r() const
{
return m_v_r;

View File

@ -26,6 +26,7 @@
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
}
#include <errno.h>
@ -48,32 +49,39 @@ int SBAController::alloc(
struct gprs_rlcmac_pdch *pdch;
struct gprs_rlcmac_sba *sba;
int8_t trx, ts;
int8_t ts;
uint32_t fn;
bool trxs[8];
int selected_trx;
int ret;
ret = m_bts.get_possible_trxs_sba(trxs);
if (ret == -EINVAL)
return -EINVAL;
selected_trx = m_bts.get_suitable_trx(trxs);
for (ts = 7; ts >= 0; ts--) {
pdch = &m_bts.bts_data()->trx[selected_trx].pdch[ts];
if (!pdch->is_enabled())
continue;
break;
}
if (ts < 0)
return -EINVAL;
sba = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_sba);
if (!sba)
return -ENOMEM;
for (trx = 0; trx < 8; trx++) {
for (ts = 7; ts >= 0; ts--) {
pdch = &m_bts.bts_data()->trx[trx].pdch[ts];
if (!pdch->is_enabled())
continue;
break;
}
if (ts >= 0)
break;
}
if (trx == 8) {
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH available.\n");
talloc_free(sba);
if (!gsm48_ta_is_valid(ta))
return -EINVAL;
}
fn = (pdch->last_rts_fn + AGCH_START_OFFSET) % 2715648;
sba->trx_no = trx;
sba->trx_no = selected_trx;
sba->ts_no = ts;
sba->fn = fn;
sba->ta = ta;
@ -81,9 +89,11 @@ int SBAController::alloc(
llist_add(&sba->list, &m_sbas);
m_bts.sba_allocated();
*_trx = trx;
*_trx = selected_trx;
*_ts = ts;
*_fn = fn;
LOGP(DRLCMAC, LOGL_DEBUG, " sba fn=%d ts = %d trx = %d\n", fn, ts, selected_trx);
return 0;
}

View File

@ -33,6 +33,7 @@
extern "C" {
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/stats.h>
}
#include <errno.h>
@ -42,6 +43,88 @@ extern void *tall_pcu_ctx;
static void tbf_timer_cb(void *_tbf);
static const struct rate_ctr_desc tbf_ctr_description[] = {
{ "rlc.nacked", "RLC Nacked " },
};
static const struct rate_ctr_desc tbf_dl_gprs_ctr_description[] = {
{ "gprs.downlink.cs1", "CS1 " },
{ "gprs.downlink.cs2", "CS2 " },
{ "gprs.downlink.cs3", "CS3 " },
{ "gprs.downlink.cs4", "CS4 " },
};
static const struct rate_ctr_desc tbf_dl_egprs_ctr_description[] = {
{ "egprs.downlink.mcs1", "MCS1 " },
{ "egprs.downlink.mcs2", "MCS2 " },
{ "egprs.downlink.mcs3", "MCS3 " },
{ "egprs.downlink.mcs4", "MCS4 " },
{ "egprs.downlink.mcs5", "MCS5 " },
{ "egprs.downlink.mcs6", "MCS6 " },
{ "egprs.downlink.mcs7", "MCS7 " },
{ "egprs.downlink.mcs8", "MCS8 " },
{ "egprs.downlink.mcs9", "MCS9 " },
};
static const struct rate_ctr_desc tbf_ul_gprs_ctr_description[] = {
{ "gprs.uplink.cs1", "CS1 " },
{ "gprs.uplink.cs2", "CS2 " },
{ "gprs.uplink.cs3", "CS3 " },
{ "gprs.uplink.cs4", "CS4 " },
};
static const struct rate_ctr_desc tbf_ul_egprs_ctr_description[] = {
{ "egprs.uplink.mcs1", "MCS1 " },
{ "egprs.uplink.mcs2", "MCS2 " },
{ "egprs.uplink.mcs3", "MCS3 " },
{ "egprs.uplink.mcs4", "MCS4 " },
{ "egprs.uplink.mcs5", "MCS5 " },
{ "egprs.uplink.mcs6", "MCS6 " },
{ "egprs.uplink.mcs7", "MCS7 " },
{ "egprs.uplink.mcs8", "MCS8 " },
{ "egprs.uplink.mcs9", "MCS9 " },
};
static const struct rate_ctr_group_desc tbf_ctrg_desc = {
"pcu.tbf",
"TBF Statistics",
OSMO_STATS_CLASS_SUBSCRIBER,
ARRAY_SIZE(tbf_ctr_description),
tbf_ctr_description,
};
static const struct rate_ctr_group_desc tbf_dl_gprs_ctrg_desc = {
"tbf.gprs",
"Data Blocks",
OSMO_STATS_CLASS_SUBSCRIBER,
ARRAY_SIZE(tbf_dl_gprs_ctr_description),
tbf_dl_gprs_ctr_description,
};
static const struct rate_ctr_group_desc tbf_dl_egprs_ctrg_desc = {
"tbf.egprs",
"Data Blocks",
OSMO_STATS_CLASS_SUBSCRIBER,
ARRAY_SIZE(tbf_dl_egprs_ctr_description),
tbf_dl_egprs_ctr_description,
};
static const struct rate_ctr_group_desc tbf_ul_gprs_ctrg_desc = {
"tbf.gprs",
"Data Blocks",
OSMO_STATS_CLASS_SUBSCRIBER,
ARRAY_SIZE(tbf_ul_gprs_ctr_description),
tbf_ul_gprs_ctr_description,
};
static const struct rate_ctr_group_desc tbf_ul_egprs_ctrg_desc = {
"tbf.egprs",
"Data Blocks",
OSMO_STATS_CLASS_SUBSCRIBER,
ARRAY_SIZE(tbf_ul_egprs_ctr_description),
tbf_ul_egprs_ctr_description,
};
gprs_rlcmac_tbf::Meas::Meas() :
rssi_sum(0),
rssi_num(0)
@ -74,11 +157,12 @@ gprs_rlcmac_tbf::gprs_rlcmac_tbf(BTS *bts_, gprs_rlcmac_tbf_direction dir) :
m_tfi(0),
m_created_ts(0),
m_ms(NULL),
m_ta(0),
m_ta(GSM48_TA_INVALID),
m_ms_class(0),
m_list(this),
m_ms_list(this),
m_egprs_enabled(false)
m_egprs_enabled(false),
m_ctrs(NULL)
{
/* The classes of these members do not have proper constructors yet.
* Just set them to 0 like talloc_zero did */
@ -151,7 +235,8 @@ void gprs_rlcmac_tbf::set_ta(uint8_t ta)
if (ms())
ms()->set_ta(ta);
m_ta = ta;
if (gsm48_ta_is_valid(ta))
m_ta = ta;
}
uint8_t gprs_rlcmac_tbf::ms_class() const
@ -188,6 +273,13 @@ const gprs_llc_queue *gprs_rlcmac_tbf::llc_queue() const
return m_ms ? m_ms->llc_queue() : NULL;
}
int gprs_rlcmac_tbf::llc_queue_size() const
{
/* m_ms->llc_queue() never returns NULL: GprsMs::m_llc_queue is a
* member instance. */
return m_ms ? m_ms->llc_queue()->size() : 0;
}
void gprs_rlcmac_tbf::set_ms(GprsMs *ms)
{
if (m_ms == ms)
@ -303,7 +395,7 @@ static void tbf_unlink_pdch(struct gprs_rlcmac_tbf *tbf)
for (ts = 0; ts < 8; ts++) {
if (!tbf->pdch[ts])
continue;
tbf->trx->current_load--;
tbf->pdch[ts]->detach_tbf(tbf);
tbf->pdch[ts] = NULL;
}
@ -313,10 +405,19 @@ void tbf_free(struct gprs_rlcmac_tbf *tbf)
{
/* update counters */
if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf);
tbf->bts->tbf_ul_freed();
if (tbf->state_is(GPRS_RLCMAC_FLOW))
tbf->bts->tbf_ul_aborted();
rate_ctr_group_free(ul_tbf->m_ul_egprs_ctrs);
rate_ctr_group_free(ul_tbf->m_ul_gprs_ctrs);
} else {
gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf);
if (tbf->is_egprs_enabled()) {
rate_ctr_group_free(dl_tbf->m_dl_egprs_ctrs);
} else {
rate_ctr_group_free(dl_tbf->m_dl_gprs_ctrs);
}
tbf->bts->tbf_dl_freed();
if (tbf->state_is(GPRS_RLCMAC_FLOW))
tbf->bts->tbf_dl_aborted();
@ -352,6 +453,8 @@ void tbf_free(struct gprs_rlcmac_tbf *tbf)
if (tbf->ms())
tbf->set_ms(NULL);
rate_ctr_group_free(tbf->m_ctrs);
LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF ends here **********\n");
talloc_free(tbf);
}
@ -375,6 +478,12 @@ int gprs_rlcmac_tbf::update()
return -rc;
}
if (is_egprs_enabled()) {
gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(this);
if (dl_tbf)
dl_tbf->egprs_calc_window_size();
}
return 0;
}
@ -394,7 +503,6 @@ int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf)
const char *gprs_rlcmac_tbf::tbf_state_name[] = {
"NULL",
"ASSIGN",
"WAIT ASSIGN",
"FLOW",
"FINISHED",
"WAIT RELEASE",
@ -495,12 +603,14 @@ void gprs_rlcmac_tbf::poll_timeout()
}
ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
bts->rlc_ack_timedout();
bts->pkt_ul_ack_nack_poll_timedout();
if (state_is(GPRS_RLCMAC_FINISHED)) {
gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(this);
ul_tbf->m_n3103++;
if (ul_tbf->m_n3103 == ul_tbf->bts->bts_data()->n3103) {
LOGP(DRLCMAC, LOGL_NOTICE,
"- N3103 exceeded\n");
bts->pkt_ul_ack_nack_poll_failed();
ul_tbf->set_state(GPRS_RLCMAC_RELEASING);
tbf_timer_start(ul_tbf, 3169, ul_tbf->bts->bts_data()->t3169, 0);
return;
@ -520,11 +630,13 @@ void gprs_rlcmac_tbf::poll_timeout()
ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
n3105++;
bts->rlc_ass_timedout();
bts->pua_poll_timedout();
if (n3105 == bts_data()->n3105) {
LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n");
set_state(GPRS_RLCMAC_RELEASING);
tbf_timer_start(this, 3195, bts_data()->t3195, 0);
bts->rlc_ass_failed();
bts->pua_poll_failed();
return;
}
/* reschedule UL assignment */
@ -540,11 +652,13 @@ void gprs_rlcmac_tbf::poll_timeout()
dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
n3105++;
bts->rlc_ass_timedout();
bts->pda_poll_timedout();
if (n3105 == bts->bts_data()->n3105) {
LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n");
set_state(GPRS_RLCMAC_RELEASING);
tbf_timer_start(this, 3195, bts_data()->t3195, 0);
bts->rlc_ass_failed();
bts->pda_poll_failed();
return;
}
/* reschedule DL assignment */
@ -561,12 +675,15 @@ void gprs_rlcmac_tbf::poll_timeout()
dl_tbf->n3105++;
if (dl_tbf->state_is(GPRS_RLCMAC_RELEASING))
bts->rlc_rel_timedout();
else
else {
bts->rlc_ack_timedout();
bts->pkt_dl_ack_nack_poll_timedout();
}
if (dl_tbf->n3105 == dl_tbf->bts->bts_data()->n3105) {
LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n");
dl_tbf->set_state(GPRS_RLCMAC_RELEASING);
tbf_timer_start(dl_tbf, 3195, dl_tbf->bts_data()->t3195, 0);
bts->pkt_dl_ack_nack_poll_failed();
bts->rlc_ack_failed();
return;
}
@ -624,6 +741,8 @@ static int setup_tbf(struct gprs_rlcmac_tbf *tbf,
"Allocated %s: trx = %d, ul_slots = %02x, dl_slots = %02x\n",
tbf->name(), tbf->trx->trx_no, tbf->ul_slots(), tbf->dl_slots());
tbf->m_ctrs = rate_ctr_group_alloc(tbf, &tbf_ctrg_desc, 0);
return 0;
}
@ -632,7 +751,9 @@ gprs_rlcmac_ul_tbf::gprs_rlcmac_ul_tbf(BTS *bts_) :
m_rx_counter(0),
m_n3103(0),
m_contention_resolution_done(0),
m_final_ack_sent(0)
m_final_ack_sent(0),
m_ul_gprs_ctrs(NULL),
m_ul_egprs_ctrs(NULL)
{
memset(&m_usf, 0, sizeof(m_usf));
}
@ -687,20 +808,25 @@ struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
if (egprs_ms_class > 0 && bts->egprs_enabled) {
tbf->enable_egprs();
tbf->m_window.set_sns(RLC_EGPRS_SNS);
/* TODO: Allow bigger UL windows when CRBB encoding is supported */
tbf->m_window.set_ws(RLC_EGPRS_MIN_WS);
setup_egprs_mode(bts, ms);
LOGP(DRLCMAC, LOGL_INFO, "Enabled EGPRS for %s, mode %s\n",
tbf->name(), GprsCodingScheme::modeName(ms->mode()));
}
rc = setup_tbf(tbf, ms, use_trx, ms_class, egprs_ms_class, single_slot);
if (tbf->is_egprs_enabled())
tbf->egprs_calc_ulwindow_size();
/* if no resource */
if (rc < 0) {
talloc_free(tbf);
return NULL;
}
tbf->m_ul_egprs_ctrs = rate_ctr_group_alloc(tbf, &tbf_ul_egprs_ctrg_desc, 0);
tbf->m_ul_gprs_ctrs = rate_ctr_group_alloc(tbf, &tbf_ul_gprs_ctrg_desc, 0);
llist_add(&tbf->list(), &bts->bts->ul_tbfs());
tbf->bts->tbf_ul_created();
@ -710,7 +836,8 @@ struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
gprs_rlcmac_dl_tbf::BandWidth::BandWidth() :
dl_bw_octets(0),
dl_loss_lost(0),
dl_loss_received(0)
dl_loss_received(0),
dl_throughput(0)
{
timerclear(&dl_bw_tv);
timerclear(&dl_loss_tv);
@ -722,7 +849,9 @@ gprs_rlcmac_dl_tbf::gprs_rlcmac_dl_tbf(BTS *bts_) :
m_wait_confirm(0),
m_dl_ack_requested(false),
m_last_dl_poll_fn(0),
m_last_dl_drained_fn(0)
m_last_dl_drained_fn(0),
m_dl_gprs_ctrs(NULL),
m_dl_egprs_ctrs(NULL)
{
memset(&m_llc_timer, 0, sizeof(m_llc_timer));
}
@ -781,18 +910,10 @@ struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
}
if (tbf->is_egprs_enabled()) {
unsigned int num_pdch = pcu_bitcount(tbf->dl_slots());
unsigned int ws = bts->ws_base + num_pdch * bts->ws_pdch;
ws = (ws / 32) * 32;
ws = OSMO_MAX(64, ws);
if (num_pdch == 1)
ws = OSMO_MIN(192, ws);
else
ws = OSMO_MIN(128 * num_pdch, ws);
LOGP(DRLCMAC, LOGL_INFO, "%s: Setting EGPRS window size to %d\n",
tbf->name(), ws);
tbf->m_window.set_ws(ws);
tbf->egprs_calc_window_size();
tbf->m_dl_egprs_ctrs = rate_ctr_group_alloc(tbf, &tbf_dl_egprs_ctrg_desc, 0);
} else {
tbf->m_dl_gprs_ctrs = rate_ctr_group_alloc(tbf, &tbf_dl_gprs_ctrg_desc, 0);
}
llist_add(&tbf->list(), &bts->bts->dl_tbfs());
@ -824,12 +945,6 @@ void gprs_rlcmac_tbf::handle_timeout()
case 0: /* assignment */
if ((state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH))) {
if (state_is(GPRS_RLCMAC_ASSIGN)) {
LOGP(DRLCMAC, LOGL_NOTICE, "%s releasing due to "
"PACCH assignment timeout (not yet sent).\n",
tbf_name(this));
tbf_free(this);
return;
} else if (state_is(GPRS_RLCMAC_WAIT_ASSIGN)) {
LOGP(DRLCMAC, LOGL_NOTICE, "%s releasing due to "
"PACCH assignment timeout.\n", tbf_name(this));
tbf_free(this);
@ -841,7 +956,7 @@ void gprs_rlcmac_tbf::handle_timeout()
if ((state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))) {
gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(this);
dl_tbf->m_wait_confirm = 0;
if (dl_tbf->state_is(GPRS_RLCMAC_WAIT_ASSIGN)) {
if (dl_tbf->state_is(GPRS_RLCMAC_ASSIGN)) {
tbf_assign_control_ts(dl_tbf);
if (!dl_tbf->upgrade_to_multislot) {
@ -996,14 +1111,13 @@ struct msgb *gprs_rlcmac_tbf::create_dl_ass(uint32_t fn, uint8_t ts)
encode_gsm_rlcmac_downlink(ass_vec, mac_control_block);
LOGPC(DCSN1, LOGL_NOTICE, "\n");
LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Downlink Assignment -------------------------\n");
bts->pkt_dl_assignemnt();
bitvec_pack(ass_vec, msgb_put(msg, 23));
bitvec_free(ass_vec);
talloc_free(mac_control_block);
if (poll_ass_dl) {
set_polling(new_poll_fn, ts);
if (new_dl_tbf->state_is(GPRS_RLCMAC_ASSIGN))
new_dl_tbf->set_state(GPRS_RLCMAC_WAIT_ASSIGN);
dl_ass_state = GPRS_RLCMAC_DL_ASS_WAIT_ACK;
LOGP(DRLCMACDL, LOGL_INFO,
"%s Scheduled DL Assignment polling on FN=%d, TS=%d\n",
@ -1020,6 +1134,35 @@ struct msgb *gprs_rlcmac_tbf::create_dl_ass(uint32_t fn, uint8_t ts)
return msg;
}
struct msgb *gprs_rlcmac_tbf::create_packet_access_reject()
{
struct msgb *msg;
msg = msgb_alloc(23, "rlcmac_ul_ass_rej");
bitvec *packet_access_rej = bitvec_alloc(23);
bitvec_unhex(packet_access_rej,
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
Encoding::write_packet_access_reject(
packet_access_rej, tlli());
bts->pkt_access_reject();
bitvec_pack(packet_access_rej, msgb_put(msg, 23));
bitvec_free(packet_access_rej);
ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
/* Start Tmr only if it is UL TBF */
if (direction == GPRS_RLCMAC_UL_TBF)
tbf_timer_start(this, 0, Treject_pacch);
return msg;
}
struct msgb *gprs_rlcmac_tbf::create_ul_ass(uint32_t fn, uint8_t ts)
{
struct msgb *msg;
@ -1071,13 +1214,12 @@ struct msgb *gprs_rlcmac_tbf::create_ul_ass(uint32_t fn, uint8_t ts)
decode_gsm_rlcmac_downlink(ass_vec, mac_control_block);
LOGPC(DCSN1, LOGL_NOTICE, "\n");
LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Uplink Assignment -------------------------\n");
bts->pkt_ul_assignment();
bitvec_free(ass_vec);
talloc_free(mac_control_block);
set_polling(new_poll_fn, ts);
ul_ass_state = GPRS_RLCMAC_UL_ASS_WAIT_ACK;
if (new_tbf->state_is(GPRS_RLCMAC_ASSIGN))
new_tbf->set_state(GPRS_RLCMAC_WAIT_ASSIGN);
LOGP(DRLCMACDL, LOGL_INFO,
"%s Scheduled UL Assignment polling on FN=%d, TS=%d\n",
name(), poll_fn, poll_ts);
@ -1176,14 +1318,11 @@ int gprs_rlcmac_tbf::set_tlli_from_ul(uint32_t new_tlli)
const char *tbf_name(gprs_rlcmac_tbf *tbf)
{
return tbf->name();
return tbf ? tbf->name() : "(no TBF)";
}
const char *gprs_rlcmac_tbf::name() const
{
if (this == NULL)
return "(no TBF)";
snprintf(m_name_buf, sizeof(m_name_buf) - 1,
"TBF(TFI=%d TLLI=0x%08x DIR=%s STATE=%s%s)",
m_tfi, tlli(),
@ -1249,3 +1388,37 @@ bool gprs_rlcmac_tbf::is_control_ts(uint8_t ts) const
{
return ts == control_ts;
}
struct gprs_rlcmac_ul_tbf *handle_tbf_reject(struct gprs_rlcmac_bts *bts,
GprsMs *ms, uint32_t tlli, uint8_t trx_no, uint8_t ts)
{
struct gprs_rlcmac_ul_tbf *ul_tbf = NULL;
struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
ul_tbf = talloc(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
if (!ul_tbf)
return ul_tbf;
talloc_set_destructor(ul_tbf, ul_tbf_dtor);
new (ul_tbf) gprs_rlcmac_ul_tbf(bts->bts);
if (!ms)
ms = bts->bts->ms_alloc(0, 0);
ms->set_tlli(tlli);
llist_add(&ul_tbf->list(), &bts->bts->ul_tbfs());
ul_tbf->bts->tbf_ul_created();
ul_tbf->set_state(GPRS_RLCMAC_ASSIGN);
ul_tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH);
ul_tbf->set_ms(ms);
ul_tbf->update_ms(tlli, GPRS_RLCMAC_UL_TBF);
ul_tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ;
ul_tbf->control_ts = ts;
ul_tbf->trx = trx;
ul_tbf->m_ctrs = rate_ctr_group_alloc(ul_tbf, &tbf_ctrg_desc, 0);
ul_tbf->m_ul_egprs_ctrs = rate_ctr_group_alloc(ul_tbf, &tbf_ul_egprs_ctrg_desc, 0);
ul_tbf->m_ul_gprs_ctrs = rate_ctr_group_alloc(ul_tbf, &tbf_ul_gprs_ctrg_desc, 0);
return ul_tbf;
}

100
src/tbf.h
View File

@ -40,11 +40,11 @@ class GprsMs;
#define Tassign_agch 0,200000 /* waiting after IMM.ASS confirm */
#define Tassign_pacch 2,0 /* timeout for pacch assigment */
#define Treject_pacch 0,2000 /* timeout for tbf reject for PRR*/
enum gprs_rlcmac_tbf_state {
GPRS_RLCMAC_NULL = 0, /* new created TBF */
GPRS_RLCMAC_ASSIGN, /* wait for DL transmission */
GPRS_RLCMAC_WAIT_ASSIGN,/* wait for confirmation */
GPRS_RLCMAC_ASSIGN, /* wait for downlink assignment */
GPRS_RLCMAC_FLOW, /* RLC/MAC flow, resource needed */
GPRS_RLCMAC_FINISHED, /* flow finished, wait for release */
GPRS_RLCMAC_WAIT_RELEASE,/* wait for release or restart of DL TBF */
@ -65,6 +65,7 @@ enum gprs_rlcmac_tbf_dl_ass_state {
enum gprs_rlcmac_tbf_ul_ass_state {
GPRS_RLCMAC_UL_ASS_NONE = 0,
GPRS_RLCMAC_UL_ASS_SEND_ASS, /* send uplink assignment on next RTS */
GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ, /* send assignment reject next RTS */
GPRS_RLCMAC_UL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
};
@ -79,6 +80,48 @@ enum gprs_rlcmac_tbf_direction {
GPRS_RLCMAC_UL_TBF
};
enum tbf_counters {
TBF_CTR_RLC_NACKED,
};
enum tbf_gprs_counters {
TBF_CTR_GPRS_DL_CS1,
TBF_CTR_GPRS_DL_CS2,
TBF_CTR_GPRS_DL_CS3,
TBF_CTR_GPRS_DL_CS4,
};
enum tbf_egprs_counters {
TBF_CTR_EGPRS_DL_MCS1,
TBF_CTR_EGPRS_DL_MCS2,
TBF_CTR_EGPRS_DL_MCS3,
TBF_CTR_EGPRS_DL_MCS4,
TBF_CTR_EGPRS_DL_MCS5,
TBF_CTR_EGPRS_DL_MCS6,
TBF_CTR_EGPRS_DL_MCS7,
TBF_CTR_EGPRS_DL_MCS8,
TBF_CTR_EGPRS_DL_MCS9,
};
enum tbf_gprs_ul_counters {
TBF_CTR_GPRS_UL_CS1,
TBF_CTR_GPRS_UL_CS2,
TBF_CTR_GPRS_UL_CS3,
TBF_CTR_GPRS_UL_CS4,
};
enum tbf_egprs_ul_counters {
TBF_CTR_EGPRS_UL_MCS1,
TBF_CTR_EGPRS_UL_MCS2,
TBF_CTR_EGPRS_UL_MCS3,
TBF_CTR_EGPRS_UL_MCS4,
TBF_CTR_EGPRS_UL_MCS5,
TBF_CTR_EGPRS_UL_MCS6,
TBF_CTR_EGPRS_UL_MCS7,
TBF_CTR_EGPRS_UL_MCS8,
TBF_CTR_EGPRS_UL_MCS9,
};
#define GPRS_RLCMAC_FLAG_CCCH 0 /* assignment on CCCH */
#define GPRS_RLCMAC_FLAG_PACCH 1 /* assignment on PACCH */
#define GPRS_RLCMAC_FLAG_UL_DATA 2 /* uplink data received */
@ -104,6 +147,7 @@ struct gprs_rlcmac_tbf {
struct msgb *create_dl_ass(uint32_t fn, uint8_t ts);
struct msgb *create_ul_ass(uint32_t fn, uint8_t ts);
struct msgb *create_packet_access_reject();
GprsMs *ms() const;
void set_ms(GprsMs *ms);
@ -142,8 +186,7 @@ struct gprs_rlcmac_tbf {
uint8_t ms_class() const;
void set_ms_class(uint8_t);
GprsCodingScheme current_cs() const;
gprs_llc_queue *llc_queue();
const gprs_llc_queue *llc_queue() const;
int llc_queue_size() const;
time_t created_ts() const;
uint8_t dl_slots() const;
@ -225,13 +268,18 @@ struct gprs_rlcmac_tbf {
uint8_t m_tfi;
time_t m_created_ts;
struct rate_ctr_group *m_ctrs;
protected:
gprs_rlcmac_bts *bts_data() const;
int set_tlli_from_ul(uint32_t new_tlli);
void merge_and_clear_ms(GprsMs *old_ms);
static const char *tbf_state_name[7];
gprs_llc_queue *llc_queue();
const gprs_llc_queue *llc_queue() const;
static const char *tbf_state_name[6];
class GprsMs *m_ms;
@ -262,6 +310,9 @@ struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
void tbf_free(struct gprs_rlcmac_tbf *tbf);
struct gprs_rlcmac_ul_tbf *handle_tbf_reject(struct gprs_rlcmac_bts *bts,
GprsMs *ms, uint32_t tlli, uint8_t trx_no, uint8_t ts_no);
int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
@ -316,7 +367,10 @@ inline bool gprs_rlcmac_tbf::is_tfi_assigned() const
{
/* The TBF is established or has been assigned by a IMM.ASS for
* download */
return state > GPRS_RLCMAC_ASSIGN;
return state > GPRS_RLCMAC_ASSIGN ||
(direction == GPRS_RLCMAC_DL_TBF &&
state == GPRS_RLCMAC_ASSIGN &&
(state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)));
}
inline uint8_t gprs_rlcmac_tbf::tfi() const
@ -372,6 +426,9 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
int release();
int abort();
void egprs_calc_window_size();
void update_coding_scheme_counter_dl(const GprsCodingScheme cs);
/* TODO: add the gettimeofday as parameter */
struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx);
@ -390,6 +447,7 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
struct BandWidth {
struct timeval dl_bw_tv; /* timestamp for dl bw calculation */
uint32_t dl_bw_octets; /* number of octets since bw_tv */
uint32_t dl_throughput; /* throughput to be displayed in stats */
struct timeval dl_loss_tv; /* timestamp for loss calculation */
uint16_t dl_loss_lost; /* sum of lost packets */
@ -398,6 +456,9 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
BandWidth();
} m_bw;
struct rate_ctr_group *m_dl_gprs_ctrs;
struct rate_ctr_group *m_dl_egprs_ctrs;
protected:
struct ana_result {
unsigned received_packets;
@ -421,6 +482,11 @@ protected:
int analyse_errors(char *show_rbb, uint8_t ssn, ana_result *res);
void schedule_next_frame();
enum egprs_rlc_dl_reseg_bsn_state egprs_dl_get_data
(int bsn, uint8_t **block_data);
unsigned int get_egprs_dl_spb_status(int bsn);
enum egprs_rlcmac_dl_spb get_egprs_dl_spb(int bsn);
struct osmo_timer_list m_llc_timer;
};
@ -439,6 +505,25 @@ struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
int assemble_forward_llc(const gprs_rlc_data *data);
int snd_ul_ud();
egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_spb(
const struct gprs_rlc_data_info *rlc,
struct gprs_rlc_data *block,
uint8_t *data, const uint8_t block_idx);
egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_first_seg(
const struct gprs_rlc_data_info *rlc,
struct gprs_rlc_data *block,
uint8_t *data, const uint8_t block_idx);
egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_second_seg(
const struct gprs_rlc_data_info *rlc,
struct gprs_rlc_data *block,
uint8_t *data, const uint8_t block_idx);
void egprs_calc_ulwindow_size();
void update_coding_scheme_counter_ul(const GprsCodingScheme cs);
/* Please note that all variables here will be reset when changing
* from WAIT RELEASE back to FLOW state (re-use of TBF).
* All states that need reset must be in this struct, so this is why
@ -451,6 +536,9 @@ struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
uint8_t m_contention_resolution_done; /* set after done */
uint8_t m_final_ack_sent; /* set if we sent final ack */
struct rate_ctr_group *m_ul_gprs_ctrs;
struct rate_ctr_group *m_ul_egprs_ctrs;
protected:
void maybe_schedule_uplink_acknack(const gprs_rlc_data_info *rlc);
};

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);
@ -314,7 +310,7 @@ drop_frame:
LOGP(DRLCMACDL, LOGL_NOTICE, "%s Discarding LLC PDU "
"because lifetime limit reached, "
"count=%u new_queue_size=%zu\n",
tbf_name(this), frames, llc_queue()->size());
tbf_name(this), frames, llc_queue_size());
if (frames > 0xff)
frames = 0xff;
if (octets > 0xffffff)
@ -359,13 +355,12 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
{
int bsn;
int data_len2, force_data_len = -1;
GprsCodingScheme cs2;
GprsCodingScheme force_cs;
bsn = m_window.resend_needed();
if (previous_bsn >= 0) {
force_cs = m_rlc.block(previous_bsn)->cs;
force_cs = m_rlc.block(previous_bsn)->cs_current_trans;
if (!force_cs.isEgprs())
return -1;
force_data_len = m_rlc.block(previous_bsn)->len;
@ -379,7 +374,27 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
m_window.mod_sns(bsn - previous_bsn) > RLC_EGPRS_MAX_BSN_DELTA)
return -1;
cs2 = m_rlc.block(bsn)->cs;
if (is_egprs_enabled()) {
/* Table 8.1.1.2 and Table 8.1.1.1 of 44.060 */
m_rlc.block(bsn)->cs_current_trans =
GprsCodingScheme::get_retx_mcs(
m_rlc.block(bsn)->cs_init,
ms()->current_cs_dl(),
bts->bts_data()->dl_arq_type);
LOGP(DRLCMACDL, LOGL_DEBUG,
"- initial_cs_dl(%d) last_mcs(%d)"
" demanded_mcs(%d) cs_trans(%d)"
" arq_type(%d) bsn(%d)\n",
m_rlc.block(bsn)->cs_init.to_num(),
m_rlc.block(bsn)->cs_last.to_num(),
ms()->current_cs_dl().to_num(),
m_rlc.block(bsn)->cs_current_trans.to_num(),
bts->bts_data()->dl_arq_type, bsn);
} else
m_rlc.block(bsn)->cs_current_trans =
m_rlc.block(bsn)->cs_last;
data_len2 = m_rlc.block(bsn)->len;
if (force_data_len > 0 && force_data_len != data_len2)
return -1;
@ -402,13 +417,14 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
if (restart_bsn_cycle())
return take_next_bsn(fn, previous_bsn, may_combine);
} else if (have_data()) {
GprsCodingScheme new_cs;
/* New blocks may be send */
cs2 = force_cs ? force_cs : current_cs();
new_cs = force_cs ? force_cs : current_cs();
LOGP(DRLCMACDL, LOGL_DEBUG,
"- Sending new block at BSN %d, CS=%s\n",
m_window.v_s(), cs2.name());
m_window.v_s(), new_cs.name());
bsn = create_new_bsn(fn, cs2);
bsn = create_new_bsn(fn, new_cs);
} else if (!m_window.window_empty()) {
LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
"because all blocks have been transmitted (FLOW).\n",
@ -422,7 +438,7 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
"- Sending new dummy block at BSN %d, CS=%s\n",
m_window.v_s(), current_cs().name());
bsn = create_new_bsn(fn, current_cs());
/* Don't send a second block, so don't set cs2 */
/* Don't send a second block, so don't set cs_current_trans */
}
if (bsn < 0) {
@ -430,10 +446,11 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
LOGP(DRLCMACDL, LOGL_DEBUG,
"- Nothing else to send, Re-transmit final block!\n");
bsn = m_window.v_s_mod(-1);
bts->rlc_final_block_resent();
bts->rlc_resent();
}
*may_combine = cs2.numDataBlocks() > 1;
*may_combine = m_rlc.block(bsn)->cs_current_trans.numDataBlocks() > 1;
return bsn;
}
@ -504,7 +521,13 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
/* now we still have untransmitted LLC data, so we fill mac block */
rlc_data = m_rlc.block(bsn);
data = rlc_data->prepare(block_data_len);
rlc_data->cs = cs;
rlc_data->cs_last = cs;
rlc_data->cs_current_trans = cs;
/* Initialise the variable related to DL SPB */
rlc_data->spb_status.block_status_dl = EGPRS_RESEG_DL_DEFAULT;
rlc_data->cs_init = cs;
rlc_data->len = block_data_len;
rdbi = &(rlc_data->block_info);
@ -517,8 +540,11 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
do {
bool is_final;
int payload_written = 0;
if (m_llc.frame_length() == 0) {
/* nothing to sent - delay the release of the TBF */
int space = block_data_len - write_offset;
/* A header will need to by added, so we just need
* space-1 octets */
@ -539,10 +565,13 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
m_llc.frame_length(), frames_since_last_drain(fn));
}
is_final = llc_queue()->size() == 0 && !keep_open(fn);
is_final = llc_queue_size() == 0 && !keep_open(fn);
ar = Encoding::rlc_data_to_dl_append(rdbi, cs,
&m_llc, &write_offset, &num_chunks, data, is_final);
&m_llc, &write_offset, &num_chunks, data, is_final, &payload_written);
if (payload_written > 0)
bts->rlc_dl_payload_bytes(payload_written);
if (ar == Encoding::AR_NEED_MORE_BLOCKS)
break;
@ -550,6 +579,7 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for %s"
"len=%d\n", tbf_name(this), m_llc.frame_length());
gprs_rlcmac_dl_bw(this, m_llc.frame_length());
bts->llc_dl_bytes(m_llc.frame_length());
m_llc.reset();
if (is_final) {
@ -562,7 +592,7 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
} while (ar == Encoding::AR_COMPLETED_SPACE_LEFT);
LOGP(DRLCMACDL, LOGL_DEBUG, "data block (BSN %d, %s): %s\n",
bsn, rlc_data->cs.name(),
bsn, rlc_data->cs_last.name(),
osmo_hexdump(rlc_data->block, block_data_len));
/* raise send state and set ack state array */
m_window.m_v_b.mark_unacked(bsn);
@ -589,8 +619,12 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
GprsCodingScheme cs;
int bsns[ARRAY_SIZE(rlc.block_info)];
unsigned num_bsns;
int punct[ARRAY_SIZE(rlc.block_info)];
enum egprs_puncturing_values punct[ARRAY_SIZE(rlc.block_info)];
bool need_padding = false;
enum egprs_rlcmac_dl_spb spb = EGPRS_RLCMAC_DL_NO_RETX;
unsigned int spb_status = get_egprs_dl_spb_status(index);
memset(punct, EGPRS_PS_INVALID, sizeof(punct));
/*
* TODO: This is an experimental work-around to put 2 BSN into
@ -600,7 +634,8 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
* be put into the data area, even if the resulting CS is higher than
* the current limit.
*/
cs = m_rlc.block(index)->cs;
cs = m_rlc.block(index)->cs_current_trans;
GprsCodingScheme &cs_init = m_rlc.block(index)->cs_init;
bsns[0] = index;
num_bsns = 1;
@ -609,14 +644,29 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
num_bsns += 1;
}
if (num_bsns == 1) {
/* TODO: remove the conditional when MCS-6 padding isn't
* failing to be decoded by MEs anymore */
if (cs != GprsCodingScheme(GprsCodingScheme::MCS8))
cs.decToSingleBlock(&need_padding);
update_coding_scheme_counter_dl(cs);
/*
* if the intial mcs is 8 and retransmission mcs is either 6 or 3
* we have to include the padding of 6 octets in first segment
*/
if ((GprsCodingScheme::Scheme(cs_init) == GprsCodingScheme::MCS8) &&
(GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS6 ||
GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS3)) {
if (spb_status == EGPRS_RESEG_DL_DEFAULT ||
spb_status == EGPRS_RESEG_SECOND_SEG_SENT)
need_padding = true;
} else if (num_bsns == 1) {
cs.decToSingleBlock(&need_padding);
}
gprs_rlc_data_info_init_dl(&rlc, cs, need_padding);
spb = get_egprs_dl_spb(index);
LOGP(DRLCMACDL, LOGL_DEBUG, "- need_padding %d spb_status %d spb %d"
"(BSN1 %d BSN2 %d)\n",
need_padding,
spb_status, spb, index, index2);
gprs_rlc_data_info_init_dl(&rlc, cs, need_padding, spb);
rlc.usf = 7; /* will be set at scheduler */
rlc.pr = 0; /* FIXME: power reduction */
@ -630,14 +680,17 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
msg_data = msgb_put(dl_msg, msg_len);
OSMO_ASSERT(rlc.num_data_blocks <= ARRAY_SIZE(rlc.block_info));
OSMO_ASSERT(rlc.num_data_blocks > 0);
/* Copy block(s) to RLC message */
for (data_block_idx = 0; data_block_idx < rlc.num_data_blocks;
data_block_idx++)
{
int bsn;
GprsCodingScheme cs_enc;
uint8_t *block_data;
gprs_rlc_data_block_info *rdbi, *block_info;
enum egprs_rlc_dl_reseg_bsn_state reseg_status;
/* Check if there are more blocks than BSNs */
if (data_block_idx < num_bsns)
@ -645,25 +698,45 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
else
bsn = bsns[0];
cs_enc = m_rlc.block(bsn)->cs;
/* Get current puncturing scheme from block */
/* get data and header from current block */
block_data = m_rlc.block(bsn)->block;
m_rlc.block(bsn)->next_ps = gprs_get_punct_scheme(
m_rlc.block(bsn)->next_ps,
m_rlc.block(bsn)->cs_last, cs, spb);
/* TODO: Use real puncturing values */
punct[data_block_idx] = data_block_idx;
if (cs.isEgprs()) {
OSMO_ASSERT(m_rlc.block(bsn)->next_ps >= EGPRS_PS_1);
OSMO_ASSERT(m_rlc.block(bsn)->next_ps <= EGPRS_PS_3);
}
punct[data_block_idx] = m_rlc.block(bsn)->next_ps;
rdbi = &rlc.block_info[data_block_idx];
block_info = &m_rlc.block(bsn)->block_info;
if(rdbi->data_len != m_rlc.block(bsn)->len) {
LOGP(DRLCMACDL, LOGL_ERROR,
"ERROR: Expected len = %d for %s instead of "
"%d in data unit %d (BSN %d, %s)\n",
rdbi->data_len, cs.name(), m_rlc.block(bsn)->len,
data_block_idx, bsn, cs_enc.name());
OSMO_ASSERT(rdbi->data_len == m_rlc.block(bsn)->len);
/*
* get data and header from current block
* function returns the reseg status
*/
reseg_status = egprs_dl_get_data(bsn, &block_data);
m_rlc.block(bsn)->spb_status.block_status_dl = reseg_status;
/*
* If it is first segment of the split block set the state of
* bsn to nacked. If it is the first segment dont update the
* next ps value of bsn. since next segment also needs same cps
*/
if (spb == EGPRS_RLCMAC_DL_FIRST_SEG)
m_window.m_v_b.mark_nacked(bsn);
else {
/*
* TODO: Need to handle 2 same bsns
* in header type 1
*/
gprs_update_punct_scheme(&m_rlc.block(bsn)->next_ps,
cs);
}
m_rlc.block(bsn)->cs_last = cs;
rdbi->e = block_info->e;
rdbi->cv = block_info->cv;
rdbi->bsn = bsn;
@ -742,8 +815,6 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
/* Increment TX-counter */
m_tx_counter++;
bts->rlc_sent();
return dl_msg;
}
@ -765,6 +836,11 @@ int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn,
unsigned received_packets = 0, lost_packets = 0;
unsigned num_blocks = strlen(show_rbb);
unsigned distance = m_window.distance();
num_blocks = num_blocks > distance
? distance : num_blocks;
/* SSN - 1 is in range V(A)..V(S)-1 */
for (unsigned int bitpos = 0; bitpos < num_blocks; bitpos++) {
bool is_received;
@ -796,7 +872,7 @@ int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn,
/* Get statistics for current CS */
if (rlc_data->cs != current_cs()) {
if (rlc_data->cs_last != current_cs()) {
/* This block has already been encoded with a different
* CS, so it doesn't help us to decide, whether the
* current CS is ok. Ignore it. */
@ -837,13 +913,15 @@ int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn,
int gprs_rlcmac_dl_tbf::update_window(unsigned first_bsn,
const struct bitvec *rbb)
{
int16_t dist; /* must be signed */
unsigned dist;
uint16_t lost = 0, received = 0;
char show_v_b[RLC_MAX_SNS + 1];
char show_rbb[RLC_MAX_SNS + 1];
int error_rate;
struct ana_result ana_res;
unsigned num_blocks = rbb->cur_bit;
dist = m_window.distance();
unsigned num_blocks = rbb->cur_bit > dist
? dist : rbb->cur_bit;
unsigned behind_last_bsn = m_window.mod_sns(first_bsn + num_blocks);
Decoding::extract_rbb(rbb, show_rbb);
@ -852,31 +930,13 @@ int gprs_rlcmac_dl_tbf::update_window(unsigned first_bsn,
"(BSN=%d) R=ACK I=NACK\n", first_bsn,
show_rbb, m_window.mod_sns(behind_last_bsn - 1));
/* apply received array to receive state (first_bsn..behind_last_bsn-1) */
if (num_blocks > 0) {
/* calculate distance of ssn from V(S) */
dist = m_window.mod_sns(m_window.v_s() - behind_last_bsn);
/* check if distance is less than distance V(A)..V(S) */
if (dist >= m_window.distance()) {
/* this might happpen, if the downlink assignment
* was not received by ms and the ack refers
* to previous TBF
* FIXME: we should implement polling for
* control ack!
* TODO: check whether this FIXME still makes sense
*/
LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of "
"V(A)..V(S) range %s Free TBF!\n", tbf_name(this));
return 1; /* indicate to free TBF */
}
}
error_rate = analyse_errors(show_rbb, behind_last_bsn, &ana_res);
if (bts_data()->cs_adj_enabled && ms())
ms()->update_error_rate(this, error_rate);
m_window.update(bts, rbb, first_bsn, &lost, &received);
rate_ctr_add(&m_ctrs->ctr[TBF_CTR_RLC_NACKED], lost);
/* report lost and received packets */
gprs_rlcmac_received_lost(this, received, lost);
@ -935,6 +995,7 @@ int gprs_rlcmac_dl_tbf::update_window(const uint8_t ssn, const uint8_t *rbb)
m_window.update(bts, show_rbb, ssn,
&lost, &received);
rate_ctr_add(&m_ctrs->ctr[TBF_CTR_RLC_NACKED], lost);
/* report lost and received packets */
gprs_rlcmac_received_lost(this, received, lost);
@ -968,7 +1029,7 @@ int gprs_rlcmac_dl_tbf::maybe_start_new_window()
release();
/* check for LLC PDU in the LLC Queue */
if (llc_queue()->size() > 0)
if (llc_queue_size() > 0)
/* we have more data so we will re-use this tbf */
establish_dl_tbf_on_pacch();
@ -1086,7 +1147,7 @@ bool gprs_rlcmac_dl_tbf::need_control_ts() const
bool gprs_rlcmac_dl_tbf::have_data() const
{
return m_llc.chunk_size() > 0 ||
(llc_queue() && llc_queue()->size() > 0);
(llc_queue_size() > 0);
}
int gprs_rlcmac_dl_tbf::frames_since_last_poll(unsigned fn) const
@ -1125,3 +1186,209 @@ bool gprs_rlcmac_dl_tbf::keep_open(unsigned fn) const
keep_time_frames = msecs_to_frames(bts_data()->dl_tbf_idle_msec);
return frames_since_last_drain(fn) <= keep_time_frames;
}
/*
* This function returns the pointer to data which needs
* to be copied. Also updates the status of the block related to
* Split block handling in the RLC/MAC block.
*/
enum egprs_rlc_dl_reseg_bsn_state
gprs_rlcmac_dl_tbf::egprs_dl_get_data(int bsn, uint8_t **block_data)
{
gprs_rlc_data *rlc_data = m_rlc.block(bsn);
egprs_rlc_dl_reseg_bsn_state *block_status_dl =
&rlc_data->spb_status.block_status_dl;
GprsCodingScheme &cs_current_trans = m_rlc.block(bsn)->cs_current_trans;
GprsCodingScheme &cs_init = m_rlc.block(bsn)->cs_init;
*block_data = &rlc_data->block[0];
/*
* Table 10.3a.0.1 of 44.060
* MCS6,9: second segment starts at 74/2 = 37
* MCS5,7: second segment starts at 56/2 = 28
* MCS8: second segment starts at 31
* MCS4: second segment starts at 44/2 = 22
*/
if (cs_current_trans.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3) {
if (*block_status_dl == EGPRS_RESEG_FIRST_SEG_SENT) {
switch (GprsCodingScheme::Scheme(cs_init)) {
case GprsCodingScheme::MCS6 :
case GprsCodingScheme::MCS9 :
*block_data = &rlc_data->block[37];
break;
case GprsCodingScheme::MCS7 :
case GprsCodingScheme::MCS5 :
*block_data = &rlc_data->block[28];
break;
case GprsCodingScheme::MCS8 :
*block_data = &rlc_data->block[31];
break;
case GprsCodingScheme::MCS4 :
*block_data = &rlc_data->block[22];
break;
default:
LOGP(DRLCMACDL, LOGL_ERROR, "Software error: "
"--%s hit invalid condition. headerType(%d) "
" blockstatus(%d) cs(%s) PLEASE FIX!\n", name(),
cs_current_trans.headerTypeData(),
*block_status_dl, cs_init.name());
break;
}
return EGPRS_RESEG_SECOND_SEG_SENT;
} else if ((cs_init.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1) ||
(cs_init.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2)) {
return EGPRS_RESEG_FIRST_SEG_SENT;
} else if ((GprsCodingScheme::Scheme(cs_init) ==
GprsCodingScheme::MCS4) &&
(GprsCodingScheme::Scheme(cs_current_trans) ==
GprsCodingScheme::MCS1)) {
return EGPRS_RESEG_FIRST_SEG_SENT;
}
}
return EGPRS_RESEG_DL_DEFAULT;
}
/*
* This function returns the status of split block
* for RLC/MAC block.
*/
unsigned int gprs_rlcmac_dl_tbf::get_egprs_dl_spb_status(const int bsn)
{
const gprs_rlc_data *rlc_data = m_rlc.block(bsn);
return rlc_data->spb_status.block_status_dl;
}
/*
* This function returns the spb value to be sent OTA
* for RLC/MAC block.
*/
enum egprs_rlcmac_dl_spb gprs_rlcmac_dl_tbf::get_egprs_dl_spb(const int bsn)
{
struct gprs_rlc_data *rlc_data = m_rlc.block(bsn);
egprs_rlc_dl_reseg_bsn_state block_status_dl =
rlc_data->spb_status.block_status_dl;
GprsCodingScheme &cs_current_trans = m_rlc.block(bsn)->cs_current_trans;
GprsCodingScheme &cs_init = m_rlc.block(bsn)->cs_init;
/* Table 10.4.8b.1 of 44.060 */
if (cs_current_trans.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3) {
/*
* if we are sending the second segment the spb should be 3
* other wise it should be 2
*/
if (block_status_dl == EGPRS_RESEG_FIRST_SEG_SENT) {
/* statistics */
bts->spb_downlink_second_segment();
return EGPRS_RLCMAC_DL_SEC_SEG;
} else if ((cs_init.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1) ||
(cs_init.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2)) {
bts->spb_downlink_first_segment();
return EGPRS_RLCMAC_DL_FIRST_SEG;
} else if ((GprsCodingScheme::Scheme(cs_init) ==
GprsCodingScheme::MCS4) &&
(GprsCodingScheme::Scheme(cs_current_trans) ==
GprsCodingScheme::MCS1)) {
bts->spb_downlink_first_segment();
return EGPRS_RLCMAC_DL_FIRST_SEG;
}
}
/* Non SPB cases 0 is reurned */
return EGPRS_RLCMAC_DL_NO_RETX;
}
void gprs_rlcmac_dl_tbf::egprs_calc_window_size()
{
struct gprs_rlcmac_bts *bts_data = bts->bts_data();
unsigned int num_pdch = pcu_bitcount(dl_slots());
unsigned int ws = bts_data->ws_base + num_pdch * bts_data->ws_pdch;
ws = (ws / 32) * 32;
ws = OSMO_MAX(64, ws);
if (num_pdch == 1)
ws = OSMO_MIN(192, ws);
else
ws = OSMO_MIN(128 * num_pdch, ws);
LOGP(DRLCMAC, LOGL_INFO, "%s: Setting EGPRS window size to %d\n",
name(), ws);
m_window.set_ws(ws);
}
void gprs_rlcmac_dl_tbf::update_coding_scheme_counter_dl(const GprsCodingScheme cs)
{
uint8_t coding_scheme = 0;
coding_scheme = GprsCodingScheme::Scheme(cs);
if (cs.isGprs()) {
switch (coding_scheme) {
case GprsCodingScheme::CS1 :
bts->gprs_dl_cs1();
rate_ctr_inc(&m_dl_gprs_ctrs->ctr[TBF_CTR_GPRS_DL_CS1]);
break;
case GprsCodingScheme::CS2 :
bts->gprs_dl_cs2();
rate_ctr_inc(&m_dl_gprs_ctrs->ctr[TBF_CTR_GPRS_DL_CS2]);
break;
case GprsCodingScheme::CS3 :
bts->gprs_dl_cs3();
rate_ctr_inc(&m_dl_gprs_ctrs->ctr[TBF_CTR_GPRS_DL_CS3]);
break;
case GprsCodingScheme::CS4 :
bts->gprs_dl_cs4();
rate_ctr_inc(&m_dl_gprs_ctrs->ctr[TBF_CTR_GPRS_DL_CS4]);
break;
}
} else {
switch (coding_scheme) {
case GprsCodingScheme::MCS1 :
bts->egprs_dl_mcs1();
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS1]);
break;
case GprsCodingScheme::MCS2 :
bts->egprs_dl_mcs2();
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS2]);
break;
case GprsCodingScheme::MCS3 :
bts->egprs_dl_mcs3();
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS3]);
break;
case GprsCodingScheme::MCS4 :
bts->egprs_dl_mcs4();
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS4]);
break;
case GprsCodingScheme::MCS5 :
bts->egprs_dl_mcs5();
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS5]);
break;
case GprsCodingScheme::MCS6 :
bts->egprs_dl_mcs6();
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS6]);
break;
case GprsCodingScheme::MCS7 :
bts->egprs_dl_mcs7();
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS7]);
break;
case GprsCodingScheme::MCS8 :
bts->egprs_dl_mcs8();
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS8]);
break;
case GprsCodingScheme::MCS9 :
bts->egprs_dl_mcs9();
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS9]);
break;
}
}
}

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)
@ -167,7 +177,7 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
/* Increment RX-counter */
this->m_rx_counter++;
update_coding_scheme_counter_ul(rlc->cs);
/* Loop over num_blocks */
for (block_idx = 0; block_idx < rlc->num_data_blocks; block_idx++) {
int num_chunks;
@ -219,36 +229,28 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
rdbi->bsn, m_window.v_q(),
m_window.mod_sns(m_window.v_q() + ws - 1));
block = m_rlc.block(rdbi->bsn);
block->block_info = *rdbi;
block->cs = rlc->cs;
OSMO_ASSERT(rdbi->data_len < sizeof(block->block));
OSMO_ASSERT(rdbi->data_len <= sizeof(block->block));
rlc_data = &(block->block[0]);
/* TODO: Handle SPB != 0 -> Set length to 2*len, add offset if
* 2nd part. Note that resegmentation is currently disabled
* within the UL assignment.
*/
if (rdbi->spb) {
LOGP(DRLCMACUL, LOGL_NOTICE,
"Got SPB != 0 but resegmentation has been "
"disabled, skipping %s data block with BSN %d, "
"TFI=%d.\n", rlc->cs.name(), rdbi->bsn,
rlc->tfi);
continue;
}
block->len =
Decoding::rlc_copy_to_aligned_buffer(rlc, block_idx, data,
rlc_data);
if (rdbi->spb) {
egprs_rlc_ul_reseg_bsn_state assemble_status;
assemble_status = handle_egprs_ul_spb(rlc,
block, data, block_idx);
if (assemble_status != EGPRS_RESEG_DEFAULT)
return 0;
} else {
block->block_info = *rdbi;
block->cs_last = rlc->cs;
block->len =
Decoding::rlc_copy_to_aligned_buffer(rlc,
block_idx, data, rlc_data);
}
LOGP(DRLCMACUL, LOGL_DEBUG,
"%s: data_length=%d, data=%s\n",
name(), block->len, osmo_hexdump(rlc_data, block->len));
/* TODO: Handle SPB != 0 -> set state to partly received
* (upper/lower) and continue with the loop, unless the other
* part is already present.
*/
/* Get/Handle TLLI */
if (rdbi->ti) {
num_chunks = Decoding::rlc_data_from_ul_data(
@ -389,3 +391,220 @@ int gprs_rlcmac_ul_tbf::snd_ul_ud()
return 0;
}
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_second_seg(
const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
uint8_t *data, const uint8_t block_idx)
{
const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
union split_block_status *spb_status = &block->spb_status;
uint8_t *rlc_data = &block->block[0];
bts->spb_uplink_second_segment();
if (spb_status->block_status_ul &
EGPRS_RESEG_FIRST_SEG_RXD) {
LOGP(DRLCMACUL, LOGL_DEBUG,
"---%s: Second seg is received "
"first seg is already present "
"set the status to complete\n", name());
spb_status->block_status_ul = EGPRS_RESEG_DEFAULT;
block->len += Decoding::rlc_copy_to_aligned_buffer(rlc,
block_idx, data, rlc_data + block->len);
block->block_info.data_len += rdbi->data_len;
} else if (spb_status->block_status_ul == EGPRS_RESEG_DEFAULT) {
LOGP(DRLCMACUL, LOGL_DEBUG,
"---%s: Second seg is received "
"first seg is not received "
"set the status to second seg received\n",
name());
block->len = Decoding::rlc_copy_to_aligned_buffer(rlc,
block_idx, data,
rlc_data + rlc->block_info[block_idx].data_len);
spb_status->block_status_ul = EGPRS_RESEG_SECOND_SEG_RXD;
block->block_info = *rdbi;
}
return spb_status->block_status_ul;
}
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_first_seg(
const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
uint8_t *data, const uint8_t block_idx)
{
const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
uint8_t *rlc_data = &block->block[0];
union split_block_status *spb_status = &block->spb_status;
bts->spb_uplink_first_segment();
if (spb_status->block_status_ul & EGPRS_RESEG_SECOND_SEG_RXD) {
LOGP(DRLCMACUL, LOGL_DEBUG,
"---%s: First seg is received "
"second seg is already present "
"set the status to complete\n", name());
block->len += Decoding::rlc_copy_to_aligned_buffer(rlc,
block_idx, data, rlc_data);
block->block_info.data_len = block->len;
spb_status->block_status_ul = EGPRS_RESEG_DEFAULT;
} else if (spb_status->block_status_ul == EGPRS_RESEG_DEFAULT) {
LOGP(DRLCMACUL, LOGL_DEBUG,
"---%s: First seg is received "
"second seg is not received "
"set the status to first seg "
"received\n", name());
spb_status->block_status_ul = EGPRS_RESEG_FIRST_SEG_RXD;
block->len = Decoding::rlc_copy_to_aligned_buffer(rlc,
block_idx, data, rlc_data);
block->block_info = *rdbi;
}
return spb_status->block_status_ul;
}
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_spb(
const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
uint8_t *data, const uint8_t block_idx)
{
const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
LOGP(DRLCMACUL, LOGL_DEBUG,
"--%s: Got SPB(%d) "
"cs(%s) data block with BSN (%d), "
"TFI(%d).\n", name(), rdbi->spb, rlc->cs.name(), rdbi->bsn,
rlc->tfi);
egprs_rlc_ul_reseg_bsn_state assemble_status = EGPRS_RESEG_INVALID;
/* Section 10.4.8b of 44.060*/
if (rdbi->spb == 2)
assemble_status = handle_egprs_ul_first_seg(rlc,
block, data, block_idx);
else if (rdbi->spb == 3)
assemble_status = handle_egprs_ul_second_seg(rlc,
block, data, block_idx);
else {
LOGP(DRLCMACUL, LOGL_ERROR,
"--%s: spb(%d) Not supported SPB for this EGPRS "
"configuration\n",
name(), rdbi->spb);
}
/*
* When the block is successfully constructed out of segmented blocks
* upgrade the MCS to the type 2
*/
if (assemble_status == EGPRS_RESEG_DEFAULT) {
switch (GprsCodingScheme::Scheme(rlc->cs)) {
case GprsCodingScheme::MCS3 :
block->cs_last = GprsCodingScheme::MCS6;
LOGP(DRLCMACUL, LOGL_DEBUG,
"--%s: Upgrading to MCS6\n", name());
break;
case GprsCodingScheme::MCS2 :
block->cs_last = GprsCodingScheme::MCS5;
LOGP(DRLCMACUL, LOGL_DEBUG,
"--%s: Upgrading to MCS5\n", name());
break;
case GprsCodingScheme::MCS1 :
LOGP(DRLCMACUL, LOGL_DEBUG,
"--%s: Upgrading to MCS4\n", name());
block->cs_last = GprsCodingScheme::MCS4;
break;
default:
LOGP(DRLCMACUL, LOGL_ERROR,
"--%s: cs(%s) Error in Upgrading to higher MCS\n",
name(), rlc->cs.name());
break;
}
}
return assemble_status;
}
void gprs_rlcmac_ul_tbf::update_coding_scheme_counter_ul(const GprsCodingScheme cs)
{
uint8_t coding_scheme = 0;
coding_scheme = GprsCodingScheme::Scheme(cs);
if (cs.isGprs()) {
switch (coding_scheme) {
case GprsCodingScheme::CS1 :
bts->gprs_ul_cs1();
rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS1]);
break;
case GprsCodingScheme::CS2 :
bts->gprs_ul_cs2();
rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS2]);
break;
case GprsCodingScheme::CS3 :
bts->gprs_ul_cs3();
rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS3]);
break;
case GprsCodingScheme::CS4 :
bts->gprs_ul_cs4();
rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS4]);
break;
}
} else {
switch (coding_scheme) {
case GprsCodingScheme::MCS1 :
bts->egprs_ul_mcs1();
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS1]);
break;
case GprsCodingScheme::MCS2 :
bts->egprs_ul_mcs2();
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS2]);
break;
case GprsCodingScheme::MCS3 :
bts->egprs_ul_mcs3();
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS3]);
break;
case GprsCodingScheme::MCS4 :
bts->egprs_ul_mcs4();
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS4]);
break;
case GprsCodingScheme::MCS5 :
bts->egprs_ul_mcs5();
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS5]);
break;
case GprsCodingScheme::MCS6 :
bts->egprs_ul_mcs6();
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS6]);
break;
case GprsCodingScheme::MCS7 :
bts->egprs_ul_mcs7();
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS7]);
break;
case GprsCodingScheme::MCS8 :
bts->egprs_ul_mcs8();
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS8]);
break;
case GprsCodingScheme::MCS9 :
bts->egprs_ul_mcs9();
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS9]);
break;
}
}
}
void gprs_rlcmac_ul_tbf::egprs_calc_ulwindow_size()
{
struct gprs_rlcmac_bts *bts_data = bts->bts_data();
unsigned int num_pdch = pcu_bitcount(ul_slots());
unsigned int ws = bts_data->ws_base + num_pdch * bts_data->ws_pdch;
ws = (ws / 32) * 32;
ws = OSMO_MAX(64, ws);
if (num_pdch == 1)
ws = OSMO_MIN(192, ws);
else
ws = OSMO_MIN(128 * num_pdch, ws);
LOGP(DRLCMAC, LOGL_ERROR, "%s: Setting EGPRS window size to %d, base(%d) slots(%d) ws_pdch(%d)\n",
name(), ws, bts_data->ws_base, num_pdch, bts_data->ws_pdch);
m_window.set_ws(ws);
}

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

@ -792,6 +792,56 @@ static void test_many_connections()
test_many_connections(alloc_algorithm_dynamic, 160, "algorithm dynamic");
}
static void test_2_consecutive_dl_tbfs()
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
uint8_t ms_class = 11;
uint8_t egprs_ms_class = 11;
gprs_rlcmac_tbf *dl_tbf1, *dl_tbf2;
uint8_t numTs1 = 0, numTs2 = 0;
printf("Testing DL TS allocation for Multi UEs\n");
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
trx->pdch[4].enable();
trx->pdch[5].enable();
trx->pdch[6].enable();
trx->pdch[7].enable();
dl_tbf1 = tbf_alloc_dl_tbf(bts, NULL, 0, ms_class, egprs_ms_class, 0);
OSMO_ASSERT(dl_tbf1);
for (int i = 0; i < 8; i++) {
if (dl_tbf1->pdch[i])
numTs1++;
}
OSMO_ASSERT(numTs1 == 4);
printf("TBF1: numTs(%d)\n", numTs1);
dl_tbf2 = tbf_alloc_dl_tbf(bts, NULL, 0, ms_class, egprs_ms_class, 0);
OSMO_ASSERT(dl_tbf2);
for (int i = 0; i < 8; i++) {
if (dl_tbf2->pdch[i])
numTs2++;
}
/*
* TODO: currently 2nd DL TBF gets 3 TS
* This behaviour will be fixed in subsequent patch
*/
printf("TBF2: numTs(%d)\n", numTs2);
OSMO_ASSERT(numTs2 == 3);
tbf_free(dl_tbf1);
tbf_free(dl_tbf2);
}
int main(int argc, char **argv)
{
tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile AllocTest context");
@ -809,6 +859,7 @@ int main(int argc, char **argv)
test_alloc_b();
test_successive_allocation();
test_many_connections();
test_2_consecutive_dl_tbfs();
return EXIT_SUCCESS;
}

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(3)

View File

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

View File

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

View File

@ -0,0 +1,2 @@
=== start test_EPDAN_decode_tree ===
=== end test_EPDAN_decode_tree ===

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,51 @@ Testing MCS-7
Testing MCS-8
Testing MCS-9
=== end test_tbf_egprs_dl ===
=== start test_tbf_egprs_retx_dl ===
Testing retx for MCS 6 - 6
Testing retx for MCS 1 - 9
Testing retx for MCS 2 - 8
Testing retx for MCS 5 - 7
Testing retx for MCS 6 - 9
Testing retx for MCS 7 - 5
Testing retx for MCS 9 - 6
=== end test_tbf_egprs_retx_dl ===
=== start test_tbf_egprs_spb_dl ===
Testing retx for MCS 6 to reseg_mcs 3
Testing retx for MCS 5 to reseg_mcs 2
Testing retx for MCS 4 to reseg_mcs 1
Testing retx for MCS 6 to reseg_mcs 3
=== end test_tbf_egprs_spb_dl ===
=== start test_tbf_puan_urbb_len ===
=== end test_tbf_puan_urbb_len ===
=== start test_tbf_update_ws ===
=== end test_tbf_update_ws ===
=== start test_tbf_li_decoding ===
=== end test_tbf_li_decoding ===
=== start test_tbf_epdan_out_of_rx_window ===
=== end test_tbf_epdan_out_of_rx_window ===
=== start test_immediate_assign_rej_multi_block ===
=== end test_immediate_assign_rej_multi_block ===
=== start test_immediate_assign_rej_single_block ===
=== end test_immediate_assign_rej_single_block ===
=== start test_packet_access_rej_epdan ===
packet reject: 40 84 7f f7 6e e6 41 4b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
=== end test_packet_access_rej_epdan ===
=== start test_tbf_egprs_two_phase_puan ===
=== end test_tbf_egprs_two_phase_puan ===
=== start test_packet_access_rej_prr ===
=== end test_packet_access_rej_prr ===
=== start test_packet_access_rej_prr_no_other_tbfs ===
=== end test_packet_access_rej_prr_no_other_tbfs ===
=== start test_multi_trx_test ===
=== start test_multi_trx_test_same_capacity ===
=== end test_multi_trx_test_same_capacity ===
=== start test_multi_trx_test_1_2_capacity ===
=== end test_multi_trx_test_1_2_capacity ===
=== start test_multi_trx_test_1_4_capacity ===
=== end test_multi_trx_test_1_4_capacity ===
=== start test_multi_trx_test_release_alloc ===
=== end test_multi_trx_test_release_alloc ===
=== start test_multi_trx_test_same_capacity_dl_ul_combined ===
=== end test_multi_trx_test_same_capacity_dl_ul_combined ===
=== end test_multi_trx_test ===

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);
}
{
@ -410,6 +427,42 @@ static void test_rlc_dl_ul_basic()
}
}
void test_immediate_assign_rej()
{
uint8_t plen;
bitvec *immediate_assignment_rej = bitvec_alloc(22);
bitvec_unhex(immediate_assignment_rej,
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
plen = Encoding::write_immediate_assignment_reject(
immediate_assignment_rej, 112, 100,
GSM_L1_BURST_TYPE_ACCESS_1);
printf("assignment reject: %s\n",
osmo_hexdump(immediate_assignment_rej->data, 22));
OSMO_ASSERT(plen == 19);
/* RA value */
OSMO_ASSERT(immediate_assignment_rej->data[3] == 0x7f);
/* Extended RA value */
OSMO_ASSERT(immediate_assignment_rej->data[19] == 0xc0);
bitvec_unhex(immediate_assignment_rej,
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
plen = Encoding::write_immediate_assignment_reject(
immediate_assignment_rej, 112, 100,
GSM_L1_BURST_TYPE_ACCESS_0);
printf("assignment reject: %s\n",
osmo_hexdump(immediate_assignment_rej->data, 22));
OSMO_ASSERT(plen == 19);
/* RA value */
OSMO_ASSERT(immediate_assignment_rej->data[3] == 0x70);
}
int main(int argc, char **argv)
{
osmo_init_logging(&gprs_log_info);
@ -422,6 +475,7 @@ int main(int argc, char **argv)
test_rlc_v_b();
test_rlc_v_n();
test_rlc_dl_ul_basic();
test_immediate_assign_rej();
return EXIT_SUCCESS;
}

View File

@ -6,3 +6,5 @@ rbb: 00 00 00 00 00 00 00 31
rbb: 10 00 00 00 00 00 00 01
show_rbb: RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
show_rbb: IIRRIIIR
assignment reject: 06 3a 10 7f 06 36 14 7f 06 36 14 7f 06 36 14 7f 06 36 14 c0 2b 2b
assignment reject: 06 3a 10 70 06 36 14 70 06 36 14 70 06 36 14 70 06 36 14 0b 2b 2b