The idea is to group protocol extensions and have one message
type (with 4 variations: Req/Ind/Cnf/Err) per group. Similar
to the stack and heap allocation model in Linux, new entries
shall be added to the 'RRCTL_MsgType' list as follows:
- regular messages - from bottom to the end,
- extension groups - from the end towards the bottom
The mask '11xxxx'B gives us 16 unique messages, one is reserved.
RRCTL is a simple protocol (inspired by Osmocom's L1CTL) that allows
an external NAS entity to control the RRC layer of srsUE. The most
notable primitives are PLMN search, selection, and PDU transfer.
The protocol assumes traditional master-slave communication, where
one side (an external NAS entity) initiates various processes,
while the other (srsUE) executes them and indicates the outcome.
Each RRCTL message starts with a header that can be defined as follows:
+-------------------------------+--------------------------+
| Message type | 6 bits |
+-------------------------------+--------------------------+
| Message sub-type | 2 bits |
+-------------------------------+--------------------------+
| Spare (RFU) | 8 bits |
+-------------------------------+--------------------------+
| Payload length | 2 octets (big endian) |
+-------------------------------+--------------------------+
| Payload (optional) | (see payload length) |
+-------------------------------+--------------------------+
The following message types are defined at the moment:
- RRCTL_RESET - reset internal state of the external NAS interface
(does nothing for now, may be useful in the future);
- RRCTL_PLMN_SEARCH - initiates PLMN (carrier) search on pre-configured
EARFCN (Absolute Radio Freqency Number);
- RRCTL_PLMN_SELECT - binds the UE to one of the previously detected
carriers (see RRCTL_PLMN_SEARCH) defined by a
given pair of MCC and MNC;
- RRCTL_CONN_ESTABLISH - establishes connection to the serving cell
(previously selected using RRCTL_PLMN_SELECT)
with a given cause and NAS PDU;
- RRCTL_CONN_RELEASE - releases previously established dedicated connection
(currently does nothing because the RRC layer does
not expose any API for that);
- RRCTL_DATA - encapsulates a received (Downlink) or to be transmitted
(Uplink) NAS PDU (the former also contains LCID).
Each message type has at least two of the following sub-types:
- RRCTL_REQ - request (usually comes from an external NAS entity),
used to initiate some process (e.g. PLMN search);
- RRCTL_IND - indication that something has happened without a prior
request (for example, a Downlink NAS PDU was received);
- RRCTL_CNF - confirmation (positive conslusion) of the requested task;
- RRCTL_ERR - negative conslusion of the requested task (error).
The protocol definition (enums ans structs) and codec functions are
defined in a separate namespaces: 'rrctl::proto' and 'rrctl::codec'
respectively. The codec functions may throw exceptions of type
'rrctl::codec::error' if something goes wrong.
The new configuration section '[extnas]' allows to disable the
built-in NAS implementation, and provide the interface (UNIX
domain socket) to an external entity. The interface itself will
be implemented in the follow up commits.
Unfortunately, the existing networking API (common/network_utils.h)
lacks the UNIX domain socket support, and it turned to be easier
to implement a simple, single client server using Boost.Asio.
The server runs in its own thread with a blocking Tx queue, and
calls a user definted callback on receipt of any data from client.
Multiple client connections are not supported and will be rejected.
This commit introduces a skeleton class (child of 'nas_base') for
the upcoming implementation of an external NAS interface, as well
as the implementation-specific configuration container.
This is the first step towards the goal of having an external NAS
interface. The new 'nas_base' class becomes a parent of 'nas',
combining all interfaces and the basic (common) API.
The 'ue_stack_lte' now holds a unique_ptr of type 'srsue::nas_base',
so the underlying NAS implementation (built-in or external) can
be choosen at run-time depending on configuration.
The implementation specific configuration now needs to be passed
to the constructor instead, not to the init() method as was before.
fix for #1934
This fixes a race condition between Stack thread and DL
PDU processing that lead to updates of the RLC buffer that
are undetected by the BSR routine.
What happens is that in a UL SCH PDU all outstanding data is transmitted
and and a LBSR with all zero buffers is sent.
14:39:47.327301 [MAC ] [D] [ 3793] BSR: LCID=3 old_buffer=59
14:39:47.330600 [MAC ] [I] [ 3793] UL LCID=3 len=58 LBSR: b=0 0 0 0
Note that "old_buffer" isn't set to zero here.
At the same time (same TTI), the MAC PDU processing thread handles DL-SCH PDUs
that may generate new UL PDUs:
14:39:47.330749 [RLC ] [I] DRB1 Tx SDU (54 B, tx_sdu_queue_len=1)
14:39:47.330762 [RLC ] [I] DRB1 Tx SDU (54 B, tx_sdu_queue_len=2)
14:39:47.330775 [RLC ] [I] DRB1 Tx SDU (54 B, tx_sdu_queue_len=3)
..
Those PDUs are "new data" since the previous buffer state was zero.
Here is the race now between the threads, at the end of the bsr::step() function
old_buffer of each LCG is updated with the previous new_buffer, so
the buffer state of LCG=2 is now 59.
Now MAC starts the next TTI:
14:39:47.331910 [MAC ] [D] [ 3794] Running MAC tti=3794
14:39:47.331928 [MAC ] [D] [ 3794] Update Bj: lcid=0, Bj=0
14:39:47.331934 [MAC ] [D] [ 3794] Update Bj: lcid=1, Bj=0
14:39:47.331938 [MAC ] [D] [ 3794] Update Bj: lcid=2, Bj=0
14:39:47.331941 [MAC ] [D] [ 3794] Update Bj: lcid=3, Bj=-1752
14:39:47.331951 [MAC ] [D] [ 3794] BSR: LCID=0 update new buffer=0
14:39:47.331960 [MAC ] [D] [ 3794] BSR: LCID=1 update new buffer=0
14:39:47.331964 [MAC ] [D] [ 3794] BSR: LCID=2 update new buffer=0
14:39:47.331971 [MAC ] [D] [ 3794] BSR: LCID=3 update new buffer=335
14:39:47.331976 [MAC ] [D] [ 3794] BSR: check_new_data() -> get_buffer_state_lcg(0)=0
14:39:47.331980 [MAC ] [D] [ 3794] BSR: check_new_data() -> get_buffer_state_lcg(1)=0
14:39:47.331984 [MAC ] [D] [ 3794] BSR: check_new_data() -> get_buffer_state_lcg(2)=59
14:39:47.331988 [MAC ] [D] [ 3794] BSR: check_new_data() -> get_buffer_state_lcg(3)=0
14:39:47.331993 [MAC ] [D] [ 3794] BSR: LCID=0 old_buffer=0
14:39:47.332000 [MAC ] [D] [ 3794] BSR: LCID=1 old_buffer=0
14:39:47.332003 [MAC ] [D] [ 3794] BSR: LCID=2 old_buffer=0
14:39:47.332007 [MAC ] [D] [ 3794] BSR: LCID=3 old_buffer=335
And since the buffer state of LCG=2 isn't zero, the new data for LCID=3 of that LCG is considered.
So effectivly, the BSR missed the "empty" buffer state for a fraction of time and doesn't
consider the outgoing data generated in the same TTI as new. It therefore
doesn't transmit a BSR.
in which a BSR wasn't
when releasing PUCCH/SRS (see 5.3.13 in 36.331) we need to reset the SR config as well.
In our case, SR is handled by MAC so we need to (re-)configure MAC, not all of
MAC though, just SR.
* Take into account CRS from neigbhour cells when measuring interference
* fix std::isnormal compilation
* Fixed compilation of test
* Address comments
* Remove unused overrides