Compare commits

...

755 Commits

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

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

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

Use the payload size reported by GprsCodingScheme instead.

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

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

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

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

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

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

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

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

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

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

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

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

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

Add the chunk length instead if the TBF is present.

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

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

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

Change the encoder/decoder to use these values.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Give assigment requests for the same direction a lower priority.

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

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

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

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

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

Return a negative value on error.

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

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

Use the bitvec_write_field_lh function instead.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Use the control TS instead.

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

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

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

This commit disables the parsing.

Note that this should be replaced by a config option.

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

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

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

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

Remove the old parse_ra_cap_ms_class function.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  egprs     ->   egprs only

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

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

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

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

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

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

Use the new bitvec based methods instead.

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

Use the new bitvec based methods instead.

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

Use the length of the show_rbb string instead.

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

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

The following methods are affected:

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

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

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

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

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

This commit enables the decoding.

Note that this requires osmo_t4_decode in libosmocore.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Note that it still only supports GRPS.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Change the names to fit both modes and both directions:

  gprs_rlc_ul_header_egprs    -> gprs_rlc_data_info
  gprs_rlc_ul_data_block_info -> gprs_rlc_data_block_info

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

This commit uses RLC_EGPRS_SNS and RLC_EGPRS_MIN_WS for the window
configuration.

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

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

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

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

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

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

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

This commit changes these to uint16_t.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Fixes: Coverity CID 1347433, 1347434, 1347435

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

This commit adds the missing EGPRS variant to the encoder.

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

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

 - egprs              Enables EGPRS
 - no egprs           Disable EGPRS

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Fixes: Coverity CID 1320604, 1320605, 1320606

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

This commit just renames the function.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Sponsored-by: On-Waves ehf
2015-08-13 19:51:50 +02:00
Jacob Erlbeck 6eed1911fd bssgp: Fix leak rate computation CS value
Currently the initial_cs_dl value is used to compute the maximum leak
rate. This can be too low if adaptive CS selection is used.

This commit changes gprs_bssgp_tx_fc_bvc to derive the max CS level
from the configuration.

Sponsored-by: On-Waves ehf
2015-07-21 19:22:36 +02:00
Jacob Erlbeck b31f5ef699 pcu: Enable LLC CoDel by default
Currently CoDel is disabled by default.

This commit enables CoDel on start up with the default interval time,
equivalent to the 'queue codel' VTY command.

To disable CoDel, use the 'no queue codel' command.

Sponsored-by: On-Waves ehf
2015-07-21 19:22:36 +02:00
Jacob Erlbeck d4ad731bae llc: Use CoDel to drop packages from the LLC queue
Currently packets are only dropped if they have reached their maximum
life time. This leads to LLC queues being constantly filled under
load, increasing the latency up to the maximum life time. This kind
of bufferbloat hinders TCP's congestion avoidance algorithms. To keep
the queues short, the CoDel active queue management algorithm can be
used.

This commit changes to llc_dequeue method to apply the CoDel
algorithm to selectively drop LLC frames before they passed to the
TBF layer to be encoded in BSNs. This feature is currently disabled
by default.

The CoDel state is managed per MS since the LLC queues are also kept
in the MS objects.

Note that there is still some buffering in the TBF objects, in the
worst case (CS4) 3.5kByte + LLC-MTU octets are stored there. The
resulting additional packet delay is not (yet) taken into account for
CoDel.

Also note that configuration changes are applied to new MS objects
only.

The following VTY commands are added to the 'pcu' node:

- queue codel           activates CoDel, the interval is selected by
                        the implementation
- queue codel interval <1-1000>
                        activates CoDel with a fixed interval given
                        in centiseconds (10ms-10s)
- no queue codel        deactivates CoDel

Which interval value to use is still an open issue. For high speed
links (e.g. Ethernet), CoDel suggests 100ms. For slower links, the
expected RTT is recommended. The current implementation uses a
default value of 2000ms.

Measurements:

Note that the following measurements depend on several other factors,
most notably the interaction with the SGSN's flow control. They are
just examples to give an idea how CoDel might influence some
parameters.

The measurements have been done with a single E71, first with a
running ping only (Idle), then with an additional TCP download
of a 360k file (Busy). The CoDel interval was set to 1s.

- Idle :
        ping ~400ms, avg queue delay 0ms, dropped 0
- Busy, No CoDel:
        ping ~6s, avg queue delay 4-6s,
        dropped  0, scheduled  948, duration 54s
- Busy, CoDel:
        ping 500-1500ms, avg queue delay ~600ms,
        dropped 77, scheduled 1040, duration 60s

More measurements with two MS downloading in parallel (two
independant measurements per case).

- Busy, No CoDel:
        dropped  0, scheduled 1883, duration 121s
        dropped 19, scheduled 2003, duration 133s
- Busy, CoDel:
        dropped 22, scheduled 1926, duration 116s
        dropped 22, scheduled 1955, duration 108s

Sponsored-by: On-Waves ehf
2015-07-21 19:22:36 +02:00
Jacob Erlbeck 4f666bc113 llc: Add CoDel AQM implementation
This commit adds an implementation of the CoDel algorithm based on
the reference pseudocode presented in
http://queue.acm.org/appendices/codel.html. Instead of abstracting
the queue itself, the implementation provides a time stamp based
automaton which is invoked after a package has been dequeued.

Note that the modifications of the algorithm shown in
https://tools.ietf.org/html/draft-ietf-aqm-codel-01 are not yet
applied.

Sponsored-by: On-Waves ehf
2015-07-21 19:22:32 +02:00
Jacob Erlbeck 7f79f0d332 bssgp: Adapt flowcontrol MS default to current alloc algorithm
Currently the values Bmax/R default MS are computed under the
assumption than min(4, N_PDCH) DL slots are allocated for an MS, even
if multislot assignment is not enabled.

This commit changes the computation to assume 1 DL slot if algorithm
A is selected or the dynamic algorithm is used and has disabled
multislot assigment due to high load.

Sponsored-by: On-Waves ehf
2015-07-17 12:06:19 +02:00
Jacob Erlbeck 77da35515c alloc: Make alloc_algorithm_dynamic stateful
Currently there is no persistent state being used in
alloc_algorithm_dynamic. So algorithm B is even used in persistent
high usage scenarios. If there are many active TBFs, multislot
assigments are not fair, because MS of a "higher" multislot class get
higher troughputs. On the other hand, as long as all PDCH are busy no
bandwidth will be wasted even if all MS use algorithm A.

This commit modifies alloc_algorithm_dynamic to disable algorithm B
when that call fails. It then keeps it disabled until there is a
single PDCH which is idle (it is considered idle, if there is at most
one active DL TBF assigned to it).

Sponsored-by: On-Waves ehf
2015-07-16 19:24:16 +02:00
Jacob Erlbeck 7b3675bf7a alloc/test: Fix trx_no assertion
Currently the value of trx_no2 is used in the assertion, even if the
call to tfi_find_free has failed.

This commit fixes the asserted expression to only compare the trx_no
values if the function call has succeeded.

Sponsored-by: On-Waves ehf
2015-07-16 19:24:16 +02:00
Jacob Erlbeck 0f352a6f22 alloc/test: Free the TBF if the recursion has failed
Currently if both an uplink and a downlink TBF are to be allocated by
alloc_tbfs() and the second allocation fails, the first TBF is not
freed.

This commit changes the recursive function to free the TBF if the ms
variable has been changed to NULL.

Sponsored-by: On-Waves ehf
2015-07-16 19:24:16 +02:00
Jacob Erlbeck bf9042203d alloc/test: Use lower case for slots with TFI shortage
Indicate those slots with lower case letters that do not have a spare
TFI for the other direction if such a TBF has not been attached to
the MS object yet.

Sponsored-by: On-Waves ehf
2015-07-16 19:24:16 +02:00
Jacob Erlbeck 7af53e61f0 alloc: Use a separate usage computation for algo A
Currently algorithm A can select an TBF even when there is no free
TBF in the reverse direction. While this does not necessarily lead to
an allocation failure, the probabily is higher. In addition, the
current slot reservations are not taken into account.

This commit changes the selection algorithm to prefer slots where TFI
are available in both directions and which are less reserved.

Sponsored-by: On-Waves ehf
2015-07-16 19:24:16 +02:00
Jacob Erlbeck 88fb6136fb alloc/test: Show expectation before failure
To simplify debugging, show the actuals value before the assertion
fails in some cases.

Sponsored-by: On-Waves ehf
2015-07-16 19:24:16 +02:00
Jacob Erlbeck e21b79cb21 alloc: Change tx_window optimization strategy
Currently each tx_window combination is checked only once by using a
set containing the sets of TX slots that have been checked already.
This approach does not ensure, that num_tx and ul_ts really match the
tx_window being tested. This does not make a difference with the
current test cases probably because num_tx starts with 1 and is
increased each iteration. Since the bitmap optimization is equivalent
to a cache optimization strategy that only uses tx_window as key. On
the other hand, ul_ts, num_tx, and rx_mask cannot be derived from
tx_window, but these values are also refered to after the call to
test_and_set_bit(). This makes it difficult to prove that correctness
of the caching. While this will not lead to a defect, the results
might be less optimal.

This commit changes the optimization strategy to skip all tx_window
where ul_ts and ul_ts+num_tx-1 are not both contained.  This provides
a similar degree of optimization like the set approach (only the
iteration with num_ts == 8 is not optimized, which only applies to to
ms class 18 and 29 MS) but ensures that the values of the related
variables have a clear relationship.

Note that the bitset based optimization for rx_window does not suffer
from a possible cache inconsistency, since only tx_window and
rx_window (tx_slot_count and rx_slot_count can be derived from the
windows and thus are covered by the cache key) are used after the call
to test_and_set_bit(). tx_window is constant over the whole lifetime
of the cache.

Sponsored-by: On-Waves ehf
2015-07-16 19:24:16 +02:00
Jacob Erlbeck f16a069fd7 pcu: Use alloc_algorithm_dynamic by default
The dynamic algorithm behaves like B until there are no TFI left.

This commit changes the default algorithm to to former.

Ticket: #1934
Sponsored-by: On-Waves ehf
2015-07-16 19:24:16 +02:00
Jacob Erlbeck 5979fe9d8a alloc: Add counters for successful algo A/B allocations
This adds counters for algorithm A and B with count successful
allocation combined for UL and DL.

Ticket: #1934
Sponsored-by: On-Waves ehf
2015-07-16 19:24:16 +02:00
Jacob Erlbeck 400ec02e8a alloc: Add 'dynamic' allocation algorithm
The idea behind this meta algorithm is to automatically select one of
the other algorithms based on the system state. Basically algorithm B
will be selected if the PDCH usage is low to improve throughput and
latency. Algorithm A will be selected to support more concurrent MS.

This commit adds a first simple state-less version of this algorithm
that always tries B first and only if that fails A is tried
afterwards.

The following VTY command is added to the 'pcu' node:

 - alloc-algorithm dynamic

Ticket: #1934
Sponsored-by: On-Waves ehf
2015-07-16 19:24:15 +02:00
Jacob Erlbeck 40da3e17e5 alloc: Remove disabled code fragment for multi-UL allocation
This part of algorithm_b has already been disabled. Further work may
depend on this, but it is going out of sync. So this commit removes
it completely.

Sponsored-by: On-Waves ehf
2015-07-16 19:24:15 +02:00
Jacob Erlbeck 5a2b8be3f5 alloc: Refactor alloc algorithms to only apply changes on success
Currently these algorithms modify other objects (MS, TBF, PDCH) even
if the allocation will fail later on. To implement an algorithm that
dynamically tries another algorithm on failure (e.g. A after B), the
first (failing) algorithm should not change or damage anything.

This commit refactors algorithm A and B to delay the actual allocation
until it is known that the allocation will not fail.

Ticket: #1934
Sponsored-by: On-Waves ehf
2015-07-16 19:24:13 +02:00
Jacob Erlbeck 2b558857dd alloc: Remove redundant first_common_ts handling
Currently this code path is only used, if an allocation has been
taken place in a former call to an allocation algorithm function.
If this was for an DL TBF, the first common TS was selected,
otherwise the least used common TS was selected for an UL TBF.
The shrinking of the UL set (to 1<<first_common_ts) is done in the
latter case.

This commit removes an additional code path that aligns the UL set to
first_common_ts, because it has no more influence on the set of
common TS after both UL and DL TBF have been allocated.

Sponsored-by: On-Waves ehf
2015-07-16 18:51:40 +02:00
Jacob Erlbeck a8c2aaf6f0 alloc/test: Add test for interleaved TBF chains
MS iniated TCP connections generally result in a sequence
of short time UL and longer lasting DL TBFs, being interleaved
between several MS. This scenario is not covered by the existing
tests.

This commit adds a test, that allocates as man as possible TBFs
several times with different test modes without clearing the BTS (and
thus the TBF list) in between. The number of allocated DL TBFs in
each round is expected to be constant.

Sponsored-by: On-Waves ehf
2015-07-16 18:51:31 +02:00
Jacob Erlbeck 69c9bfa089 alloc/test: Put TBF allocation loop into alloc_many_tbfs
Currently all TBFs are deleted after the allocation loop finishes.
This make it difficult to interleave the TBF allocation like it
happens with real MS.

This commit refactors the allocation loop into alloc_many_tbfs and
adds support for TLLIs, which are derived from the counter value and
used to retrieve an old MS object if alloc_many_tbfs is called a
second time.

Note that this does not make a difference for the existing tests.

Sponsored-by: On-Waves ehf
2015-07-16 10:25:14 +02:00
Jacob Erlbeck b2439bbb8a ms: Add is_idle() method to GprsMs::Guard
Currently there is no simple way to determine, whether the MS object
protected by a guard will continue to exist after the guard object is
destroyed.

This patch adds a is_idle() method that will return true if the MS
object is just kept by the guard from being idle. In that case, the
MS object would either be deleted or return true for
GprsMs::is_idle() after the guard's destruction, provided that no
TBF attachment took place in between.

Sponsored-by: On-Waves ehf
2015-07-16 10:25:14 +02:00
Jacob Erlbeck 3a10dbd564 tbf: Put the TFI->TBF mapping into the PDCH objects
Currently the TBFs are registered in a TFI indexed array within the TRX
objects. TBFs can be searched globally by TFI and TRX number. This
conflicts with the use of the same TFI for different TBF on different
PDCH. This use case requires the specification of the PDCH as
additional search dimension.

This commit moves the TFI index TBF arrays into the PDCH objects. The
related methods are updated accordingly.

Ticket: #1793
Sponsored-by: On-Waves ehf
2015-07-16 10:25:14 +02:00
Jacob Erlbeck e0853cdf42 alloc: Allocate TFI per slot (algorithm A)
Currently the TFI are managed per TRX, thus only a maximum of 32 TBF
per direction and per TRX are possible simultaneously.

This commit modifies algorithm_a() to allow the sharing of TFI
between different PDCH. Since algorithm A only assigns a single slot
to each TBF, the TFI of each PDCH can be assigned independently.
This increases the maximum to 32 TBF per direction and per PDCH
concerning the TFI allocation.

Ticket: #1793
Sponsored-by: On-Waves ehf
2015-07-16 10:25:14 +02:00
Jacob Erlbeck 5879c6493f tbf: Move TFI selection into alloc_algorithm
Currently the TFI and the TRX have to be determined before the actual TBF
allocation function is called, passing TFI and TRX number as
parameters. This does fit to TFI reuse for different slots, since
this were tightly coupled with the slot selection.

This commit just moves the TFI selection into the alloc_algorithm
functions. The tfi parameter is removed from the the TFI alloc
functions. The trx parameter is changed into use_trx to optionally
limit the trx selection (same semantics like in tfi_find_free).

Sponsored-by: On-Waves ehf
2015-07-16 10:25:14 +02:00
Jacob Erlbeck 47a57f6f86 pdch: Manage TFIs per direction
Currently a single bit set is used to maintain a set of used TFI
without distinguishing between uplink and downlink. Since the
namespaces of UL and DL TFI are separate, this implementation is
not correct.

This commit changes gprs_rlcmac_pdch to use a separate bit set for
each direction. It also replace the corresponding conditional fprintf
statement in check_tfi_usage (AllocTest.cpp) by an equivalent
OSMO_ASSERT.

Sponsored-by: On-Waves ehf
2015-07-16 10:25:14 +02:00
Jacob Erlbeck 61205a7e65 alloc/test: Check for TFI conflicts
This commit adds the check_tfi_usage function that checks the TFI
usage. It iterates through all TBFs, records on which PDCH it uses
which TFI and check for conflicts. It also checks the bits returned
by pdch->assigned_tfi(). The latter suffers from an bug in that
method (no separation of uplink and downlink), so a conditional
fprintf is used instead of an assertion. The method tfi_find_free
is checked for conflicts after allocations.

Sponsored-by: On-Waves ehf
2015-07-16 10:25:14 +02:00
Jacob Erlbeck 57cf69a18c alloc: Fix MS_B/MS_C interpretation
Currently the handling of MS_B and MS_C is not compliant with TS
45.002, annex B.1. These values may only interpreted as 0, if
frequency hopping is not enabled and if there is no change from Rx to
Tx or vice-versa.

This commit sets Ttb/Trb to 1 if the table entry is MS_B/MS_C, since
only combined down/up access modes are supported.

Sponsored-by: On-Waves ehf
2015-07-16 10:24:37 +02:00
Jacob Erlbeck dd08ac86e6 alloc: Do not use masking for multislot class type 2 MS
Currently the masks are computed equally for each class type. This
does not make much sense for class type 2 MS, since those are capable
to work in full duplex mode.

This commit sets the masks to 0xff for class type 2 MS.

Sponsored-by: On-Waves ehf
2015-07-16 10:24:37 +02:00
Jacob Erlbeck bae33a7001 alloc: Select applicable Tta/Tra
According to TS 45.002, 6.4.2.2 the choice whether Tta or Tra has to
be applied, depends on the medium access mode (currently always
dynamic) and the number of UL/DL slots. Currently either value can be
used which might result in combinations not covered by the spec.

This commit changes find_multi_slots() to skip non-compliant
combinations.

Note that this code will have to be extended, if other medium
access modes are implemented.

Sponsored-by: On-Waves ehf
2015-07-16 10:24:37 +02:00
Jacob Erlbeck 5e46a20e03 alloc: Use an enum instead of numbers to select the mask
The local enums MASK_TT and MASK_TR replace the hard coded indices.
The variable m_idx is renamed to mask_sel for more clarity.

Sponsored-by: On-Waves ehf
2015-07-16 10:24:37 +02:00
Jacob Erlbeck c135b878cd alloc: Merge find_least_busy_pdch and find_least_reserved_pdch
Both functions only differ in the computation of the value for
num_tbfs.

This commit merge both functions and adds a parameter containing a
function for that compuation.

Sponsored-by: On-Waves ehf
2015-07-16 10:24:37 +02:00
Jacob Erlbeck 1139ec1d0f sba: Fix loop exit in SBAController::alloc (Coverity)
The commit 506f156f7a has reverted the
TS search order. The outer loop exit condition was not updated
accordingly. This bug would would only lead to an error if there were
multiple TRX where the first TRX has not got any PDCH assigned.

This commit corrects the break condition.

Fixes: Coverity CID 1311776
Sponsored-by: On-Waves ehf
2015-07-15 14:17:19 +02:00
Jacob Erlbeck 3db617f14a llc: Fix comparison warning
Fixes: Jenkins build #609 warning

Addresses:
  llc.cpp:56, GNU C Compiler 3 (gcc), Priority: Normal
  comparison between signed and unsigned integer expressions

Sponsored-by: On-Waves ehf
2015-07-14 08:51:55 +02:00
Jacob Erlbeck efe62a7395 alloc: Use least reserved PDCH for algo A
Currently the slot selection of algorithm A is based on the current
slot usage by active TBF. Especially in the Dl after UL case which
reflects the commen use case "MS initiates TCP connection", the
resulting distribution is not optimal with respect to PDCH usage.

This commit changes the implementation to use the slot reservation
information instead.

Sponsored-by: On-Waves ehf
2015-07-07 11:52:28 +02:00
Jacob Erlbeck 14376a73a5 alloc/test: Delete first TBF after the second is allocated
Currently when using the test modes TEST_MODE_DL_AFTER_UL or
TEST_MODE_UL_AFTER_DL, the first TBF is deleted before the second is
allocated. The far more interesting case were to keep the first TBF a
little bit longer until the second TBF has been created and delete
then. This comes closer the the situation observed with real MS,
where the first TBF takes some time (timeout or waiting for Ack)
before it gets deleted and thus detached from the MS object.

This commit delays the call to tbf_free accordingly.

The effect can be observed in the results of the algo A tests, where
the uniform distribution of the allocated PDCH is lost.

Sponsored-by: On-Waves ehf
2015-07-07 11:51:07 +02:00
Jacob Erlbeck 506f156f7a sba: Reverse TS search order
Currently the search for an enabled PDCH slot for SBA start with the
first TS. If there are more than 2 PDCH slots enabled, this slot will
conflict with an existing multislot reservation for most multislot
classes. This were less likely if the search were reversed and
started with the last slot due to the 3 slot shift between Tx and Rx.

When multislot allocation is enabled and several MS are connected,
and increased rate of poll timeouts can be observed.

This commit tries to reduce the number of poll timeouts by reverting
the slot search order for SBA allocation.

Sponsored-by: On-Waves ehf
2015-07-07 10:41:44 +02:00
Jacob Erlbeck 1653f837e3 alloc: Disable inner loop debugging by default
The current logging statements within the inner loop of
find_multi_slots drain quite a lot of CPU resources even if
LOGL_DEBUG is not enabled. This might cause issues on the target
hardware.

This commit disables these LOGP calls unless the
ENABLE_TS_ALLOC_DEBUG macro has been set explicitly. This results in
a reduction in the CPU usage reported by callgrind for
find_multi_slots from 42% to 25% when executing AllocTest.

Sponsored-by: On-Waves ehf
2015-07-07 10:35:28 +02:00
Jacob Erlbeck 20b7ba7501 alloc: Optimize find_free_usf
According to callgrind, this function consumes 33% CPU when running
the AllocTest program.

This commit uses the assigned_usf() method to get the USFs allocated
by a PDCH instead of traversing the TBFs.

Sponsored-by: On-Waves ehf
2015-07-07 10:35:28 +02:00
Jacob Erlbeck cc9358f95a tbf: Keep a set of used TFI and USF per PDCH
Currently is is rather expensive to get TFI and USF usage per PDCH,
because the TBFs need to be scanned to get that information.

This commit adds corresponding bit sets which get updated by the
attach_tbf/detach_tbf methods of the gprs_rlcmac_pdch class.

Sponsored-by: On-Waves ehf
2015-07-07 10:35:28 +02:00
Jacob Erlbeck 16c6ecc365 alloc: Skip common TS without free USF when rating
Currently the search of the "best" slot combination is done
separately from the UL slot selection, which can lead to an
allocation failure due to USF exhaustion even if another combination
had been possible.

This commit reduces the probability for this event by skipping UL
slots without free USF while calculation the capacity.

Note that the implementation is rather inefficient which will be
fixed by the following commits.

Sponsored-by: On-Waves ehf
2015-07-07 10:29:12 +02:00
Jacob Erlbeck 5f494b8415 alloc: Only reserve 1 UL slot with algorithm B
Since currently the algorithm B will only allocate a single UL slot
and will have to stick to it (first common TS), the other possible UL
slots will not be allocated while the reservation is kept.

This commit adds code to update the reserved set of UL slots to only
reserve the single common TS when the UL TBF is allocated.

Interestingly this leads to fewer allocated TBF in some cases due to
USF exhaustion. This will be improved by the following commit "alloc:
Skip common TS without free USF".

Sponsored-by: On-Waves ehf
2015-07-07 10:22:18 +02:00
Jacob Erlbeck 9ae282372c alloc: Set minimum slot capacity to 1
Currently the capacity of a PDCH slot is calculated as 32 - N_reserved
for each direction. This can result in a capacity of 0 and even
negative values.

This commit forces the capacity of an usable slot to be at least zero
under the assumption, that an overly reserved PDCH is still better
than none.

Sponsored-by: On-Waves ehf
2015-07-07 10:03:29 +02:00
Jacob Erlbeck ed46afda6f alloc: Only use common UL slots when calculating the capacity
Currently al possible UL slots are included in the capacity
calculation which is the base of the slot selection. Nevertheless
UL-only slots will never be used, since only one uplink slot (which
must be a common slot) will be used.

This patch changes the code to only include common slots in the
capacity sum.

Note that this might not be optimal if algorithm B supported
multiple uplink slots.

Sponsored-by: On-Waves ehf
2015-07-07 09:35:26 +02:00
Jacob Erlbeck ea65c72d06 alloc: Replace Algorithm B implementation
The current implementation always starts the downlink slot allocation
with the first possible slot, depending on which channels are enabled
and which multislot class is offered by the MS. So in configurations
with many (>4) PDCH, some PDCH are not really used.

The new implementation introduced by this commit differs as follows:

 - The reservation mechanism provided by GprsMs is used to avoid
   incompatibilities is used in the same way like algo A does. This
   basically means, that the allocation is done once when the first
   TBF is requested and then used until all TBF have been released.

 - All combinations of Rx and Tx slots are checked for compatibility
   with the multiscot class. Basically the combination with the most
   usable PDCH and the least number of reservations is used.

 - Only one UL slots is provided.

 - Tta and Tra are checked.

Sponsored-by: On-Waves ehf
2015-07-07 09:35:26 +02:00
Jacob Erlbeck c91c18e6ef tbf: Add Poll Timeout counters
This commits adds three poll timeout counters

  - RLC Assign Timeout
  - RLC Ack Timeout
  - RLC Release Timeout

to help diagnosing to cause for these events. There seems to be an
increased rate of these when a PDCH is shared by multiple TBFs.

Sponsored-by: On-Waves ehf
2015-07-07 09:33:29 +02:00
Jacob Erlbeck e0c734dcfe Revert "tbf: Add GprsMs* argument to update() and use it in reuse_tbf"
This reverts commit 2272a83a13b57ea7e99fe96ac76e4ad892e19e90.

The modification is no longer needed, since the call to update has
been removed from reuse_tbf().

Conflicts:
	src/tbf_dl.cpp

Sponsored-by: On-Waves ehf
2015-07-03 15:58:45 +02:00
Jacob Erlbeck 07111668d4 tbf: Remove call to update() in reuse_tbf
Since both TBF are based on the same reservation which means that
they should be compatible with respect to the slot usage, and since
the new TBF has not been forced to single slot usage, an update of
the allocation is not necessary now.

This commit removes the call to update() from within reuse_tbf().

Sponsored-by: On-Waves ehf
2015-07-03 15:58:43 +02:00
Jacob Erlbeck 4944c195d4 tbf: Set ms in call to tbf_alloc_dl_tbf
The call to tbf_alloc_dl_tbf misses the pointer to the GprsMs object
which is already known in that case (tbf_reuse). This leads to a full
reallocation of the PDCH slots, which is possibly incompatible with
the old set of slots. This can result in hanging TCP connections and
TCP connection failures.

This commit replaces the old NULL value by the actual GprsMs object.
Since the set_ms() is also done within the tbf_alloc_dl_tbf method,
that call is removed.

Sponsored-by: On-Waves ehf
2015-07-03 15:53:15 +02:00
Jacob Erlbeck 5cd496d208 alloc: Base algo A on reserved PDCHs
Currently algorithm A bases its time slots selection on the number of
TBF actively using the PDCHs. This statistically prefers the first
time slots, especially with short living TBFs. So when the first TBF
is triggered by an uplink transfer (which generally results in a
short-lived TBF) the potentially longer living DL TBF will be bound
to the same slot. When another MS then requests an uplink TBF, it
will get the same slot (no UL TBF currently active).

This commit changes the algorithm to base its selection on reserved
slots instead.

Sponsored-by: On-Waves ehf
2015-07-03 15:37:16 +02:00
Jacob Erlbeck 83426b20a3 alloc: Ignore slots with differing TSC if multiple slots are requested
According to TS 45.002, 6.4.2 the training sequence (TSC) must be the
same for all slots in a multi-slot set.

This commit updates find_possible_pdchs() to only consider slots with
the same TSC if more that 1 slot shall be assigned.

Note that the first PDCH's TSC will be used as reference, so if two
or more groups with a common TSC are configured, only the first will
be used. This restriction does not apply to algorithm A, since it
will not assign more than one slot and therefore sets the max_slots
parameter to 1.

Sponsored-by: On-Waves ehf
2015-07-03 15:37:16 +02:00
Jacob Erlbeck 617c7127f4 ms: Get the set of slots currently active
This commits adds methods to GprsMs and gprs_rlcmac_tbf to retrieve
the slots that are actively used.

Sponsored-by: On-Waves ehf
2015-07-03 15:37:16 +02:00
Jacob Erlbeck 23f93a15ca ms: Add support for slot reservation
In contrast to the slots currently used by existing TBFs, the
reserved slots refer to the time slots that can be used for newly
allocated TBFs without causing conflicts (given that the first common
TS does not change). They correspond to the potential use of the
PDCHs and can be used to achieve a more uniform slot allocation.

This commit adds bit set based methods to GprsMs and gprs_rlcmac_trx
and a counter to gprs_rlcmac_pdch. The current TRX will also be
stored in the MS object.

Sponsored-by: On-Waves ehf
2015-07-03 15:37:16 +02:00
Jacob Erlbeck ec478756cc alloc: Load balancing for algo A
Currently only the first enabled PDCH will be used. Beside the
throughput this will also limit the number of TBFs:

  - number of UL TBFs <= 7
  - number of DL TBFs <= 32

This commit changes the allocation algorithm to use the PDCH with the
least number of attached TBFs. This will improve the troughput in
both directions and the UL limits:

  - number of UL TBFs <= min(32, N_PDCH * 7) UL TBFs

Ticket: #1794
Sponsored-by: On-Waves ehf
2015-07-03 15:37:15 +02:00
Jacob Erlbeck 9380f5d218 tbf: Add GprsMs* argument to update() and use it in reuse_tbf
Since set_ms() is caled on the new DL TBF, the old DL TBF loses the
reference to the MS object. This will lead to a segfault, when
update() is called in reuse_tbf().

This commit adds an optional GprsMs* parameter to update() and uses it
for the slot allocation.

This fixes a TbfTest crash that would otherwise occur after applying
the next commit.

Sponsored-by: On-Waves ehf
2015-07-03 15:30:26 +02:00
Jacob Erlbeck ac89a555fa ms: Add tbf() method to get the TBF based on the direction
To avoid the need for a switch or conditional statement when needing
a TBF from an MS object in direction independant code, this method
contains that case distinction. For additional flexibility, a
reverse() function is added to get the opposite direction.

Sponsored-by: On-Waves ehf
2015-07-03 13:17:22 +02:00
Jacob Erlbeck 699b8dca49 ms: Add first_common_ts method to GprsMs
This method gets the index (0 based) of first common time slot used
by the TBFs attach to it. It expects that all of them have the same
notion of this. If no TBF is attached, -1 will be returned.

Sponsored-by: On-Waves ehf
2015-07-03 13:17:22 +02:00
Jacob Erlbeck ccc34e4d30 tbf: Maintain the number of TBF per PDCH
Currently the PDCH object do not know anything about the TBFs using
them. To make the slot allocation load dependant, at least some
numbers are required.

This commit adds TBF counters (one per direction) and the related methods
attach_tbf, detach_tbf, and num_tbfs to gprs_rlcmac_pdch.

Sponsored-by: On-Waves ehf
2015-07-03 13:17:22 +02:00
Jacob Erlbeck c6d4ceeda6 tbf/test: Add assertions
These assertions check for the TBF allocation result before the tbf
object is dereferenced the first time.

Sponsored-by: On-Waves ehf
2015-07-03 12:05:51 +02:00
Jacob Erlbeck 9ec49e2411 alloc/test: Use LOGL_DEBUG environment variable
When this environment variable is set, the logging level is set to
LOGL_DEBUG to help debugging without putting everything into
AllocTest.err.

Sponsored-by: On-Waves ehf
2015-07-03 12:05:51 +02:00
Jacob Erlbeck fa464bbce9 alloc/test: Enhance test_alloc_a
This commit adds:
 - an assertion to check that count is valid,
 - an assertion the verify that tbf_alloc did not fail
 - a slots parameter to specify the enabled slots
 - additional test invocations with more slots enabled

Sponsored-by: On-Waves ehf
2015-07-03 12:05:51 +02:00
Jacob Erlbeck e565564bc9 alloc/test: Add test for successive allocation
This test allocates as many as possible UL/DL TBF pairs, shows their
PDCH usage, and checks how many of them has been created
successfully.

Sponsored-by: On-Waves ehf
2015-07-03 12:05:46 +02:00
Jacob Erlbeck cb1b494c89 tbf: Add BTS::ms_alloc method
Currently the code that creates the MS objects with tbf.cpp is
duplicated.

This commit moves the corresponding code into a new method. Since
there is no TLLI available there, the GprsMsStorage::create_ms method
has been refactored into two variants: one with TLLI/direction and
one without.

Sponsored-by: On-Waves ehf
2015-06-29 11:43:17 +02:00
Jacob Erlbeck 6d86628e5b tbf: Always create an MS object on TBF allocation
Currently the MS object are created when the TLLI gets known.
Therefore some information (TA, MS class) must be stored in the TBF
itself and is copied to the MS object later on. This would get even
more complex, if the allocation algorithms were extended based on
this scheme.

This commit ensures, that an MS object will always be created on TBF
allocation, even if the TLLI is not yet known. These 'anonymous'
objects are still managed by the MS storage. To avoid dangling
entries without a TLLI there (which cannnot be retrieved anyway), the
timer in the MS objects is not started after all TBF have been
detached, so that they get deleted immediately in that case.

Note that an MS object can still be removed (e.g. by replacement)
from an existing TBF, so tbf->ms() can be NULL.

Ticket: #1794
Sponsored-by: On-Waves ehf
2015-06-29 10:53:32 +02:00
Jacob Erlbeck e2e004e7a9 tbf: Pass the MS object around instead of old_tbf
Currently the old TBF (either uplink or downlink) is passed around at
TBF allocation mainly to get information about the MS. To implement
more complex allocation algorithms, the MS object itself will be
needed anyway.

This commit replaces the old_tbf arguments by MS object arguments.

Sponsored-by: On-Waves ehf
2015-06-29 10:53:32 +02:00
Jacob Erlbeck ace7b570a0 tbf: Remove update_tlli method
This method does not do anything anymore, it's functionality has been
taken over by update_ms.

This commit removes gprs_rlcmac_tbf::update_tlli completely.

Sponsored-by: On-Waves ehf
2015-06-29 10:53:32 +02:00
Jacob Erlbeck f1379346f7 vty: Fix documentation for 'no cs downgrade-threshold'
The NO_STR is missing, this commit adds it.

Fixes: Jenkins #603
Sponsored-by: On-Waves ehf
2015-06-29 10:49:30 +02:00
Jacob Erlbeck 34cf156b80 llc/test: Use a portable way to set timeval variables
Using complex initialiser lists doesn't seem to work well with Debian
Squeeze.

This commit changes the initialisation to use separate assignments
instead.

Fixes: Jenkins #601, #602
Addresses:
  CXX    LlcTest.o
  ../../tests/llc/LlcTest.cpp: In function 'void test_llc_meta()':
  ../../tests/llc/LlcTest.cpp:137: error: expected primary-expression
  before '.' token
  ../../tests/llc/LlcTest.cpp:137: warning: extended initializer
  lists only available with -std=c++0x or -std=gnu++0x

Sponsored-by: On-Waves ehf
2015-06-29 10:28:36 +02:00
Jacob Erlbeck 9cc783a87d Revert "llc/test: Explicitly enable extended initialiser lists"
This reverts commit a99d95e3af.

That commit has only removed the warning but not the error.

Sponsored-by: On-Waves ehf
2015-06-29 10:27:25 +02:00
Jacob Erlbeck a99d95e3af llc/test: Explicitly enable extended initialiser lists
To support extended initialiser lists some platforms (at least debian
squeeze) require to add -std=c++0x or -std=gnu++0x to CXXFLAGS. While
that option is deprecated on newer platforms (at least gcc 4.8) this
options is still supported on every platform currently in use.

This commit adds -std=gnu++0x to the CXXFLAGS used to compile
LlcTest.cpp.

Sponsored-by: On-Waves ehf
2015-06-29 10:06:29 +02:00
Jacob Erlbeck e500e2e5d1 llc: Add missing include directive for struct timeval
While including time.h is sufficient with Ubuntu's current libc6
2.19, the correct (and portable) include file is sys/time.h.

This commit modifies the include directive in llc.h accordingly.

Fixes: Jenkins #600

Addresses:
    In file included from gprs_ms.h:28,
        from gprs_ms.cpp:22:
        llc.h:68: error: field 'recv_time' has incomplete type
        llc.h:69: error: field 'expire_time' has incomplete type

Sponsored-by: On-Waves ehf
2015-06-29 09:46:17 +02:00
Jacob Erlbeck b671dbfe94 llc: Move storage of timestamps into gprs_llc_queue
Currently the receive and expiry timestamps are prepended to the LLC
msgb before it is passed to gprs_llc_queue::enqueue(). Since this meta
information should not be counted as LLC octets, the gprs_llc_queue
needs to known about this (unless the correction was done in the LLC
layer).

This commit moves the meta information storage code into
gprs_llc_queue.  The meta data is now stored in the control block
(cb) area of the msgb.

Note that the info pointer that is returned from the dequeue method
is only valid if that method returns a (non-NULL) msgb. It must not
be used after that msgb has been modified or freed.

Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck 1e50a3dade llc: Make timeval arguments const
Some struct timeval pointer arguments do not have the const qualifier,
albeit the methods do not write to the structures. The next commit
will change related pointers to const, so this commit provides the
required constness.

Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck 70b96aa232 ms: Reduce DL CS level if only a few LLC bytes are left
If just a few bytes are left to send to the MS, it makes sense to
reduce the coding scheme level to increase the throughput. This
has been shown by Chen and Goodman in their paper "Theoretical
Analysis of GPRS Throughput and Delay". See their throughput over C/I
measurement graphs (figures 4 and 5 in the paper) for details.

This commit implements a simplified CS downgrade feature for the
downlink. The coding scheme will be downgraded if there are only a
few octets are left to be send over the TBF (see the
downgrade-threshold command below) and the NACK rate is not low (the
CS will not get degraded on a high quality RF link). As an exception,
CS-3 will be degraded to CS-1, since CS-2 does not improve the
throughput in general when a few small packets are sent and the
signal fades slowly (see Chen/Goodman).

The following VTY command is added to the config-pcu node:

- cs downgrade-threshold <1-10000>
- cs no downgrade-threshold

to set the threshold of the number of remaining bytes to be RLC/MAC
encoded. The CS will only be reduced, if the number is below the
threshold. The 'no' command disables this feature completely. The
default value is 200 octets.

Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck 07eb655244 llc: Keep track of the number of stored LLC octets
To get the number of LLC octets that are stored in the queue, this
commit adds a m_queue_octets member along with a octets() method.
This value is updated similarly to m_queue_size on each modifying
method call.

Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck 1eae96ca2f llc: Add missing include directive to llc.h
Currently struct llist_head is used without declaration which
accidently did not produce an error so far.

This commit adds the missing include directive.

Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck 626369c2fb llc/test: Add test program for LLC related tests
Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck 409efa1ec8 tbf: Fix downlink packet loss
When the MS is pinged with a longer interval, many packets get lost
even if the GprsMs object is kept. If the interval is above the time
where the DL TBF is in state FLOW (mainly influenced be the
dl-tbf-idle-time command), an new TBF must be requested via AGCH for
each ICMP PING message.

Currently the LLC frame containing the PING is immediately stored
in the TBF and gets lost, if TBF establishment fails for some reason.

This commit moves all calls to put_frame() to schedule_next_frame(),
where the data is moved from the LLC queue to the frame storage
within the TBF object. This method is only called from within
create_new_bsn() when the TBF is in the FLOW state and the frame is
going to be encoded immediately.

At all other places, where put_frame() has been called before, the
LLC message is just appended to the LLC queue in the GprsMs object.
This change effectively simplifies the related code parts, since
date/len information and discard notifications is no longer needed
there.

Ticket: #1759
Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck 411686402b tbf/test: Add test for DL LLC packet loss
Currently LLC frames are lost or even reordered when the TBF has be
established via the AGCH and the procedure fails for some reason.

This test tries to reproduce this behaviour by throwing away the
first TBF while calling the handle() method several times. The
results of create_dl_acked_block() are checked against expected
values (this is currently party disabled because the bug still
persists).

Ticket: #1759
Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck 04a108617a ms: Store the NACK rate in the MS object
Currently the NACK/unconfirmed ratio is already passed to the
corresponding MS object, but the value is not being stored there.

This commit adds a member and a getter method and include the values
into the output of the 'show ms' command.

Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck e1d2b3568a tbf: Include CS into create_new_bsn log message
This change lets the test suite fail, so it get its own commit.

Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck da1a79ef5b l1: Add debug log messages for I_LEVEL
The I_LEVEL values that are obtained now look suspicious. They do not
seem to be contained in messages recorded via gsmtab.

To help debugging this issue, this commit adds related debug messages
that are generated while the encoded values are taken from the
RLC/MAC messages.

Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck 51b1151044 l1: Store measurement values sent by the MS
This commit extends the pcu_l1_meas structure by MS side measurement
values which are transmitted by PACKET DOWNLINK ACK/NACK and
PACKET RESOURCE REQUEST messages. The encoded values are remapped to
dB respectively % values. The values are stored in the corresponding
MS object (if there is one).

Note that the values are store as (rounded) integers, so some
different encodings are mapped to the same decoded value.

Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck 94cde130ca ms: Add UL CS selection based on L1 link quality
Currently the UL CS values are set to the corresponding DL CS value,
eventually limited by a maximum value. This approach does not reflect
the general situation of the RF link between ME and BTS, which is
rather asymmetric e.g. due to a lower degree of TX efficiency of the
built-in antenna. This means, that UL and DL CS control should be
decoupled for better results.

This commit adds automatic UL CS selection based on the link quality
measurement parameter. Each coding scheme is mapped to a link quality
range. If the link quality value leaves that range, the current UL CS
value is increased/decreased accordingly. This value will be copied
when the next PACKET_UPLINK_ACK_NACK or PACKET_UPLINK_ASSIGMENT is
sent to the MS.

The following VTY command will be added to the config-pcu node:

-  cs link-quality-ranges cs1 <0-35> cs2 <0-35> <0-35>
          cs3 <0-35> <0-35> cs4 <0-35>

which sets the ranges for the four coding schemes. For instance the
example below reflects the current default values:

  cs link-quality-ranges cs1 6 cs2 5 8 cs3 7 13 cs4 12

set the following ranges, where the overlapping is used to configure
a hysteresis:

  CS1: -inf ..  6
  CS2:    5 ..  8
  CS3:    7 .. 13
  CS4:   12 .. inf

Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck e4bcb62dbf ms: Store the L1 measurement values in the MS objects
This commits adds the GprsMs::update_l1_meas() and GprsMs::l1_meas()
methods to store and access the measurement values. The internal
state is updated depending on which values are actually set.

In addition, these values are shown in the output of the 'show ms
imsi|tlli' command.

Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck 20f6fd1b63 l1: Pass all L1 measurements upwards
Currently only the RSSI value is passed to the upper layers. Other
values like TA and BER which are needed for TA update respectively CS
selection are not propagated.

This commit introduces and passes a struct that contains a set of
measurement values.

Sponsored-by: On-Waves ehf
2015-06-22 10:39:06 +02:00
Jacob Erlbeck b4584ff6c4 build: Add -lrt to AM_LDFLAGS
This is needed to link programs using clock_gettime and related
functions when compiling with older glibc versions.

This should fix the Jenkins build. Nevertheless fixing this in
configure.ac were probably nicer.

Sponsored-by: On-Waves ehf
2015-06-22 10:36:50 +02:00
Jacob Erlbeck 0808f68601 ms: Fix timer start condition (Coverity)
Currently the timer can be started even if m_ul_tbf is attached.

Replace m_dl_tbf by m_ul_tbf to only start the timer if _both_ TBF
are detached.

Fixes: Coverity CID 1304683
Sponsored-by: On-Waves ehf
2015-06-11 13:13:55 +02:00
Jacob Erlbeck 25db7c6116 ms: Add missing initialiser for m_delay (Coverity)
Set m_delay to 0 in the constructor to disable the timer feature by
default.

Fixes: Coverity CID 1304682
Sponsored-by: On-Waves ehf
2015-06-11 13:13:55 +02:00
Jacob Erlbeck 7bf9f49728 tbf: Check for NULL in name() (Coverity)
The gprs_rlcmac_tbf::name() method is generally used to generate log
messages. To avoid the need for an explicit NULL check for the tbf
and to get a consistent text if it is NULL, this commit adds a
NULL check to the method itself so that it can be called with this ==
NULL.

Fixes: Coverity CID 1304680, 1304681
Sponsored-by: On-Waves ehf
2015-06-11 13:13:55 +02:00
Jacob Erlbeck 0ae4313800 bssgp: Calculate the avg_delay_ms in 32bit only (Coverity)
Currently the delay_sum is stored in 64 to avoid overflow errors.
But only the result of tv_sec * 1000 is casted to 64 bit, resulting
in an overflow if the accumulated queue delay reached 25 days (which
will not happen in practice, unless there are >200k LLC messages with
a max of 10s delay each in the queue). If that were the case, the
only impact would be a wrong number in a log message and in the BSSGP
FLOW CONTROL message.

This commit changes the calculations so that they are done in 32 bit
only, rather than to do the calculation in 64 bit properly.

Fixes: Coverity CID 1298705
Sponsored-by: On-Waves ehf
2015-06-11 13:13:55 +02:00
Jacob Erlbeck d0aee85b29 llc: Fix LLC UI frame detection (Coverity)
Currently the wrong nibble is masked out, so the conditional
expression always yields true.

Therefore gprs_llc::is_user_data_frame() always returns true. As a
consequence, the low watermark feature of
gprs_rlcmac_dl_tbf::llc_dequeue() is not being used in fact.

This commit fixes the mask value.

Fixes: Coverity CID 1292834, 1292835
Sponsored-by: On-Waves ehf
2015-06-11 13:13:55 +02:00
Jacob Erlbeck 09fdf6622a bssgp: Handle btcx == NULL in gprs_bssgp_pcu_rx_sign (Coverity)
Currently it is assumed, that btcx is non-NULL. The btcx is only used
to obtain the BVCI in some log messages.

This commit changes that by using -1 as shown BVCI value.

Fixes: Coverity CID 1040961
Sponsored-by: On-Waves ehf
2015-06-11 13:13:55 +02:00
Jacob Erlbeck 37e896dff1 vty: Add command to show detailed MS info
This commit extends the "show ms" command to display an extended set
of information for a single MS.

The following VTY commands are added:

- show ms tlli TLLI
- show ms imsi IMSI

Sponsored-by: On-Waves ehf
2015-06-08 09:41:24 +02:00
Jacob Erlbeck b33e675e5a ms: Add support for maximum CS values
Currently the CS values can be increased to CS4 even when the "cs"
configuration command has been used with a lower value. The "cs"
command just sets the initial coding scheme, so other means are
needed to limit the selection. One approach is to use the CS flags
passed in SI, but these are currently ignored.

To make it possible to limit the CS selection by configuring the PCU,
this commit adds the following VTY commands to config-pcu:

- cs max <1-4>             Limit DL and UL CS to the given value
- cs max <1-4> <1-4>       Limit DL and UL CS separately (DL first)
- no cs max                Don't limit

Ticket: #1674
Sponsored-by: On-Waves ehf
2015-06-08 09:41:07 +02:00
Jacob Erlbeck 8158ea7288 ms: Add blocking period for CS upgrade
Currently the CS level is immediately increased if the error rate
drops below the lower threshold. Since the measurement values are not
damped, this behaviour leads to a quick return to higher CS values
even under bad radio conditions. Since with GPRS RLC/MAC blocks
cannot be resent with another coding scheme, increasing the CS value
should be done carefully.

This commit adds a blocking period that only allows higher CS values
if all error rate measurements were below the LOW threshold for a
certain amount of time (currently fixed to 1s).

Ticket: #1674
Sponsored-by: On-Waves ehf
2015-06-08 09:40:48 +02:00
Jacob Erlbeck 144a1d0516 tbf: Ignore lost+recv == 1
Currently the CS level gets changed quickly if single RLC/MAC blocks
are sent (e.g. LLC dummy commands), since the rate is either 0% or
100%.

This commit ignores measurements which are based on a single block
only.

Sponsored-by: On-Waves ehf
2015-06-08 09:40:41 +02:00
Jacob Erlbeck 8322d08071 tbf: Add adaptive coding scheme configuration
This commit adds the following VTY commands to config-pcu:

- cs threshold <0-100> <0-100>    Enables adaptive CS selection
- no cs threshold                 Disables it

The "cs threshold LOW HIGH" command sets the water marks
(cs_adj_lower_limit and cs_adj_upper_limit) used to
decide about switching coding schemes.

Ticket: #1739
Sponsored-by: On-Waves ehf
2015-06-08 09:40:35 +02:00
Jacob Erlbeck a17fccbcf4 tbf: Add debugging output to analyse_errors()
To help with the debugging, optimisation, and understanding of this
method, this commit adds an info string containing a flag character per
RLC/MAC data block in the current window.

Note that the blocks are shown in reversed order (highest BSN first)
in comparison to other logging output.

Sponsored-by: On-Waves ehf
2015-06-08 09:40:28 +02:00
Jacob Erlbeck 1751c62c98 tbf: Add adaptive DL CS adjustment
To cope with transmission failures due to bad radio conditions, a
different coding scheme with more redundance can be used.

This commit adds an implemenation that is based on the Ack/Nack
ratio per PACKET DOWNLINK ACK/NACK message received from the MS.

Basically the CS level is decreased, if the block error rate goes
above cs_adj_upper_limit (default 33%), and it is increased, if the
rate drops below cs_adj_lower_limit (default 10%). Only blocks that
have been encoded with the current CS are taken into account.

Note that this approach doesn't measure the MS->BTS conditions and
that the measurement values reported by the MS are not taken into
account.

Ticket: #1739
Sponsored-by: On-Waves ehf
2015-06-08 09:40:09 +02:00
Jacob Erlbeck f47f68a9d8 vty: Add 'show ms all' command
This command lists the entries of the ms_store by a line per MS.
Beside TLLI and IMSI, some measurement and state information is
shown.

A ms_list() getter method is added to GprsMsStorage to obtain a list
of the MsGprs objects.

The following VTY command is added to the 'enable' node:

 - show ms all

Sponsored-by: On-Waves ehf
2015-06-08 09:39:51 +02:00
Jacob Erlbeck 62e96a3535 vty: Add a file for C++ functions
Currently the pcu_vty.c doesn't compile with C++. Thus C++ object
cannot be access directly there.

This commit adds a helper C++ file that exports all functions with C
calling conventions and naming to work around that limitation until
the transition of pcu_vty.c is completed.

Sponsored-by: On-Waves ehf
2015-06-08 09:39:43 +02:00
Jacob Erlbeck a700dd9e11 tbf: Move the current CS field to GprsMs
Currently the current CS value is stored in the cs field of
gprs_rlcmac_tbf and initialised when it is used the first time.

This commit adds separate fields for UL and DL CS values to the
GprsMs class and provides corresponding getter methods for GprsMs and
gprs_rlcmac_tbf.

Ticket: #1739
Sponsored-by: On-Waves ehf
2015-06-08 09:39:25 +02:00
Jacob Erlbeck 17214bb06d ms: Add back pointer to BTS
Since more functionality will be moved to the GprsMs class, a pointer
to the current BTS object is added to allow access to configuration
data and other methods.

Sponsored-by: On-Waves ehf
2015-06-08 09:39:07 +02:00
Jacob Erlbeck befc760f86 tbf: Store MS class in GprsMs objects
The ms_class value is a property of the MS and thus belongs to the
GprsMs class. Nevertheless the MS object is created after the TLLI
gets known, so the value still has to be stored in the TBF initially.

This commit add the ms_class value to the GprsMs class and introduces
TBF accessor functions which either access that object or, if that is
not available, the value stored locally.

Ticket: #1674
Sponsored-by: On-Waves ehf
2015-06-08 09:38:49 +02:00
Jacob Erlbeck 489a2b35d8 tbf: Move the LLC queue to GprsMs
Currently the enqueued DL LLC messages which have not yet passed to
RLC/MAC encoding are eventually copied from one TBF to the next one
(see gprs_rlcmac_dl_tbf::reuse_tbf). Since the enqueued LLC messages
are related to a specific MS, they should be stored at that layer.

This commit moves the gprs_llc_queue object to GprsMs and changes the
TBF's accessor methods accordingly. The LLC copying code is removed
from gprs_rlcmac_dl_tbf::reuse_tbf().

Sponsored-by: On-Waves ehf
2015-06-08 09:38:44 +02:00
Jacob Erlbeck 10ed79553a tbf: Make the ms() getter method const
Currently this method cannot be used in other const methods.

This commit adds the missing const keyword.

Sponsored-by: On-Waves ehf
2015-06-08 09:35:47 +02:00
Jacob Erlbeck 1d0a52a349 llc: Add missing declarations to llc.h
Currently llc.h relies on the structs BTS, timeval, and msgb being
declared before the file is included.

This commit adds forward declaration for these structs, because they
will only be used for pointer types.

Sponsored-by: On-Waves ehf
2015-06-08 09:35:29 +02:00
Jacob Erlbeck 6dbe822062 llc: Separate LLC queue handling from gprs_llc
Currently the gprs_llc class handles both LLC queueing and the
partition into smaller pieces for RLC/MAC encapsulation. This hinders
the separation of TBF and MS related data, since LLC queueing belongs
to the MS related code while the RLC/MAC encoding/decoding belongs to
the TBF layer.

This commits takes the LLC queueing related methods and members and
puts them into a new class gprs_llc_queue. It puts the queueing
object into gprs_rlcmac_tbf and adds accessor functions. The
implementation in tbf.cpp and tbf_dl.cpp is adapted accordingly.

Ticket: #1674
Sponsored-by: On-Waves ehf
2015-06-08 09:35:11 +02:00
Jacob Erlbeck b3f713bd7b l1: Fix warning by logging acc_delay
The acc_delay value is computed but not used, resulting in a warning.

This commit adds a logging message it case the function is executed
to make the warning disappear. It also adds a CPP warning to remind
of the incomplete implementation.

Addresses:
  sysmo_l1_if.c:226:10: error: variable ‘acc_delay’ set but not used
[-Werror=unused-but-set-variable]

Sponsored-by: On-Waves ehf
2015-06-08 09:34:45 +02:00
Jacob Erlbeck 3c91cb881d pcu: Set ms_idle_sec to 60s
Since the timing advance storage has been removed, the TA values are
lost, when the last TBF of a single MS is freed. The TA storage has
at least saved the last 30 TA values. So now in more cases a wrong TA
value can be transmitted to an MS. Note that this also could have
happened before the removal of the storage, especially if more than
30 MS were in use.

This commit changes the default value of ms_idle_sec to 60s which is
higher than default value for T3314 (44s, see TS 24.008, 11.2.2),
after which the SGSN will have to start paging anyway. In that case,
a RACH request will be triggered, that will trigger an update of the
TA value in the PCU.

Sponsored-by: On-Waves ehf
2015-06-05 13:49:06 +02:00
Jacob Erlbeck a098c19b55 tbf: Set MS timeout
This commit sets the MS timeout when the MS object is created. The
value is taken from the ms_idle_sec field in gprs_rlcmac_bts which
can be changed by the VTY commands shown below.

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

- ms-idle-time <1-7200>      Set the timeout in seconds
- no ms-idle-time            Disable the timeout

Another timer that is related is T3314 (Ready Timer, default 44s, GSM
24.008, 11.2.2) which requires the SGSN to use paging after its
expiry. Longer timeouts can help if adaptive coding scheme selection
is used (not yet implemented). On the other hand, measurement values
(TA, signal quality) can get invalid after some time, especially with
a moving MS. So a value slightly above T3314 is probably a good value
to start with.

Sponsored-by: On-Waves ehf
2015-05-28 17:40:15 +02:00
Jacob Erlbeck d9e102472a ms: Add timer
Currently the MS object is immediately idle when all TBFs are
detached and if no guard is being used. Since the plan is to use the
MS objects to pass information from one TBF to the next one even
across the gap of some seconds of inactivity, a mechanism is needed
to keep the MS objects around for some time.

This commit extends the GprsMs class by a timer that keeps the MS
objects in non-idle state for some time after all TBFs have been
detached. The set_timeout method must be used with a non-zero value
to activate this feature.

Sponsored-by: On-Waves ehf
2015-05-28 16:38:16 +02:00
Jacob Erlbeck 1db67e0a35 tbf: Remove TimingAdvance storage
Currently the TA storage stores up to 30 TLLI->TA mappings, if more
entries are created the oldest one is dropped. In theory this can
lead to missing TA information if many MS are present.

This commit removes the TimingAdvance class completely, since the TA
value is now stored in the GprsMs objects.

Note that the GprsMs objects are currently not kept after the TBFs
have detached from them, so the TA values are now kept for a shorter
time than before.

Ticket: #1674
Sponsored-by: On-Waves ehf
2015-05-28 13:59:25 +02:00
Jacob Erlbeck 9200ce6019 tbf: Store the timing advance (TA) value in the GprsMs object
The TA value rather relates to an MS and not to a single TBF. So all
TBFs share the same TA value. Currently the TA value is stored per
TBF and eventually copied from an old TBF to a new one. It is in
general only passed with an RACH request when the TLLI and thus the
MS is not yet known.

This commit adds a TA member to the GprsMs class and uses that one
when the TBF is associated to an MS object. Since the TBF is not
always associated with an MS object (after RACH or when it has been
replaced by another TBF), the TA value is still stored in each TBF
and that value is used as long as no MS object is being associated.

Sponsored-by: On-Waves ehf
2015-05-28 13:58:23 +02:00
Jacob Erlbeck ddfc0d5763 tbf/test: Add tests for single and two phase access
These tests cover the message exchange from receiving from the first
RACH request to the first data block when establishing an uplink TBF.

This will be used to check, whether TA and other values are passed to
an MS object correctly.

In addition, the RX RACH log message in rcv_rach is extended to
contain the single block fn.

Sponsored-by: On-Waves ehf
2015-05-28 12:29:43 +02:00
Jacob Erlbeck d3eac2867a tbf: Remove TBF chaining (m_new_tbf and m_old_tbf)
Currently a new TBF is chained to an existing older one, either of
the other direction (active or releasing) or of the same direction
(releasing). This does not work properly work if and uplink and a
downlink TBF are being established at the same time while an old TBF
is being released. In that case, one of them is thrown away and the
pending procedure is cancelled.

The chaining is no longer necessary since the GprsMs objects have
been introduced which keep track of the active TBFs.

This commit removes the TBF members m_new_tbf and m_old_tbf and the
related methods and code paths.

Note that a new TBF can replace an older TBF entry of the same
direction within an MS object when it is associated with an MS (e.g.
by TLLI or because it is assigned via another, already associated
TBF). In that case, the old TBF is no longer associated with an MS
object.

Ticket: #1674
Sponsored-by: On-Waves ehf
2015-05-28 12:29:43 +02:00
Jacob Erlbeck 1c68abaffa tbf/test: Attach TLLI and MS objects to TBFs
Currently the DL TBF's TLLI are not set. This will have to change in
the future, when the m_new_tbf chaining is replaced by the usage of MS
object to group TBFs.

This commit just calls update_ms() on newly created TBFs. This
changes many of the lines written to stderr due to the TLLI change,
but doesn't change other aspects or messages beside creating and
destroying MS objects.

Sponsored-by: On-Waves ehf
2015-05-28 12:29:43 +02:00
Jacob Erlbeck 71e55118f5 tbf: Remove IMSI handling from trigger_dl_ass
Currently the BTS::trigger_dl_ass() method assigns the IMSI to the MS
object. This should be (and is already) done earlier where the MS
object is retrieved/created.

This commit removes the corresponding code along with the 'imsi'
parameter from trigger_dl_ass.

Sponsored-by: On-Waves ehf
2015-05-28 12:29:42 +02:00
Jacob Erlbeck 7b9f825ae8 ms: Use the IMSI to retrieve the MS object
This commit extends get_ms() to really compare the IMSI if it has
been given. Matching by TLLI has a higher precedence than matching by
IMSI.

Ticket: #1674
Sponsored-by: On-Waves ehf
2015-05-28 12:29:40 +02:00
Jacob Erlbeck b0e5eaf59a tbf: Move IMSI to MS object
Currently the IMSI is stored in the TBFs. Since it directly refers to
an MS, it should rather be stored in an MS object.

This patch move the m_imsi field from gprs_rlcmac_tbf to GprsMs,
changes gprs_rlcmac_tbf::imsi() to get the IMSI from the associated
MS object, and adds getter and setter to GprsMs. Before changing the
IMSI of the associated MS object, assign_imsi() checks if there is
already another MS object with the same IMSI and eventually resets
the IMSI of that one. So using update_ms() and assign_imsi() ensures
that there are not two MS object entries is the storage with the
same TLLI or the same IMSI.

Ticket: #1674
Sponsored-by: On-Waves ehf
2015-05-28 12:28:40 +02:00
Jacob Erlbeck 9a2845d491 tbf/test: Fix IMSI creation
Currently the generated IMSI contains blanks due to a missing '0'
flag in the corresponding snprintf statement. In addition, the buffer
is effectively limited to 15 bytes which is shorter than the
generated character sequence, such that the last character of the
IMSI is removed.

This patch fixes both issues. Since snprintf itself will add a
terminating \0 character, the whole buffer can be passed to snprintf.

Sponsored-by: On-Waves ehf
2015-05-27 13:30:01 +02:00
Jacob Erlbeck 0e50ce6145 tbf: Always call set_tlli/confirm_tlli in update_ms
Currently the m_tlli member in GprsMs is set by the constructor,
circumventing the TLLI confirmation mechanism.

This commit replaces the get_or_create_ms() method by a create_ms()
method which takes the TLLI and the direction (UL or DL) as
parameters to select either set_tlli() or confirm_tlli(). The MS
object is instantiated with TLLI = 0, and therefore GprsMs::tlli() is
extended to return the DL TLLI if both of the other TLLI are not set.

Note that create_ms() will not check whether an MS object with a
matching TLLI is already stored in the list, so it should only be
called after a corresponding get_ms() in general.

Sponsored-by: On-Waves ehf
2015-05-27 13:30:01 +02:00
Jacob Erlbeck 767193e20b tbf: Remove the TLLI from the TBFs
Currently the TLLI is stored in each TBF. Since each MS is now
represented by a GprsMs object which takes care of TLLI updating,
and each TBF that has been associated with an TLLI also contains a
reference to a GprsMs object, per TBF TLLI handling is no longer
needed. Keeping all TBF m_tlli members up to date is complex and
doesn't currently work correctly in all circumstances.

This commit removes m_tlli and related members from the TBF class and
the tbf_by_tlli functions from the BTS class.

Ticket: #1674
Sponsored-by: On-Waves ehf
2015-05-27 13:29:59 +02:00
Holger Hans Peter Freyther d1cb41bfd0 write_queue: Check the result of osmo_wqueue_enqueue and free
The write_queue is designed to have a maximum amount of pending
messages and will refuse to take new messages when it has been
reached. The caller can decide if it wants to flush the queue
and add the message again, create a log. But in all cases the
ownership of the msgb has not been transferred. Fix the potential
memory leak in the failure situation.
2015-05-22 10:37:57 +02:00
Holger Hans Peter Freyther 5752285bc5 misc: Update the email address to point to the current ML
We have unified all PS related communication to the
osmocom-net-gprs mailinglist, update configure.ac to
point there.
2015-05-22 10:33:46 +02:00
Holger Hans Peter Freyther b75e23143b llist: Reduce the external dependencies of this test
Debian is using the classic bfd linker and when passing
libosmogb as link dependency it always wants/needs to
resolve the bssgp_prim_cb symbol (which is to be provided
by the application).

Only keep the libosmocore dependency.

Fixes:
lib/libosmogb.so: undefined reference to `bssgp_prim_cb'
collect2: error: ld returned 1 exit status
Makefile:511: recipe for target 'llist/LListTest' failed
2015-05-22 10:33:35 +02:00
Jacob Erlbeck 4f459799e3 tbf: Just pass the MS object in reuse_tbf
Currently the MS will be searched based on the TLLI in resue_tbf().
Since the MS object is already known in the TBF when the TLLI is set,
it can just be passed to the new TBF.

This commit removes the call to update_ms() and just adds
new_tbf->set_ms(ms()) which will also work as expected if ms() == NULL.

Sponsored-by: On-Waves ehf
2015-05-21 17:11:40 +02:00
Jacob Erlbeck be0cbc1b7e tbf: Explicitly pass the direction to update_ms()
The type of the TBF update_ms() is being called on does not always
reflect whether the TLLI has been signaled by the MS or the SGSN.

This commit adds an additional parameter to tell the method, in which
direction the TLLI has been passed.

Sponsored-by: On-Waves ehf
2015-05-21 17:11:14 +02:00
Jacob Erlbeck 87597358cf tbf: Get the TLLI from the MS object
Since the synchronisation of the TBF's concerning the TLLIs has been
removed in 'Support new and old TLLI's', gprs_rlcmac_tbf::tlli() can
return the old TLLI which is probably different to ms()->tlli() in
that case. This can lead to a wrong TLLI being used in BSSGP messages.

This commit modifies the TBF's tlli() method to get the current TLLI
from the MS object.

Sponsored-by: On-Waves ehf
2015-05-21 17:10:49 +02:00
Jacob Erlbeck 9399046729 ms: Support new and old TLLIs
According to the specification (GSM 04.08/24.008, 4.7.1.5) after a
new P-TMSI has been assigned, the old P-TMSI must be kept basically
until it has been used by both sides. Since the TLLI will be derived
from the P-TMSI, the old TLLI must also be kept until the new TLLI
has been used by both MS and SGSN.

This commit modifies the TLLI handling of GprsMs accordingly.
set_tlli() is only used with TLLIs derived from MS messages,
confirm_tlli() is used with TLLIs derived from messages received from
the SGSN. tlli() returns the value set by the MS. check_tlli()
matches each of the TLLI used by either MS or SGSN as well as the old
TLLI until it has been confirmed.

Sponsored-by: On-Waves ehf
2015-05-21 17:10:42 +02:00
Jacob Erlbeck e43460b50f ms: Integrate the MS storage
Use the MS storage to find a MS object for a given TLLI instead of
searching the TBF lists. The TBFs are then taken from the MS object,
if one has been found. If all TBF might be temporarily detached from
the MS object, a GprsMs::Guard is added to prevent the deletion of
the object, in case another TBF gets attached later on in the scope.

Ticket: #1674
Sponsored-by: On-Waves ehf
2015-05-20 11:36:12 +02:00
Jacob Erlbeck 5367086175 ms: Add MS storage class
Currently the MS objects are contained in the TBF objects only. To
allow for an extended life time after the TBF objects have been freed
and to find them based on TLLI, a container for the MS objects is
needed.

This commit adds the container class and also adds the corresponding
m_list member to GprsMs. Further integration into the PCU code is not
yet done.

Ticket: #1674
Sponsored-by: On-Waves ehf
2015-05-20 11:36:12 +02:00
Jacob Erlbeck dfef28de88 llist: Add a C++ wrapper for linux_list
This commit adds the LListHead class which is a wrapper around the
linuxlist. It adds an additional member to refer to the container,
since the container_of macro doesn't work properly with C++ classes.
All functions and macros from linuxlist.h are support except for the
entry macros (e.g. llist_entry, llist_for_each_entry, ...). To access
the container (entry), an entry() method is provided instead:

  llist_for_each(pos, &elems) {
      pos->entry()->do_something();
  }

Sponsored-by: On-Waves ehf
2015-05-20 11:36:06 +02:00
Jacob Erlbeck 67c385046d tbf/test: Optionally show talloc report
To show the talloc report more easily, this commit adds a call to
talloc_report_full if the TALLOC_REPORT_FULL environment variable is
set (the value is ignored). Since the stderr output is checked by the
test suite, this feature is not enabled by default.

Sponsored-by: On-Waves ehf
2015-05-20 11:31:07 +02:00
Jacob Erlbeck fecece0e59 tbf: Add MS object management to TBF code
This commit adds MS object creation and cleanup to the TBF related
code. MS objects are created when a TBF that has been "anonymous" so
far gets associated with a TLLI. When a TBF is replaced by another,
the old TBF is detached and the new one is attached to the MS. When
all TBFs have been detached, the MS object gets deleted.

The TBF related code should not call attach_tbf/detach_tbf directly
but use set_ms instead to make sure, that the references are updated
properly. GprsMs::detach_tbf also calls set_ms(NULL) on the detached
TBF object.

The MS object is not really used yet, the focus is still on object
creation, TBF association, and cleanup.

Ticket: #1674
Sponsored-by: On-Waves ehf
2015-05-20 11:31:07 +02:00
Jacob Erlbeck e04e0b0a20 ms: Add GprsMs class to hold per-MS information
Currently only TBF objects are used to handle the data flow between
the MS and the SGSN. MS specific data (e.g. pending LLC frames, TLLI)
is copied between successive TBFs. If all TBFs (uplink and downlink)
are idle for some time, all information about the MS is discarded in
the PCU. This makes the implementation of some features more
difficult, e.g. proper TLLI and timing advance handling,
connection based CS selection, and proper management of multiple TBF.

This commit adds the GprsMs class that is intended to hold
information directly related to the MS and to keep references to the
active TBFs.

The class is not yet integrated with the other PCU code. A GprsMs
object container and MS specific fields (TA, CS) will be added in
later commits.

Note that calling detach_tbf() can possibly delete the MS object
depending on the callback implementation.

Ticket: #1674
Sponsored-by: On-Waves ehf
2015-05-20 11:30:41 +02:00
Jacob Erlbeck 6eeb7c7e74 bssgp: Increment BSSGP flow control tag value
Currently the tag value in FLOW CONTROL BVC messages is always 1.

This commit changes the implementation to increment that value with
each of the FLOW CONTROL BVC messages that is sent to the SGSN.

Sponsored-by: On-Waves ehf
2015-05-06 15:26:08 +02:00
Jacob Erlbeck 6e4ccec6c4 bssgp: Compute and transmit queue delay
The specification 28.018,  allows to transmit the average LLC downlink
queueing delay in FLOW CONTROL BVC messages (BVC Measurement IE, see
GSM 28.018, 10.4.4 and 11.3.7).

This commit extends gprs_bssgp_pcu.cpp to compute the average delay
time between two subsequent FLOW CONTROL BVC messages. The average is
implemented as an arithmetic average without any weighting.

Ticket: OW#1432
Sponsored-by: On-Waves ehf
2015-05-06 15:22:57 +02:00
Jacob Erlbeck 0288cdb0a8 bssgp: Add VTY command to Limit the bucket size by time
Currently the bucket size is by default being computed based on the
leak rate and the expected life time of LLC frames. The latter is
either taken from 'queue lifetime' (if given) or a fixed value is
used. Using 'queue lifetime' has the drawback that it sets a 'hard'
limit, since frames will be dropped if they stay in the queue
for a longer time.

This commit adds a VTY command to specifically set the time used for
the computation of the bucket size advertised to the SGSN. It does
not affect the PCUs queue handling in any way. If the bucket time is
not set (or if the 'no' command has been used), the old behaviour
(see above) is applied.

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

- flow-control bucket-time <1-65534>  Sets the time in centisecs
- no flow-control bucket-time         Don't use this time

Ticket: OW#1432
Sponsored-by: On-Waves ehf
2015-05-06 15:22:33 +02:00
Jacob Erlbeck 3d62fc55d8 bssgp: Compute BVC bucket size and leak rate
Currently the PDCH assignment and coding scheme does not influence
the values transmitted by the FLOW-CONTROL-BVC messages.

This commit adds the computation of those values. If the leak rate is
not given explicitly, it is derived from the number of PDCH and the
allowed coding scheme. If the BVC bucket size is not given
explicitly, it is derived from the leak rate and the maximum buffer time.
The latter is taken from the 'queue lifetime' command (or 10s if this
has not been used). The MS default bucket size is set to 50% of the
BVC bucket size. The MS default rate computation assumes, that each MS
only supports up to 4 time slots for GPRS RX.

Sponsored-by: On-Waves ehf
2015-05-06 15:20:43 +02:00
Jacob Erlbeck 87d7341fbe bssgp: Make BVC bucket size / leak rate configurable
Currently the FLOW-CONTROL_BVC message contains fixed values: The tag
is 1, the BVC bucket size is 6MB, the BVC bucket leak rate is
820kbit/s, the MS bucket size is 50kB, and the MS leak rate is
50kbit/s.

This commit makes the BVC parameters configurable and adds the
following VTY commands:

- flow-control force-bvc-bucket-size <1-6553500>
- no flow-control force-bvc-bucket-size
- flow-control force-bvc-leak-rate <1-6553500>
- no flow-control force-bvc-leak-rate
- flow-control force-ms-bucket-size <1-6553500>
- no flow-control force-ms-bucket-size
- flow-control force-ms-leak-rate <1-6553500>
- no flow-control force-ms-leak-rate

The 'no' variants restore the default behaviour.

Sponsored-by: On-Waves ehf
2015-05-06 15:20:35 +02:00
Jacob Erlbeck 29d91e9271 tbf: Added calls to llc_dropped_frame
Currently this function which increments the corresponding counter is
just called in gprs_llc::clear(). It is not called on places where
LLC DISCARDED messages are sent.

This commit adds calls to llc_dropped_frame() at these places.

Sponsored-by: On-Waves ehf
2015-05-06 15:20:25 +02:00
Jacob Erlbeck 801d6fe40c tbf/test: Fix old_tbf argument to tbf_alloc_ul_tbf
Currently the ul_tbf is used in its own declaration as an argument to
tbf_alloc_ul_tbf, where dl_tbf can be used instead and makes more
sense.

This commit uses dl_tbf instead of ul_tbf as old_tbf.

Addresses:
  tbf/TbfTest.cpp:71:14: warning: ‘ul_tbf’ may be used uninitialized in
      this function

Sponsored-by: On-Waves ehf
2015-05-04 10:02:01 +02:00
Jacob Erlbeck 0494c75a53 pcu_emu/test: Initialise current_test
Currently the global current_test is not initialised, which leads to
memory access failures. In addition, the variable is signed but used
in comparisons with unsigned values.

This commit changes the type to size_t and initialises the variable
to 0.

Sponsored-by: On-Waves ehf
2015-05-04 10:02:01 +02:00
Jacob Erlbeck 0d39dc92b5 l1if: Add missing function prototypes
A few prototypes are not part of any header files, leading to
corresponding compiler warnings.

This commit adds the missing prototypes to sysmo_l1_if.h.

Addresses:
  sysmo_l1_if.c:347:2: warning: implicit declaration of function
      ‘l1if_transport_open’
  sysmo_l1_if.c:364:3: warning: implicit declaration of function
      ‘l1if_transport_close’
  sysmo_l1_fwd.c:89:3: warning: implicit declaration of function
      ‘l1if_handle_sysprim’
  sysmo_l1_fwd.c:91:3: warning: implicit declaration of function
      ‘l1if_handle_l1prim’
  sysmo_l1_hw.c:109:3: warning: implicit declaration of function
      ‘l1if_handle_sysprim’
  sysmo_l1_hw.c:118:3: warning: implicit declaration of function
      ‘l1if_handle_l1prim’

Sponsored-by: On-Waves ehf
2015-05-04 10:02:01 +02:00
Jacob Erlbeck 1f33294b1c pcu: Fix non-critical warnings
These fixes do not affect the semantics of the code. They either help
gcc by providing default values that won't be used ("may be
uninitialised"), remove unused variables, or change signed to
unsigned variables to avoid comparison warnings.

Addresses:
  bts.cpp:494:32: warning: 'tbf' may be used uninitialized in this
      function
  emu/test_replay_gprs_attach.cpp:81:27: warning: comparison between
      signed and unsigned integer expressions
  emu/test_pdp_activation.cpp:95:23: warning: unused variable ‘budh’
  emu/test_pdp_activation.cpp:97:6: warning: variable ‘rc’ set but
      not used
  emu/pcu_emu.cpp:109:26: warning: unused variable ‘bts’
  alloc/AllocTest.cpp:74:27: warning: unused variable ‘tbf’
  osmocom/core/utils.h:13:50: warning: comparison between signed and
      unsigned integer expressions
  types/TypesTest.cpp:319:7: warning: unused variable ‘count’
  types/TypesTest.cpp:320:11: warning: unused variable ‘rbb’
  alloc/AllocTest.cpp:74:27: warning: unused variable ‘tbf’
  alloc/AllocTest.cpp:132:11: warning: unused variable ‘ts_no’
2015-05-04 10:01:25 +02:00
Jacob Erlbeck 2acfbebfd3 vty: Fix value range of commands accepting csecs
Currently an uint8_t csec variable is used to temporarily store
the number of centiseconds which can be set within the range
1 - 65535. This way values above 255 cannot be set properly. This
affects the VTY commands "queue lifetime", "queue hysteres", and
"queue idle-ack-delay".

This commit replaces the uint8_t by an uint16_t.

Sponsored-by: On-Waves ehf
2015-05-04 09:58:34 +02:00
Jacob Erlbeck c0c580c414 vty: Fix warnings about undeclared functions
This commit adds missing includes to pcu_vty.c and fixes the
llist_head type (missing struct keyword) used by tbf_print_vty_info.

Sponsored-by: On-Waves ehf
2015-05-04 09:58:34 +02:00
Jacob Erlbeck 56e8d71090 tbf: Remove double assigment to m_last_dl_drained_fn
Currently the value -1 is assigned twice to m_last_dl_drained_fn
within append_data().

This commit removes the first of these assigments.

Sponsored-by: On-Waves ehf
2015-04-30 15:23:16 +02:00
Jacob Erlbeck 00fc6b13f2 pcu: Call bssgp_set_log_ss(DBSSGP) in main()
Currently the BSSGP functions in libosmocore do not log correctly to
DBSSGP since the DBSSGP variable in common_vty.c is left
uninitialized.

This commit adds the call to bssgp_set_log_ss() to inform libosmocore
which sub system id it shall use for BSSGP.

Sponsored-by: On-Waves ehf
2015-04-09 19:26:39 +02:00
Jacob Erlbeck 90de3a7ffe tbf: Send BSSGP LLC discarded on TBI exhaustion
Currently the PCU silently discard LLC frames from the SGSN if a
DL TBF cannot be allocated.

This commit changes tbf_new_dl_assignment and reuse_tbf to send an
LLC discarded message to the SGSN in this case.

Ticket: #607
Sponsored-by: On-Waves ehf
2015-04-09 19:24:47 +02:00
Jacob Erlbeck d58b711eec tbf/test: Add test for DL TBF exhaustion
This test calls gprs_rlcmac_dl_tbf::handle() with varying TLLI and
IMSI until the function fails.

Sponsored-by: On-Waves ehf
2015-04-09 19:24:00 +02:00
Jacob Erlbeck cc12f02658 vty: Use libosmocore VTY standards
This commit applies to following changes to pcu_vty.c:

- Use vty_install_default() instead of install_default()
- Add a blank after the '#' of the prompt
- Rename the 'pcu' configuration node to 'config-pcu'

Sponsored-by: On-Waves ehf
2015-04-09 17:05:19 +02:00
Jacob Erlbeck d0261b72de tbf: Force ACK after the last DL LCC frame has been received
If the protocol layers above LLC (e.g. TCP) need an acknowledgement
to continue, it can take up to 400ms (single TS) until the MS is
polled for Ack/Nack which it can use to request an uplink TBF
quickly. The 400ms result from requesting an DL Ack/Nack every 20 RLC
blocks until all pending LLC frames have been sent.

Especially TCP's slow start mechanism can lead to a high delay at the
start of the connection, since the sender will eventually stop after
having sent the first packets (up to 4 (RFC2581) or 10 (RFC6928)).

This commit modifies append_data() to (re-)start
a timer every time it handles an LLC packet and to request an
Ack/Nack every time it expires. So if the server ceases to send IP
packets, the MS is polled in the assumption, that the server is
waiting for an ACK.

The following VTY commands are added (pcu node):

 - queue idle-ack-delay <1-65535>  timeout in centiseconds
 - no queue idle-ack-delay         disable this feature (default)

A sensible value is 10 (100ms) that at gave promising results when
testing locally.

Sponsored-by: On-Waves ehf
2015-04-02 18:14:08 +02:00
Jacob Erlbeck 0c1c8778df tbf: Use a hysteresis when discarding DL LLC frames
Currently single LLC blocks are discarded when the PDU lifetime
expires. If an IP packet has been fragmented either on the IP or on
the LLC layer and is therefore distributed over several LLC frames,
the kept fragments are transmitted and then discarded by the MS
because of the missing PDU. This can cause massive IP packet loss
when there are many fragmented packets (e.g. when trying 'ping
-s1800' or if the GGSN chops downlink IP packets into several SNDCP
packets).

On the other hand, discarding too many packets might disturb the
congestion handling of TCP. Dropping plain TCP ACKs might also hinder
flow control and congestion avoidance.

This commit adds a hysteresis algorithm to the LLC discard loop. If
an LLC message's age reaches the high water mark, further message's
with an age above the low water mark are discarded, too. This is
aborted, if a GMM, a non-UI, or a small message is detected. In
these cases, that message is kept.

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

- queue hysteresis <1-65535>   set the difference between high
                               (lifetime) and low watermark in
                               centiseconds
- no queue hysteresis          disable this feature (default)

Since the SGSN will most probably send all fragments of a single
N-PDU without much delay between them, a value slightly above the
average transmission delay jitter between SGSN and PCU is probably a
sensible value to discard all fragments of a single IP packet.

This is an experimental feature that might be replaced by more
advanced means of active queue management in the future.

Sponsored-by: On-Waves ehf
2015-04-02 12:34:48 +02:00
Jacob Erlbeck 0a0b5dcb32 pcu: Add pcu_utils.h to Makefile.am
Addresses:
src/gprs_rlcmac_sched.cpp:26:23: error: pcu_utils.h: No such file or directory

Sponsored-by: On-Waves ehf
2015-03-29 13:55:42 +02:00
Jacob Erlbeck 502bd1feea tbf: Poll MS on idle DL TBFs
If an MS wants to open a new UL TBF, it can either use (P)RACH or
request one in a Ack/Nack message for a DL TBF (PACCH). When a TBF
becomes idle (LCC queue is empty but the TBF is kept open), there
aren't any Ack/Nack requests that can be used by the MS to ask for an
UL TBF, therefore it has to use the RACH. This leads to many RACH
requests even for a single HTTP transaction, so it takes some time to
retrieve even a simple web page.

This commit modifies the scheduler to regularly send Ack/Nack
requests on idle DL TBFs. It does so by extending the priority based
scheduling algorithm to have 5 priority levels (highest priority
first):

  - Control block is pending
  - High age (100%) threshold reached (-> request Ack/Nack)
  - Data is waiting or there are pending Nacks
  - Low age (200ms) threshold reached (-> request Ack/Nack)
  - Pending Nacks that have been resent already
  - None of the above (-> send DL dummy control block)

The 'age' refers to the time since since the last control block has
been sent on the TBF. This high age threshold is set to
dl-tbf-idle-time or to 50% of T3190 (whichever is smaller), aiming
for at least a poll (and TBF shutdown) after the TBF has expired and
to safely prevent expiry of T3190. So if dl-tbf-idle-time > 200ms,
there will be a poll every 200ms and a final poll after
dl-tbf-idle-time. On high load, the interval between polls can get
higher, but the 'high age' poll should be in place.

This commit implements the scheduling with respect to GSM 44.060,
9.3.1a ("Delayed release of downlink TBF").

Ticket: #556
Sponsored-by: On-Waves ehf
2015-03-25 12:34:38 +01:00
Jacob Erlbeck e25b5b91f6 tbf: Only create dummy frames if necessary
Currently a lot of LLC dummy commands will be generated while waiting
for an ACK for the DL TBF, even if there are blocks that could be
resent instead.

This patch modifies create_dl_acked_block to only call
create_new_bsn() if there is unsent LLC data or m_window is empty.
If the TBF is in state FLOW, no unsent LLC data is left, but there
are blocks left in m_window, those are resent instead.

Sponsored-by: On-Waves ehf
2015-03-25 12:34:38 +01:00
Jacob Erlbeck 2cbe80b53e tbf/test: Add test for delayed release
This tests checks the implementation of the delayed release of an
downlink TBF.

Ticket: #556
Sponsored-by: On-Waves ehf
2015-03-25 12:34:34 +01:00
Jacob Erlbeck 3bed5d11d2 tbf: Implement delayed release of a downlink TBF
Currently a DL TBF is immediately closed, when the LLC queue is
drained. This will lead to a new DL assignment if data is received
afterwards. In addition, it is not possible to keep the PACCH open
to poll the MS for UL establishment requests there.

GSM 44.060, 9.3.1a suggests to delay the release of an inactive TBF
for some time (max 5s).

This commit mainly changes create_new_bsn() to send LLC dummy
commands as filler if no LLC data is available until keep_open()
returns false. The keep_open() functions returns true unless a
configurable time has passed after the LLC data store drained. By
default, that time is not set which causes keep_open() to always
return false, so that delayed release is effectively disabled.

The following VTY commands are added:
  - dl-tbf-idle-time <1-5000>    to set the delay in ms
  - no dl-tbf-idle-time          to disable delayed release

Ticket: #556
Sponsored-by: On-Waves ehf
2015-03-25 12:32:35 +01:00
Jacob Erlbeck cbb1e70554 tbf: Insert LLC dummy command if no frame is available
If a BSN is going to be created but there is no frame stored in
m_llc, an empty LLC message would be created. This shouldn't happen
currently, but this will be a common case, when delayed TBF release
is implemented.

This commit changes create_new_bsn() to create an LLC dummy
command in that case and to put it into the frame buffer.

Sponsored-by: On-Waves ehf
2015-03-25 12:21:55 +01:00
Jacob Erlbeck c495209122 tbf: Use put_frame in append_data if the TBF has no data
Currently if append_data() is used when there is no LLC data in the DL TBF,
it will either call reuse_tbf() which in turn will call put_frame(),
or it will append the LLC message to the queue, even if the queue and
the frame buffer are empty. This only happens with the test case so
far, but this would change when idle DL TBFs are kept open for some
time. It results in empty LLC message being sent to the MS (see log
below).

This commit changes append_data to check for this case and to
eventually use put_frame() instead of appending the LLC data to the
queue.

Addresses:
  TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=FLOW) downlink (V(A)==0 ..
  V(S)==0)
  - Sending new block at BSN 0
  -- Chunk with length 0 is less than remaining space (20): add length
    header to to delimit LLC frame
  Complete DL frame for TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=FLOW)len=0
  - Dequeue next LLC for TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=FLOW)
    (len=200)

Sponsored-by: On-Waves ehf
2015-03-25 12:12:29 +01:00
Jacob Erlbeck 005ee7f862 tbf: Add frames_since_last_poll method
This functions calculates the number of frames that have passed since
the last DL poll (RRBP flag set) has been sent. It returns a value
less than zero (fn_now - fn_sched) if the block has been scheduled but
not yet sent.

If the function is called before the first data block has been sent
it will return -1.

If the function is called before the first DL poll is sent, it
returns the number of frames since the first data block has been
sent.

Sponsored-by: On-Waves ehf
2015-03-25 12:12:27 +01:00
Jacob Erlbeck 2493c660e9 tbf/test: Fix fn/block_nr in test_tbf_final_ack
Currently fn and block_nr are not incremented correctly. In addition,
the comments around the sending of blocks are not accurate either.

This commit introduces the send_rlc_block helper function which takes
care of the increments, updates the comments to reflect what is
really happening, and adds assertion to verify at least some aspects
of what is now stated in the comments.

Sponsored-by: On-Waves ehf
2015-03-25 12:10:48 +01:00
Jacob Erlbeck a3e4509ff9 tbf/test: Put BTS setup and DL TBF creation into helper functions
Put the generic parts of test_tbf_final_ack into helper functions to
reduce the size of the test and to avoid too much code duplication
when creating new tests.

Sponsored-by: On-Waves ehf
2015-03-25 12:10:48 +01:00
Jacob Erlbeck eceb910fef tbf: Add helper functions for DL TBFs
Some properties of a DL TBF are explicitly calculated within modules
using DL TBFs.

This commit introduces the methods need_control_ts(), have_data(),
is_control_ts() to hide internals of the DL TBF implementation.

Sponsored-by: On-Waves ehf
2015-03-25 12:10:45 +01:00
Jacob Erlbeck 95340242ed tbf: Refactor create_dl_acked_block
Turn the big nested if statement into a sequence of smaller ones. The
call to create_new_bsn is moved upwards.

Sponsored-by: On-Waves ehf
2015-03-20 14:32:20 +01:00
Jacob Erlbeck 612e93e360 llc: Add put_dummy_frame to create an LLC dummy command
The LLC dummy command is needed for RLC block stuffing, e.g. when a
TBF should be kept open if no LLC data is available. The RLC block
headers do not support stuffing, only the last block of a TBF can be
used partially. LLC dummy commands are discarded by the receiver
immediately, because the have an invalid FCS checksum.

This commit adds the function put_dummy_frame, which puts a LLC dummy
command into the frame buffer. The requested length is given as an
argument, but the real length might be adjusted according to the
specification (see GSM 44.064, 6.4.2.2).

Sponsored-by: On-Waves ehf
2015-03-20 14:12:28 +01:00
Jacob Erlbeck 39645b824a bssgp: Handle BSSGP STATUS messages
Currently incoming BSSGP STATUS messages are just logged and apart
from that ignored. Since it is possible that the gbproxy can
eventually send both valid replies (from the primary SGSN) and
STATUS(unknown BVCI) messages (from the secondary SGSN). Since the
PCU assumes in this case that the BVC has been successfully reset and
unblocked, it will not send a BVC RESET message after receiving an
NS_UNBLOCK.

This commit changes gprs_bssgp_pcu_rcvmsg to pass BSSGP STATUS
messages to bssgp_rcvmsg() which will in turn call bssgp_prim_cb()
with primitive NM_STATUS. Then the BVC RESET or UNBLOCK procedure
will be started if the IE in the message matches and the PCU assumes
that the BVC should have been successfully reset and unblocked
respectively while the STATUS message tells otherwise.

Note that bssgp_rcvmsg() from libosmocore is otherwise used for the
SGSN side only, albeit it generally just decodes messages, does basic
protocol handling and eventually invokes PRIM indications. The only
exception here is the handling of BVC RESET, which is implemented
specifically for the SGSN.

Ticket: OW#1414
Sponsored-by: On-Waves ehf
2015-03-17 10:44:07 +01:00
Jacob Erlbeck 1e96af6325 bssgp: Set blocking and reset timer to 30s
Currently the timer T1 and T2 are hard-coded to 1s which is pretty
low in consideration of a possible high latency connection to the
SGSN.

This commit introduces macros for the timer values and sets them to
30s.

Sponsored-by: On-Waves ehf
2015-03-17 10:43:42 +01:00
Jacob Erlbeck 7c44415d78 tbf: Fix scheduling for DL Ack/Nack request
Currently the DL Ack/Nack is not requested, if the last chunk of the
final LLC frame in the TBF fits exactly into the remaining space of
the RLC block. It is also not requested, if the RRBP flag cannot be
set due to scheduling issues (single block allocation, polling
already pending for this TBF, ...). So up to POLL_ACK_AFTER_FRAMES
(20) RLC data blocks will have to be resent, before requesting the
Ack/Nack will be retried.

This commit removes the first_fin_ack parameter of
create_dl_acked_block entirely and adds a request_dl_ack() method
that should be called, when the TBF's state changes to FINISHED. This
request will be reset, when the block has been scheduled
successfully. Since the first_fin_ack hasn't been set if chunk ==
space, calling request_dl_ack() on both places in create_new_bsn()
when the state is changed to FINISHED will also fix the first issue
described above.

Note that DL scheduling might not be fair concerning access to
first_ts when multiple TS are used for a TBF. In theory, a TBF might
never get access to first_ts in the worst case.

Sponsored-by: On-Waves ehf
2015-03-12 18:08:18 +01:00
Jacob Erlbeck 0eabffdc35 sched: Modify DL scheduling to use different priorities
Currently the DL blocks are scheduled round robin to each TBF that is
either in state FLOW or FINISHED and not waiting for an IMM.ASS
confirmation. This way, if single blocks has been NACK'ed by the MS
and the PCU has already resent the missing packets, the PCU starts
retransmitting them until it has received an ACK/NACK even if other
TBF have RLC blocks that need to be transmitted.

This commit changes sched_select_downlink to select the next TBF with
the highest priority, where blocks that are going to be resent again
have a lower priority unless the window is stalling. If there is only
one TBF the old behaviour is kept, since there is no other TBF that
can have a higher priority.

If there is much packet loss on a single phone, this modification can
lead to a higher latency for that MS.

Sponsored-by: On-Waves ehf
2015-03-06 19:28:10 +01:00
Jacob Erlbeck 1842c921b3 tbf: Reduce m_new_tbf logging messages
Currently tbf->m_new_tbf may point to itself if no new TBF is
assigned. But this leads to additional logging messages, since the
code in set_new_tbf and tbf_free assumes, that a real new TBF is
assigned and generates log messages accordingly.

This commit adds checks to avoid those messages in the above case.

Sponsored-by: On-Waves ehf
2015-03-06 19:28:08 +01:00
Jacob Erlbeck adcdf150a6 tbf: Add name() method and put the buf into the tbf
Currently tbf_name() must not be used twice in a printf statement
with different TBFs, since the same baffer will be used for each.

This commit puts the text buffer into struct gprs_rlcmac_tbf to avoid
this problem.

Sponsored-by: On-Waves ehf
2015-03-06 19:25:53 +01:00
Jacob Erlbeck a41a71e2d4 pcu: Fix log message
This commit adds a missing LF to a log message.

Sponsored-by: On-Waves ehf
2015-02-26 16:24:32 +01:00
Jacob Erlbeck 297edf754f tbf: Don't use 'old' DL TBFs after reuse_tbf
Currently two DL TBF objects with the same TLLI exist after reuse_tbf
which make the result of tbf_by_tlli undefined. This leads to several
DL TBFs belonging to the same TLLI.

This commit extends tbf_by_tlli to check the m_tlli_valid flag for
DL, too. That flag is set to 0 in reuse_tbf to mark the 'old' DL TBF
as invalid after its LLC data has been copied to the new one.

Sponsored-by: On-Waves ehf
2015-02-26 16:22:34 +01:00
Jacob Erlbeck 08fe76a893 tbf: Fix dangling m_new_tbf pointer
Currently if a 'new' TBF is freed before the 'old' one (where
old_tbf->m_new_tbf == new_tbf), the old_tbf->m_new_tbf is not cleared
and can be accessed later on. This can lead to inconsistencies or
segmentation faults.

This commit adds m_old_tbf which points back from new_tbf to old_pdf.
m_new_tbf and m_old_tbf are either both set to NULL or one is the
reverse pointer of the other (tbf->m_new_tbf->m_old_tbf == tbf and
tbf->m_old_tbf->m_new_tbf == tbf). It extends set_new_tbf and
tbf_free to update the pointee accordingly.

The TBF test is extended to check this invariant at several places.

Sponsored-by: On-Waves ehf
2015-02-23 15:10:20 +01:00
Jacob Erlbeck 5e9f40d3d9 tbf/test: Modify test to create a dangling TBF pointer
When new_tbf is freed before dl_tbf in test_tbf_final_ack, dl_tbf
still contains a pointer to it in m_new_tbf.

This patch changes the test to accept a test mode parameter and runs
it twice which a different order of tbf_free in each run. Consistency
checks are added, to check for a danglilng m_new_tbf pointer in both
cases.

Sponsored-by: On-Waves ehf
2015-02-23 15:07:06 +01:00
Jacob Erlbeck 18fef10641 tests: Fix library link order
Currently libgprs.la is listed after libosmocore.so which leads to
link errors when compiling under Ubuntu with the system build tools
(no cross compiling).

This patch moves libgprs.la in front of the external libs.

Sponsored-by: On-Waves ehf
2015-02-23 14:17:01 +01:00
Holger Hans Peter Freyther f5c97476de tbf: Fix dereference before null check
m_new_tbf might be NULL. Assign was_releasing _after_ we have
done the NULL check.

Related: Coverity CID#1238847
2014-12-26 18:32:00 +01:00
Holger Hans Peter Freyther 49f26bf6e8 Fix VTY documentation
Documentation error (missing docs):
<command id='show tbf all'>
        <param name='all' doc='(null)' />
2014-12-20 15:22:09 +01:00
Daniel Willmann 07e39302ec tests/tbf: Commit TbfTest.err *with* whitespace "errors"
Otherwise the diff and hence the test will fail...
2014-09-19 12:01:39 +02:00
Daniel Willmann 0f58af6627 tests/tbf: Use correct function to enqueue llc data
The test called the llc enqueue() function directly which didn't take
care of prepending the tv values for the timeout to the data.

Now the test uses dl_tbf.append() which takes care of prepending the tv
values. With this patch make distcheck on jenkins should no longer fail
with "Discarding LLC PDU because lifetime limit reached." messages.
2014-09-19 11:57:21 +02:00
Daniel Willmann 635d47c78c tbf_dl: Fix warnings
Remove unused log argument (cnp bug) and unused variable
2014-09-17 17:59:03 +02:00
Daniel Willmann 1c15573012 poll_controller: Be stricter with the timeout handling
There is no reason to believe that an ACK that was polled for FN X and
has not arrived on the next frame will arrive sometime after.

Ticket: SYS#382
Sponsored-by: On-Waves ehf.
2014-09-10 19:19:58 +02:00
Daniel Willmann efd5dbb168 tbf: Handle DL ACK/NACK poll timeout correctly
If the PCU didn't receive the downlink ack/nack when it was polled there
is no reason to assume it will arrive at a later time. In that case
N3105 is already increased, but the next block sent should have a valid
RRBP set as well to poll again.
This can happen if either the BSN with the valid RRBP gets lost on the
downlink or the DL ACK/NACK gets lost on the uplink path.

Ticket: SYS#382
Sponsored-by: On-Waves ehf
2014-09-10 19:19:58 +02:00
Daniel Willmann 510d7d36ed tests/tbf: Test for final ack issue
Ticket: SYS#382
Sponsored-by: On-Waves ehf
2014-09-10 19:19:58 +02:00
Daniel Willmann e481815583 tbf,bts: Keep track of new TBF for dl/ul assignment in m_new_tbf
There are a couple of possibilities where one TBF is used to assign a
new one:

1. Assign a DL TBF from a UL TBF
2. Assign a UL TBF from a DL TBF
3. Assign a DL TBF from a DL TBF which is in wait-release state (T3193 is
   running)

In these cases the assignment is sent on the existing TBF and triggers
the assignement of the new TBF (with different TFI/direction).

The current code detects these situations by looking at dl/ul_ass_state
and then chosing the TBF with the opposite direction (DL/UL) that has
the same TLLI. This does not work in the case 3 above where a new DL TBF
is triggered for a DL TBF. The current code reuses the old TBF (and
TFI), but this violates the spec.

This patch introduces a m_new_tbf member which is set to the new TBF to
be assigned. When receiving a control ack the code looks up the
n_new_tbf member of the tbf that requested the control ack and completes
the ul/dl assignment. If the old TBF was in the wait release state
(T3193 is running) it is released.

From 3GPP TS 04.60 9.3.2.6:
"""
If the network has received the PACKET DOWNLINK ACK/NACK message with
the Final Ack Indicator bit set to '1' and has new data to transmit for
the mobile station, the network may establish a new downlink TBF for the
mobile station by sending the PACKET DOWNLINK ASSIGNMENT or PACKET
TIMESLOT RECONFIGURE message with the Control Ack bit set to '1' on
PACCH. In case the network establishes a new downlink TBF for the mobile
station, the network shall stop timer T3193.
"""

reuse_tbf() is modified to allocate a new TBF with a new TFI and trigger
a dl assignment for that TBF on the old TBF. All pending data is moved
to the new TBF.

Ticket: SYS#382
Sponsored-by: On-Waves ehf
2014-09-10 19:04:46 +02:00
Daniel Willmann 08e57c8366 tbf: Include TBF state in tbf_name output
Sponsored-by: On-Waves ehf
2014-08-15 18:28:45 +02:00
Daniel Willmann 341689d6c5 TbfTest: Make logging more verbose
Sponsored-by: On-Waves ehf
2014-08-15 18:28:45 +02:00
Holger Hans Peter Freyther ad1fcccf60 Merge branch 'sysmocom/tbf-split' 2014-08-12 19:25:32 +02:00
Daniel Willmann ca102af92b tbf: Split out UL/DL TBF methods into separate files
tbf_ul.cpp for UL TBF methods
tbf_dl.cpp for DL TBF methods

This commit contains no code changes.

Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-12 19:24:49 +02:00
Daniel Willmann eb100244b0 tbf, bts: Use tbf set_state method instead of tbf_new_state function
All the function did was add debug output and call the set_state method.
Move the debugging into the method and remove the function.

Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-08 14:15:24 +02:00
Daniel Willmann 418a4230e0 tbf, gprs_rlcmac_meas: Move the DL bandwidth variables to the DL TBF
The bandwidth calculation as well as loss report is only done for DL TBF
so move everything related to that in there.

Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-08 14:15:24 +02:00
Daniel Willmann cf706b0775 tbf: Make llc_dequeue a method of DL TBF
llc_dequeue is only used in DL TBF to send the data from the BSSGP to
the MS.

Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-08 14:15:24 +02:00
Daniel Willmann e2732e2f59 tbf: Make snd_ul_ud() and assemble_forward_llc() methods of UL TBF
They are only called for UL TBF.

Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-08 14:15:24 +02:00
Holger Hans Peter Freyther f72fcfe219 Merge remote-tracking branch 'origin/sysmocom/tbf-split'
the TBF handling in osmo-pcu mixes UL and DL TBFs together. They
are quite different, however, and should be treated differently.

This patch series continues the work of splitting up UL and DL TBFs
into their own classes and explicitly using a DL TBF or UL TBF
instead of the base class in those parts of the code that deal
with only one variant.

I didn't test every single commit, but each should compile and
the result passes make check as well as our iperf test against
a GSM modem.

[Text from Daniel's cover mail]
2014-08-08 08:49:03 +02:00
Daniel Willmann 7e994e392d tbf, ...: Make the fields in the dl/ul struct member variables
There is no need for the union/struct anymore. Make the variable members
of the UL/DL class.

As a result gprs_rlc_dl_window gets a reset() method because
memset(&dir.dl, 0, sizeof(dir.dl)) doesn't work anymore in reuse_tbf().

Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-07 16:12:05 +02:00
Daniel Willmann 6a8a1dcda2 tbf: Move the dir.dl/ul members out of the base class into DL/UL TBF
Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-07 16:12:05 +02:00
Daniel Willmann d1d1633121 tbf: Remove unused function sns()
The current code uses dir.*l.window.sns() directly. The current
implementation only returns the dl.window.sns() which will cause
problems.
Remove now and re-add it as a method of DL/UL TBF if and when it is
needed.

Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-07 16:12:05 +02:00
Daniel Willmann cd44ec41c5 gprs_rlcmac_ts_alloc: Be explicit about which TBF is used
Use UL/DL TBFs instead of the base class wherever it is clear that the
code only deals with one kind of TBF.

Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-07 16:12:05 +02:00
Daniel Willmann f55e58f5cf encoding: Change function signature to use UL TBFs where it makes sense
encode_rbb() and write_packet_uplink_assignment are only called with UL
TBFs so change the function signature to reflect that.

Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-07 16:12:05 +02:00
Daniel Willmann 350f64d9e2 tbf: Move UL TBF methods from base class into UL TBF
The methods create_ul_ack(), rcv_data_block_acknowledged(),
maybe_schedule_uplink_acknack() are only used for UL TBFs so make them
methods of that class instead of the base class.

Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-07 16:12:05 +02:00
Daniel Willmann 4f3c420852 gprs_rlcmac_sched: Use UL/DL TBFs in scheduler
Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-07 16:12:05 +02:00
Daniel Willmann b2886f1a0d tbf: Split UL and DL TBFs up in create_dl_ass()
Explicitly use UL and DL TBFs where they are needed.

Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-07 16:12:05 +02:00
Daniel Willmann 93d1711011 poll_controller: Use DL/UL TBFs in PollController::expireTimedout()
Be specific about where we are handling an UL and DL TBF

Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-07 16:12:05 +02:00
Daniel Willmann 6a16e0c092 tbf: Use DL/UL TBFs in poll_timeout codepaths that deal only with one type
Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-07 16:12:04 +02:00
Daniel Willmann 351a573396 tests/alloc: Use the specific UL/DL TBF classes instead of the base
Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-07 16:12:04 +02:00
Daniel Willmann 3016888ee0 tbf, gprs_bssgp_pcu: Move some methods to DL TBF
These methods are only used on DL TBFs and can be moved to the subclass.

Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-07 16:12:04 +02:00
Daniel Willmann 532a4b54f5 bts: Change parameter in BTS::trigger_dl_ass() to DL TBF
This method is always called with a DL TBF as argument so make it clear.

Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-07 16:12:04 +02:00
Daniel Willmann 1dac2ebb71 bts: Make use of DL TBF explicit in rcv_imm_ass_cnf()
Ticket: SYS#389
Sponsored by: On-Waves ehf
2014-08-07 16:12:04 +02:00
Daniel Willmann b8f260176e tbf: Make create_new_bsn and create_dl_acked_block a method of DL TBF
These functions are only used for DL TBFs so move them.
sched_select_downlink() in src/gprs_rlcmac_sched.cpp now needs to deal
with DL TBFs instead of the base class.

Ticket: SYS#389
Sponsored-by: On-Waves ehf
2014-08-07 13:03:10 +02:00
Daniel Willmann 6c813fc9bc bts, tbf: Make rcvd_dl_ack a method of the DL TBF
This method is only userul for DL TBFs so move it. As a result
gprs_rlcmac_pdch::rcv_control_ack needs to work with dl_tbfs.

Ticket: SYS#389
Sponsored-by: On-Waves ehf
2014-08-07 13:03:10 +02:00
Daniel Willmann 538ac5b574 tbf: Make append_data a function of DL TBFs
This function is only used in DL TBFs (called by handle).

Ticket: SYS#389
Sponsored-by: On-Waves ehf
2014-08-07 13:03:09 +02:00
Daniel Willmann 2354402b7a tbf: Make tbf_lookup_dl() return and handle() use a dl_tbf
These two functions only deal with DL TBFs so make it clear

Ticket: SYS#389
Sponsored-by: On-Waves ehf
2014-08-07 13:03:09 +02:00
Daniel Willmann 057c285cd7 bts: Remove the OSMO_ASSERTs for TBF direction
llist_add is called on the TBF lists in tbf_alloc_ul/dl_tbf or in
rotate_in_list. All three places check the direction/add the new TBF to
the correct list so an ASSERT on entry is not needed.

Ticket: SYS#389
Sponsored-by: On-Waves ehf
2014-08-07 13:03:09 +02:00
Daniel Willmann 1b3864fc47 tbf: Use plain old data structure (PODS) for llist management
The PODS struct has a back pointer to access the actual object.
llist_pods_for_each_entry traverses the list of struct llist_pods and
makes the entry available (through the back pointer).

Ticket: SYS#389
Sponsored-by: On-Waves ehf
2014-07-30 18:27:30 +02:00
Holger Hans Peter Freyther 4bbe3349c2 Merge branch 'sysmocom/tbf-split'
We start to separate the UL and DL part of the TBF now as they
really behave differently. One works on RLCmac frames (plural)
and sends them, the other is just assembling one of them. The
code will become more clear now.
2014-07-22 12:37:18 +02:00
Daniel Willmann 48aa0b0d99 bts, tbf: Split alloc_tbf function into separate UL and DL versions
UL and DL tbfs are used in very separate parts and are not the same
thing so split the alloc function and use the UL/DL version throughout
the code.

Ticket: SYS#389
Sponsored-by: On-Waves ehf
2014-07-22 12:36:31 +02:00
Daniel Willmann 0d12a2fa89 bts, tbf: Change the TBF return type of functions to the ul/dl version
Many functions only ever deal with or return a UL or a DL TBF.
Explicitly change the type to reflect which TBF is used where.

Ticket: SYS#389
Sponsored-by: On-Waves ehf
2014-07-22 12:36:13 +02:00
Daniel Willmann fe6e2e4a08 bts: Return the special type for {ul,dl}_tbf_by_* functions
Start returning the special type instead of the base gprs_rlcmac_tbf
when retrieving a TBF.

Ticket: SYS#389
Sponsored-by: On-Waves ehf
2014-07-16 22:10:17 +02:00
Daniel Willmann 078bb713e1 tbf: Add ul and dl TBF types and allocate them in tbf_alloc()
Ticket: SYS#389
Sponsored-by: On-Waves ehf
2014-07-16 19:03:36 +02:00
Daniel Willmann 2207c5e4ef bts: Ensure tbf direction with OSMO_ASSERT()
In the lookup functions make sure that we are actually returning tbfs
with the expected direction.

Ticket: SYS#389
Sponsored-by: On-Waves ehf
2014-07-16 19:03:36 +02:00
Daniel Willmann febf1a0ac9 bts: Split tbf_by_poll_fn into separate dl and ul functions
rcv_control_dl_ack_nack is only meaningful for dl tbf while
rcv_control_ack can be sent in response to a dl or ul tbf. So
rcv_control_ack still needs to check for ul and dl tbfs.

Ticket: SYS#389
Sponsored-by: On-Waves ehf
2014-07-03 15:36:52 +02:00
Daniel Willmann 54044b0635 bts: Separate functions for dl/ul tbf_by_tfi lookup
Ticket: SYS#389
Sponsored-by: On-Waves ehf
2014-07-03 15:36:52 +02:00
Daniel Willmann b59d61b4b4 bts, tbf: Separate functions for dl/ul tbf_by_tlli lookup
In the future we want to separate ul and dl tbf into different
classes that inherit from a common base.

Ticket: SYS#389
Sponsored-by: On-Waves ehf
2014-07-03 15:36:46 +02:00
Holger Hans Peter Freyther e8915b9d9d misc: Add missing include for the rate counter
pcu_vty.c:285:2: warning: implicit declaration of function 'vty_out_rate_ctr_group' is invalid in C99 [-Wimplicit-function-declaration]
        vty_out_rate_ctr_group(vty, "", bts_main_data_stats());
        ^
2014-07-02 14:57:47 +02:00
Holger Hans Peter Freyther 2c98f1db78 misc: Add missing include for atoi
pcu_vty.c:128:21: warning: implicit declaration of function 'atoi' is invalid in C99 [-Wimplicit-function-declaration]
        bts->fc_interval = atoi(argv[0]);
                           ^
2014-07-02 14:56:36 +02:00
Holger Hans Peter Freyther c421e8aaf2 misc: Add {} to avoid nested if/if/else ambigiouty
pcu_vty.c:89:3: warning: add explicit braces to avoid dangling else [-Wdangling-else]
                else
2014-07-02 14:55:17 +02:00
Holger Hans Peter Freyther 35cc1c0ff3 misc: Do not mix struct/class in the forward declaration 2014-07-02 14:48:44 +02:00
Holger Hans Peter Freyther f3405e5b03 sysmobts: Add a hot-fix to avoid dumping -1 amount of data 2014-06-16 21:51:36 +02:00
Daniel Willmann 6d8884de49 Always exit and don't try to recover
The current code tries to recover from dropped connections and resets the
pcu state so it can keep running. However, this never worked correctly
which is why the -e option is used. This option exits the pcu as soon as
the internal state needs to be reset.

This patch removes this option and makes this behaviour default.

Ticket: SYS#390
Sponsored-by: On-Waves ehf
2014-06-15 19:28:51 +02:00
Daniel Willmann 77e58f602d bts: Remove outdated comment
There is no parameter in the function and the bts has a memeber trx
which has a member pdch.

Sponsored-by: On-Waves ehf
2014-06-04 19:01:56 +02:00
Daniel Willmann 5d77f14904 csn1: Add spaces between string literal and identifier
This is required by C++11

csn1.cpp:1147:44: error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal]
LOGPC(DCSN1, LOGL_NOTICE, "%"PRIu64"|", bitvec_read_field(vector, readIndex, bits_to_handle));

Sponsored-by: On-Waves ehf
2014-06-04 17:17:48 +02:00
Daniel Willmann 17a1d5e162 gprs_rlcmac_pdch: Get rid of ul/dl_tbf
The current code keeps a reference to all tbfs in the bts and another
reference in the pdch. This allows for the possibility of both lists to
go out of sync.

This patch removes the pdch-specific list of ul and dl tbfs and uses the
lists in the bts to lookup tbfs everywhere.

Performance for going through the global list is not an issue yet. We
can optimize this later and in a better way.

Sponsored-by: On-Waves ehf
2014-06-04 17:17:45 +02:00
Daniel Willmann 1e0c61032f gprs_rlcmac_pdch: Don't access private members
This patch introduces methods to get ul and dl tbf by tfi and uses them
in gprs_rlcmac_sched.

Sponsored by: On-Waves ehf
2014-06-04 17:14:22 +02:00
Daniel Willmann cf1fae7f38 tbf: Re-send dl assignment if we can upgrade to multislot
The current code would only ever assign one PDCH for the initial
assignment (from CCCH). Only if reuse_tbf is called the algorithm would
actually use multiple DL PDCHs if possible.

This patch introduced a tbf attribute upgrade_to_multislot that is set
if we have multiple PDCH configured, and support multislot assignment,
but can only assign a single PDCH (alloc_algorithm_b, parameter single
is set). In this case after the assignment completes (and the MS is
listening on a PDCH) we resend a DL assignment though the PACCH and this
time we can assign multiple timeslots.
2014-05-30 18:23:17 +02:00
Daniel Willmann 73191a443f tbf/bts, encoding: Keep track of WAIT_RELEASE state for DL assignment
The current code does not properly distinguish between DL assignments to
reuse a tbf (after it was put in state WAIT_RELEASE) and DL assignments
for an active tbf to change the allocation of the PDCH timeslots.

This patch introduces a new variable was_releasing which remembers if
trigger_dl_ass() was called with a tbf in state WAIT_RELEASE. In that
case we have to set the CONTROL_ACK field in the download assignment.

This should allow us to send DL assignments to change PDCH TS allocation
of a tbf before we enter FLOW state.
2014-05-30 18:21:00 +02:00
Daniel Willmann fc03bbe078 tbf/bts: Rename tbf->snd_dl_ack to tbf->rcvd_dl_ack
This function is called to act upon a received DL ACK packet so this
name makes more sense.
2014-05-30 18:19:06 +02:00
Andreas Eversberg 9167055ed2 Fixed calculation of colliding UL/DL slots in TS allocation algorithm B
Counter j must be wrapped to prevent beeing negative, when it is
initialized. This wrapping happens, if TS 0 is used for PDCH in
combination with regular GPRS phones (MS class 12 or similar).
2014-05-30 18:18:09 +02:00
Holger Hans Peter Freyther 8f3520579a Merge remote-tracking branch 'origin/sysmocom/master' 2014-05-15 12:24:12 +02:00
Holger Hans Peter Freyther f81e2f7621 systemd: Do not re-start in case of exit(1) (e.g. a config issue) 2014-03-21 18:18:36 +01:00
Holger Hans Peter Freyther 59fe8f79cc systemd: Reduce the re-start interval to a couple of seconds
With the exit on failure/disconnect option we restart the PCU a
lot more and might not want to wait five seconds.
2014-02-04 13:20:54 +01:00
Holger Hans Peter Freyther d8f339592d sysmopcu: Re-start the PCU on disconnect
During the refactorings I highlighted that the PCU has two
equally broken clean-up paths.

Fixes: SYS#134
2014-01-21 16:59:33 +01:00
Holger Hans Peter Freyther a09e33cdeb TODO: Update the todolist with some musings... 2014-01-16 10:11:57 +01:00
Holger Hans Peter Freyther e5109ba1f0 tbf: Change the log area to RLCMACDL so we see it in a DL trace 2014-01-16 10:11:22 +01:00
Holger Hans Peter Freyther 3d0cc2f97d tbf: Make finding use-after-free more easy and set to NULL or return
Make finding use-after-free more easy by setting things to NULL
or simply return after tbf_free(this) has been called.
2014-01-16 10:09:42 +01:00
Holger Hans Peter Freyther a004799699 tbf: Use past-tense and call it created_ts 2014-01-16 10:07:20 +01:00
Holger Hans Peter Freyther b293043e2d Merge remote-tracking branch 'sysmocom/tbf-vty' into sysmocom/master 2014-01-16 10:06:22 +01:00
Daniel Willmann 80367aae17 tbf: Save a timestamp at tbf allocation and print it in the VTY 2014-01-15 17:41:45 +01:00
Daniel Willmann 772415fd8a pcu_vty: Add a command to print info about all TBFs 2014-01-15 17:41:45 +01:00
Daniel Willmann afa72f5210 tbf: Add a function to output tbf info to VTY 2014-01-15 17:41:44 +01:00
Holger Hans Peter Freyther 85c1ea5cb6 Merge remote-tracking branch 'sysmocom/window-rework' into sysmocom/master 2014-01-15 17:25:49 +01:00
Daniel Willmann 3ce011f44f rlc: Rename state() to show_state() to better reflect its function
show_state() is only used for debugging output to make the v(b) array
human readable.
2014-01-15 15:23:35 +01:00
Daniel Willmann d54d9f5c75 rlc: Use an enum for the state array instead of chars
gprs_rlc_bsn_state is now used to hold the ACK state of sent/received
rlc packets.
2014-01-15 15:23:35 +01:00
Daniel Willmann 146514e180 rlc/tbf: Move v_b into DL window
Move functions resend_needed(), mark_for_resend(), update(),
move_window(), state(), count_unacked() out of v_b directly into the UL
window and provide a function get_state in v_b to access the v_b
elements.
2014-01-15 15:23:21 +01:00
Daniel Willmann 55844795be rlc/tbf: Add function receive_bsn that updates v_r, v_q, v_n
We don't need to expose the mecanics of updating the variables to the
outside.
2014-01-15 15:23:21 +01:00
Daniel Willmann 7c3751b10b rlc/tbf: Move v_n into gprs_rlc_ul_window and adapt the tests
v_n is part of the UL window handling so move it inside the ul_window
2014-01-15 15:23:21 +01:00
Daniel Willmann f4a1ec6ce7 rlc: Rename the simple raise_v_r method to avoid naming conflicts 2014-01-15 15:23:21 +01:00
Holger Hans Peter Freyther c2fab7a6ff alloc: Update the test result now that everything is back to working again 2014-01-15 14:42:58 +01:00
Andreas Eversberg 0a940087b2 alloc_algorithm_b: Remove obsolete 'i' incrementation from for-loop 2014-01-15 14:33:49 +01:00
Holger Hans Peter Freyther 2bf3446f7a misc: Allow to cross-execute the tests using qemu
When cross-compiling osmo-bts/osmo-pcu one can not easily
execute the testsuite. By adding the OSMO_QEMU variable in
front of the normal execution we can execute the tests. This
should work for native and cross builds.

$ OSMO_QEMU="qemu-arm -L /opt/poky/1.1.2/sysroots/armv5te-poky-linux-gnueabi/" make check
2014-01-15 11:14:52 +01:00
Holger Hans Peter Freyther 746b390201 Merge branch 'sysmocom/allocation-corrections' into sysmocom/master
Merge the refactorings we did at sysmocom and the new test cases that
test the easy success cases of the allocation algorithm. Include the
multislot fixes from Andreas.
2014-01-15 10:39:26 +01:00
Andreas Eversberg 1cd9d886e6 alloc_algorithm_b: Add seperate function to shrink rx window when TS are removed
After reduce_rx_window() and update_rx_win_max() was called, one or more TS
might be removed. tx_win_min and tx_win_max must be adjusted to the new range
of allocated slots.
2014-01-15 10:39:08 +01:00
Andreas Eversberg fe2dcc8aec alloc_algorithm_b: Increment 'i', so allocated TS will not exceed tx_range 2014-01-15 10:39:08 +01:00
Holger Hans Peter Freyther f3eec04655 alloc/test: Add a crazy test that tests each possible combination
Make a crazy test that will test each possible PDCH configuration
and MS Class and verify that the UL/DL assignments work and that
they are on the same first_common_ts.
2014-01-15 10:39:08 +01:00
Andreas Eversberg 765736dc77 alloc_algorithm_b: Do not select uplink slots that cannot be used for downlink
In order to poll MS, the mobile must be able to receive polling request.

In order to allow the MS to transmit, the USF must be received by the mobile.
2014-01-15 10:39:08 +01:00
Andreas Eversberg 7a16d46fdc alloc_algorithm_b: Set tx_range to 8, if all 8 TS are supported by MS 2014-01-15 10:39:08 +01:00
Andreas Eversberg ccde4c462d alloc_algorithm_b: For type 1 MS, limit number of donwlink TS to 5
The algorithm does not support more than 5 TS on downlink for type 1 MS.
Supporting more than 5 TS would require adding more complexity to this
algorithm. MS that support more than 4 (or 5) TS on downlink are rare,
if they really exist.
2014-01-15 10:39:08 +01:00
Andreas Eversberg b03d427b08 alloc_algorithm_b: Correctly increment RX/TX window, even if TS is not useable 2014-01-15 10:39:08 +01:00
Holger Hans Peter Freyther 73193110f2 alloc: Move the uplink ts selection/pre-assignment out of the code
Create a select_ul_slots which is very (95%) similar to the
select_dl_slots handling. This needs to be refactored and the
todo for multiple uplink slots should be handled too.

Add a warning about a potential failure on a busy PCU.
2014-01-15 10:39:08 +01:00
Holger Hans Peter Freyther 1fe69323ad alloc: Move the selection of the first enabled pdch to a new method 2014-01-15 10:39:08 +01:00
Holger Hans Peter Freyther 3fd2ddf1a2 alloc: Move the tx window calculation to a new method 2014-01-15 10:39:08 +01:00
Holger Hans Peter Freyther dd4af8045f alloc: Move upating of the rx window max for Type==1 to a new method 2014-01-15 10:39:08 +01:00
Holger Hans Peter Freyther e45c19b3e8 alloc: Move the collision handling to a new method
The naming of RX/TX still looks fishy and it should use DL/UL instead.
Work on pointers right now. We could introduce a struct with the window
min and max and pass this struct. The usage of 'D' appears wrong in the
ul_usage..
2014-01-15 10:39:08 +01:00
Holger Hans Peter Freyther 882fc9b174 alloc: Move the selection of downlink slots to a new method
This code could evolve into working for Uplink and Downlink. The
only different for the final Uplink assignment is that free USFs
are collected.
2014-01-15 10:39:08 +01:00
Holger Hans Peter Freyther f34f34495b alloc: Simplify the assignment of Trb and Ttb
Put the cases for MS_A/MS_B and MS_A/MS_C together.
2014-01-15 10:39:08 +01:00
Holger Hans Peter Freyther df022f6cb2 alloc: Add a note that the tx window handling differs from rx
It looks like the code is different (without a reason) a good
reason.
2014-01-15 10:39:08 +01:00
Holger Hans Peter Freyther c7b998cc73 alloc/test: Go through all possible ms_classes for the allocation
Generate results for all possible classes.
2014-01-15 10:39:08 +01:00
Holger Hans Peter Freyther 4af30533f0 alloc/tests: Create an allocation test for various scenarious
The allocation in the TBF/BTS code is quite complex. In parts this
is due the assignment and requests occuring under differen circumstances.
Attempt to re-create the commono scenarios.

Remove the bogus msclass check in gprs_rlcmac_tbf::update as the
allocation code will check the ms class anyway.
2014-01-15 10:39:07 +01:00
Holger Hans Peter Freyther f37e514a96 bts: Rename ts and trx to ts_no and trx_no as we operate on number 2014-01-15 10:34:22 +01:00
Holger Hans Peter Freyther 8f399de135 tbf: Kill the tsc member as it duplicates data
We can just use first_ts and the trx/pdch to extract this information.
Avoid duplication of data.
2014-01-15 10:34:09 +01:00
Holger Hans Peter Freyther ba26368040 tbf: Fix typo and call it same 2014-01-15 10:33:56 +01:00
Holger Hans Peter Freyther 6f791d049a sched: Document a possible race condition 2014-01-15 10:33:28 +01:00
Daniel Willmann 6f0796a33a Set csnStream direction *after* csnStreamInit
Fixes a bug introduced in commit 402cdc. That commit sets direction to
zero so setting it to 1 should be done after the call to
csnStreamInit().

This issue was discovered by the rlcmac test.
2014-01-15 10:03:42 +01:00
Daniel Willmann fdcdde2756 Set csnStream direction *after* csnStreamInit
Fixes a bug introduced in commit 402cdc. That commit sets direction to
zero so setting it to 1 should be done after the call to
csnStreamInit().

This issue was discovered by the rlcmac test.
2014-01-15 09:57:07 +01:00
Ivan Kluchnikov 402cdcd02f Fix warnings in gsm_rlcmac.cpp
1. ar.direction variable was not initialized
2. overrunning array "data->RLC_DATA" of 20 bytes at byte offset 22 using index "i" (which evaluates to 22)
2014-01-09 20:01:48 +01:00
Daniel Willmann 2f1974b5ac sysmo_l1_if: Fix off-by-one bug when sending UL messages to gsmtap
The first byte in data_ind->msgUnitParam.u8Buffer is not part of the GSM
frame, but something else, it is also skipped in the call to
pcu_rx_data_ind_pdtch().

This patch fixes GSM RLC/MAC UL packet decoding in wireshark, especially
ACK/NACK packets are now displayed correctly (amongst others).
2014-01-09 20:00:00 +01:00
Ivan Kluchnikov 1b517342ae Fix warnings in gsm_rlcmac.cpp
1. ar.direction variable was not initialized
2. overrunning array "data->RLC_DATA" of 20 bytes at byte offset 22 using index "i" (which evaluates to 22)
2013-12-30 14:56:26 +04:00
Holger Hans Peter Freyther 705653b2d7 sched: Attempt to improve the fairness and schedule UL/AL ACK/ASS
It is possible that certain UL ACK messages are not sent when there
are many many uplink and downlink assignments. Try to be more fair
and schedule them round-robin. This way no starvation should occur.
2013-12-25 15:31:46 +01:00
Holger Hans Peter Freyther aadfc2e121 sched: Remove unused bts parameter from the internal method 2013-12-25 15:19:54 +01:00
Daniel Willmann 9c623892f5 llc: Calculate the average queuing delay of the LLC queues
Use a formula like it is used with TCP. This can help to make
decisions about if frames should be dropped or not at the time
we enqueue them.

This code will store two timeval structs in fron the of the
actual data and compute the average at the time of the dequeue.
2013-12-25 14:55:04 +01:00
Holger Hans Peter Freyther a42b2ad5ed llc: Initialize the LLC frame with garbage to detect wrong usage 2013-12-25 14:27:07 +01:00
Holger Hans Peter Freyther 550bb88a9e llc: Count the number of frames queued inside the LLC queue 2013-12-18 12:11:27 +01:00
Holger Hans Peter Freyther 88553abf4d rlc: Use sizeof() for the memset instead of ARRAY_SIZE
In this case both will give the same result but it is better to use
sizeof. But it is better to use the raw number of bytes instead of
the number of elements.
2013-12-18 12:11:10 +01:00
Henning Heinold c92b964e2d systemd: Add a service for the sysmopcu
Provide a systemd service file for sysmopcu, leaves
the bundled combined sysvinit for sysmobts and sysmopcu
for legacy systems.

Sponsored-by: sysmocom
2013-12-17 19:16:10 +01:00
Holger Hans Peter Freyther b3a87ced5a test: Remove the side-effect from the assertion
Coverity complains about this code as it doesn't know that the
OSMO_ASSERT is always on. But it is good practice to now have the
side-effect in here.

Fixes: Coverity CID 1080724
2013-12-12 14:42:35 +01:00
Holger Hans Peter Freyther 81e8f0a8a2 l1if: Include string.h to fix possible coverity issue
Coverity warns about the below lines of code. strerror is supposed
to return a char* and %s should work on char. Let's see if including
string.h will make coverity more happy. The pre-processes file looked
sane as well.

 LOGP(DL1IF, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
               strerror(errno));

Coverity: CID 1040951, CID 1040950, CID 1040952
2013-12-12 14:32:33 +01:00
Holger Hans Peter Freyther 752a3b2baa Merge branch 'sysmocom/gprs-window-handling' into sysmocom/master
Daniel and me worked on the window handling. We now test that what
we encode is compatible to what we decode. The char array now includes
SSN-1 to the right of the array (reflecting a time axis that grows
towards the right..)
2013-12-12 11:11:02 +01:00
Daniel Willmann 5e94cd4fde decoding: Cosmetic - change rbb decoding 2013-12-12 11:09:17 +01:00
Daniel Willmann cc5a4cbe9b rlc: Make the update loop more understandable
Add bitnum_to_bsn() as a convenience function to get the BSN, use it
in the update handling and ignore rbb for values outside of our tx
window.
2013-12-12 11:07:47 +01:00
Daniel Willmann 48df40d2a4 tests/TypesTest: Check rbb handling in DL 2013-12-12 11:06:40 +01:00
Daniel Willmann f1786a375f tests/TypesTest: Ensure that extract_rbb(encode_rbb(x)) == x 2013-12-12 11:05:43 +01:00
Daniel Willmann 6f7cb2cb4f decoding: Use 'I' and 'R' in rbb array for DL
We want to match up rbb decoding and encoding so it helps to use the
same chars.
2013-12-12 11:03:50 +01:00
Daniel Willmann 0f2b5fc749 tests/TypesTest: Print the result of Encoding::encode_rbb() 2013-12-12 11:03:35 +01:00
Daniel Willmann 5241c1a018 encoding: Factor out encode_rbb to help testing 2013-12-12 11:03:13 +01:00
Daniel Willmann 52ea8a0d87 encoding: Use ul_window ssn()/update_rbb() methods when encoding ul ACK
Use the ssn() and update_rbb() methods of gprs_rlc_ul_window when
encoding the ACK/NACK packet in write_packet_uplink_ack()
2013-12-12 11:02:09 +01:00
Daniel Willmann e6e605ba86 encoding: Change wording to match that of 3GPP TS 04.60
The spec (in ch. 9.1.8.1) speaks of RECEIVED or INVALID in the RLC
receiver, so change the wording in the log message to match.
2013-12-12 11:01:52 +01:00
Daniel Willmann c3f4330fa3 tests/TypesTest: Add OSMO_ASSERT_STR_EQ which prints out the parameters
Use it to compare rbb
2013-12-12 11:01:32 +01:00
Daniel Willmann f86fb7a953 tests/TypesTest: Test ssn() and update_rbb() uplink window methods in TypesTest
Test that ssn and rbb are updated correctly.
2013-12-12 11:01:13 +01:00
Daniel Willmann 8a31f9e016 rlc: Manage the received block bitmap in the ul_window
Added two methods to gprs_rlc_ul_window
* ssn() returns the starting sequence number
* update_rbb() returns an array of chars representing the state of the
  received   block bitmap. Each element is either 'I'nvalid or
  'R'eceived. The rbb is generated from v_n

rbb[63] relates to BSN ssn-1
...
rbb[0] relates to BSN ssn-64
2013-12-12 10:59:56 +01:00
Holger Hans Peter Freyther 11f2d58dbd rlc: Create a testcase for the uplink window 2013-12-04 21:05:18 +01:00
Holger Hans Peter Freyther 3cbf9e040c rlc: Make the RLC types only operate on the BSN
The code has an internal optimization to only use window_size
space. This means that the caller needed to know that only half
of the size was used. Change the API to work on the BSN and do
the mapping internally. The compiler should have plenty of
opportunity to propagate the constant(s) but this has not been
verified.
2013-12-04 21:01:02 +01:00
Holger Hans Peter Freyther 3c95776805 tbf: Separate the handling for rh->si and call in case of re-transmission
In case of a retransmission ack the window again..
2013-12-04 13:51:43 +01:00
Holger Hans Peter Freyther 7a5f3c2153 tbf/sched: We pick the _last_ entry, rotate the lists
We always pick the _last_ entry from the lists. Let's rotate
so we make it a bit more fair.
2013-11-26 21:00:52 +01:00
Holger Hans Peter Freyther 7f3e662b34 tbf/rlc: Move raising of V(Q) into the ul window code 2013-11-26 21:00:51 +01:00
Holger Hans Peter Freyther cbb00ebd04 rlc/tbf: Move the code to raise V(R) into the ul_window handling 2013-11-26 21:00:51 +01:00
Holger Hans Peter Freyther 423dd2286b tbf/rlc: Move the putting of a block into the rlc code 2013-11-26 21:00:51 +01:00
Holger Hans Peter Freyther 4c06d9155c tbf: Move the extraction of the TLLI into a separate method 2013-11-26 21:00:51 +01:00
Holger Hans Peter Freyther c15d5cc230 tbf/rlc: Remove the side-effect from the loop. 2013-11-26 21:00:51 +01:00
Holger Hans Peter Freyther 9977e1545a tbf: Reduce complexity and deal with only one way of assignment
The normal mode of the PCU doesn't really work right now. There
is no place to have certain kind of features that are not built
and not unit-tested.
2013-11-26 21:00:51 +01:00
Holger Hans Peter Freyther 6ab5b24be4 tbf/rlc: Move the check if something is in the window out. 2013-11-26 21:00:51 +01:00
Holger Hans Peter Freyther 7decedcbf8 rlc: The bsn has never been used. Remove it from the Xl_window 2013-11-26 21:00:51 +01:00
Holger Hans Peter Freyther faf3ef45b3 rlc: Add a basic test for the DL Window and moving the window 2013-11-26 21:00:51 +01:00
Holger Hans Peter Freyther e9b1ebba9d rlc: Test the basic of the gprs_rlc_v_n code for remembering the state 2013-11-26 21:00:51 +01:00
Holger Hans Peter Freyther 270f7fce1d tbf/rlc: Move the v_n handling into a dedicated object 2013-11-26 21:00:51 +01:00
Holger Hans Peter Freyther f1593b7c49 tbf/rlc: Big change and move the window handling out to the rlc
The send and receive window is now managed by an external object.
There are some issues that can only be solved with C++11 but it
is progres and removes some of the spaghetti code. For GPRS the
sns and ws is hardcoded. Move that into the window code.
2013-11-26 21:00:51 +01:00
Holger Hans Peter Freyther ab6cd921e3 tbf: Update the legend and mention invalid as well
We should not see it but the legend should be complete.
2013-11-26 20:57:42 +01:00
Holger Hans Peter Freyther 9a76968ec4 rlc: Use uint16_t for the BSN in all cases.
The sns is only 128 for GPRS (it can be bigger for EDGE) so the
uint8_t was enough but it is bad to have inconsistencies for both
of them.
2013-11-26 20:57:40 +01:00
Holger Hans Peter Freyther 9c5539d46d tbf/rlc: Move the counting of unacked frames into the RLC V_B class
Whenwe receive a final ack we assume that all the unacked frames
have been acked now. Move the counting to V_B and now the caller
can remove the BSN and the "lost" variable which has always been
zero.
2013-11-26 20:57:24 +01:00
Holger Hans Peter Freyther 86dc355a33 tbf: Split the handling of DL ACK/NACK into two separate parts
Split the handling of the final ack and the handling of frames
that is moving frames.
2013-11-26 20:57:24 +01:00
Holger Hans Peter Freyther bc15570651 tbf/rlc: Change the code and generate the entire state in the V_B 2013-11-26 20:57:24 +01:00
Holger Hans Peter Freyther e358ff8fa4 tbf/rlc: Move the moving of V(A) into the V_B code
Move the code that moves the V_B to the first not acked frame. Return
how many indexes the V_B was moved and update the V_A in the caller.
2013-11-26 20:57:24 +01:00
Holger Hans Peter Freyther df6b4f52e0 tbf/rlc: Move the parsing of RBB to Decoding, move window marking out
Move the parsing of the bitbmap out of the TBF code into Decoding.
Move the updating of the V_B into the V_B class. Add some comments
about handling the mod_sns, mod_sns_half parameters by using template
code.
2013-11-26 20:57:24 +01:00
Holger Hans Peter Freyther 9eb8ace260 tbf: Fix the indention of this routine 2013-11-26 20:57:24 +01:00
Holger Hans Peter Freyther 12c039cdb2 debug_diagram: Remove the special debug_diagram compilation mode
This approach is somehow flawed. We need/want to debug problems on
systems with real traffic and re-compiling it with debug_diagram
is not an option. All internal logging needs to be expressive enough
so we can understand what is going on (e.g. create a script to
post-process the output).
2013-11-26 20:57:24 +01:00
Holger Hans Peter Freyther a6ba67cb3a tbf: Simplify the create_dl_acked_block into two parts
Separate the resend from creating a new block. It shows that the
first_fin_ack is always set to FALSE for re-sending. This might
not be what we want.
2013-11-26 20:57:24 +01:00
Holger Hans Peter Freyther 8b16ae30fe tbf/rlc: Move the marking of frames for resend into the v_b 2013-11-26 20:57:24 +01:00
Holger Hans Peter Freyther c03e38291a tbf: Move the check if the window is stalled to a separate method 2013-11-26 20:57:24 +01:00
Holger Hans Peter Freyther 1577779526 rlc/tbf: Move the loop into the gprs_rlc_v_b class 2013-11-26 20:57:19 +01:00
Holger Hans Peter Freyther ef93bdb19b tbf: Count how often we re-start a BSN in the send routine
There appears to be a scheduling issue. Even without any NACKs
we are re-transmitting a lot of frames. It might be because of
this.
2013-11-24 00:01:50 +01:00
Holger Hans Peter Freyther 64b49bc3ce tbf: Always increase the tx_counter when we transmit a frame
In case the ACK could not be scheduled we were not increasing the
tx_counter. Change the code flow to always increase the tx_counter
after we have created a frame.
2013-11-23 23:16:07 +01:00
Holger Hans Peter Freyther 40fc8f9e46 sched: Make internal methods static 2013-11-23 23:03:14 +01:00
Holger Hans Peter Freyther b18aedcc50 tbf: Make the ws and sns number "private" (they should also be const) 2013-11-23 16:27:50 +01:00
Holger Hans Peter Freyther 9525567d77 rlc: Create a basic test that mark/is is talking about the same 2013-11-23 16:19:17 +01:00
Holger Hans Peter Freyther 6b5660c19f rlc: Move the rlc headers into a separate header file 2013-11-23 16:19:17 +01:00
Holger Hans Peter Freyther 321f3c3104 rlc: Move the ack state array into a separate class that can be tested 2013-11-23 16:19:17 +01:00
Holger Hans Peter Freyther 092478f294 rlc: Count nacked frames in the statistics too 2013-11-23 01:01:19 +01:00
Holger Hans Peter Freyther 5a7f636ce4 llc: FIx a typo in the message 2013-11-21 21:59:32 +01:00
Holger Hans Peter Freyther c6382ddd6e rlc: Add a simple test for the RLC data structure for the init 2013-11-21 21:42:20 +01:00
Holger Hans Peter Freyther 6058220d2a types: Add a simple testcase for basic types and fix the LLC code
* Make append_data, remaining_space and fits_in_current.. work
  on m_length and not the index. This ways things can't overflow.
* The current API consumer was moving the m_index so it should have
  worked okay.
2013-11-21 21:30:23 +01:00
Holger Hans Peter Freyther 58db60c68e rlc: Start to move things out of the tbf into the rlc
Add remarks of possible broken behavior inside the tbf
routines. Move the preparation (and init) into the new
rlc.cpp file.
2013-11-19 21:26:43 +01:00
Daniel Willmann c3d5325fc8 tbf: The memory regions overlap so memmove should be used 2013-11-19 21:26:43 +01:00
Holger Hans Peter Freyther c70aae4697 rlc: Count the window stalls on the RLC level 2013-11-19 17:09:37 +01:00
Holger Hans Peter Freyther 937e2a6919 meas: Add a warning for flow control handling here 2013-11-19 17:01:49 +01:00
Holger Hans Peter Freyther e5dc2a0ac5 rlc: Pass the gprs_rlc_data to the assemble function for assembly 2013-11-13 20:17:06 +01:00
Holger Hans Peter Freyther 796270bf83 rlc: Put a block and the length into a separate object
This will ease passing things around.
2013-11-13 20:05:00 +01:00
Holger Hans Peter Freyther 9241fd0957 rlc: Begin to move the rlc block handling into a separate object
The secret of gprs_rlc will be the manipulation of the blocks for
the current window. We might add the window handling in this class
as well.
2013-11-13 19:51:55 +01:00
Holger Hans Peter Freyther e9429b5d3e rlc: Count the sent and resent RLC blocks 2013-11-13 19:36:57 +01:00
Holger Hans Peter Freyther 55cf994c29 tbf: Re-use the code that is re-using the tbf
Address the fixme and re-use the code. Fix the counting in the
final ack case and provide a log message in the other case. Prefix
it with the tbf name
2013-11-13 18:15:45 +01:00
Holger Hans Peter Freyther b1302b083e llc: Move the decision if a frame has expired into the LLC
This way the generation of the expiry information and the check
is at the same place. This should make reading the code more easy.
2013-11-13 17:16:28 +01:00
Holger Hans Peter Freyther 32f9a59ab4 llc: Make the index 'private' by appending a m_ to it.
At some point in the future we can start using the private/protected
keywords in this struct.
2013-11-13 17:14:42 +01:00
Holger Hans Peter Freyther e23102602c llc: Move some more secrets from the TBF into the LLC
Introduce a method to append data to a TBF and then reset the
read pointer when the frame has been sent.
2013-11-13 17:08:07 +01:00
Holger Hans Peter Freyther b3d5ee2934 bts: Count the number of llc frames that were "scheduled" to be sent
This does not mean that they have been successfully transferred
to the SGSN/MS but at least that they have reached a certain point
in the message flow.
2013-11-13 16:43:26 +01:00
Holger Hans Peter Freyther 51e093bd1c llc: Use timeradd and timercmp and fix for < 1s PDU llc timeouts 2013-11-13 15:35:45 +01:00
Holger Hans Peter Freyther 985fd114f2 llc: Change the flow. Exit early for the special case and initialize the tv 2013-11-13 15:19:39 +01:00
Holger Hans Peter Freyther fce431cf3a llc: Move the timeout calculation into the TBF 2013-11-13 15:17:12 +01:00
Holger Hans Peter Freyther aa35ba7584 tbf: Count how often we re-use a TBF that was already being deactivated 2013-11-13 15:02:50 +01:00
Holger Hans Peter Freyther 857281f7ff llc: Remove one of the two variants to put data into the frame 2013-11-13 14:58:19 +01:00
Holger Hans Peter Freyther d26318e4a7 misc: Fix coverity warning about indention
So we had one intended line inside the if for "SMS VALUE" and
at the same time one line with "SMS VALUE" at the same indention.
Assume it is copy and paste and remove the line. Currently we
are only parsing the ms_class so this change should not have any
semantic change.

Fixes: Coverity CID 1058761
2013-11-11 20:28:29 +01:00
Holger Hans Peter Freyther 4e8b50cd8d misc: Fix a potential leak on config parsing
Fixes: Coverity CID 1040949
2013-11-11 20:12:30 +01:00
Holger Hans Peter Freyther 0e0f2f5faf pcu: Address the return without value
Fixes: Coverity CID 1040959
2013-11-11 20:10:04 +01:00
Holger Hans Peter Freyther a6e47c7f54 llc: Add another warning for me to take a look 2013-11-11 20:08:35 +01:00
Holger Hans Peter Freyther 5697b4ccfa tbf: Make sure that tfi is signed so we can make a < 0 check
For the failure check we need this variable to be < 0.

Fixes: Coverity CID 1107941
2013-11-11 16:22:39 +01:00
Holger Hans Peter Freyther acb5427bda llc: Move all direct accesses to the frame into the llc structure
Add some todo items where we could add assertions now that I see
the constraints and invariants of this code.
2013-11-07 08:15:58 +01:00
Holger Hans Peter Freyther be57081721 llc: Move the llc structure to a new header file 2013-11-07 08:02:10 +01:00
Holger Hans Peter Freyther b7840466ce tbf: Split create_dl_acked_block into two methods
The method was more than 300 hundred lines of code. Split the
selection of the index and the creation of the dl_msg.
2013-11-07 07:50:26 +01:00
Holger Hans Peter Freyther a1da251c10 tbf: Move the tbf_handle and tbf_append_data into the class 2013-11-07 07:32:51 +01:00
Holger Hans Peter Freyther 096f6f9f39 llc: Move the llc code out of the tbf.cpp into a new dedicated one 2013-11-07 07:21:06 +01:00
Holger Hans Peter Freyther 758dc12c9f llc: First routines moved from the TBF into the LLC 2013-11-07 07:16:21 +01:00
Holger Hans Peter Freyther 28e5378b55 llc: Begin creating a LLC class and move counts into it.
Begin to move state of the LLC into a separate object. This will
allow to make side-effects more clear and kill some code duplication.
2013-11-06 20:23:56 +01:00
Holger Hans Peter Freyther 9948514086 tbf: Move the tx part into the tbf as it accesses internal data 2013-11-06 20:15:11 +01:00
Holger Hans Peter Freyther 77e05971b4 tbf: Move the llc handling into the tbf (from the bts)
This will be moved to a LLC class in the future but after this
we can make the sns/ws private now and have little to update
outside the tbf.
2013-11-06 19:16:43 +01:00
Holger Hans Peter Freyther bc1626e5de tbf: Update the timing advance for the new tlli based on the old one 2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther b809866be5 tbf: Learn and propagate the TLLI changes due a new P-TMSI
During a routing area update a new P-TMSI was assigned. During
the PACKET CONTROL ACK on the DL we notice the change of TLLI
but didn't propagate this. This means that a Routing Area Update
Complete was only sent after a new RACH request.

Addresses:
<0007> gprs_rlcmac_meas.cpp:103 UL RSSI of TLLI=0x88661bc6: -67 dBm
<0002> bts.cpp:945 Got ACK, but UL TBF is gone TLLI=0xe512eba3
<0007> gprs_rlcmac_meas.cpp:158 DL packet loss of IMSI=274080000004765 / TLLI=0xe512eba3: 0%
<0002> tbf.cpp:668 TBF TFI=0 TLLI=0x88661bc6 T3169 timeout during transsmission
<0002> tbf.cpp:690 - Assignment was on PACCH
<0002> tbf.cpp:694 - No uplink data received yet
2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther 5464c9baf2 tbf: Have one imsi field and assign it through a function
Have one IMSI field per TBF and assign through a function call.
The IMSI should be used to look-up the TBF on the SGSN->PCU
direction.
2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther 34f6e5ebe6 tbf: Make tfi private and update the code
All logging code that used tbf->tfi is now using tbf_name to
print the the TBF. External code is now using tfi() which is
inlined and should result in the same code being generated as
before (+debug code that can be stripped).
2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther 474685e26e tbf: Make the tlli "private" and update the updating code
Now all updates to the tlli/tlli_valid are in one place. If we
implement the policy to update the matching/linked TBF we can
now to do it in a single place. Add a todo item for that as I
am waiting for feedback from the mailinglist.
2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther bd449f57a7 tbf: Create tbf_name and use it in log statements
This is like gsm_lchan_name and should be used in log statements.
This way we can easily change the information that is printed and
we know how to search things. The other part is that direct use
of tfi/tlli is removed which will allow us to make them private
and at the same time start to resolve the "tlli" updated in many
places.

Not old log statements are changed yet. This will done whenever
a bad log statement is seen on the console...
2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther 870c601f1d tbf: TODO:Mark TLLI changes as todo item in the code
The TLLI can change when a new P-TMSI is assigned to the phone,
e.g. during a (periodic) routing area update. When the TLLI
changes we need to update all TBFs and maybe even register the
timing advance for the new TLLI..
2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther 6b88cddc21 bts: Simplify rcv_resource_request, remove logically dead code
tbf = find
 if (tbf) {
   tbf_free(tbf);
   tbf = NULL;
 }
 if (!tbf) {
   code...
 }

Remove the if (!tbf) and indention as the tbf is always to be
NULL.
2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther 8021644e9d bts: Kill dead stores of the tlli/tfi, fix the log message again
The code meant to print the tfi and not the tlli. Update it.
2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther ec80f82824 tbf: Remove the trx_no field from the tbf, go through the trx object 2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther fc498c9e7b tbf: Go through the trx to get the ARFCN 2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther 875fc895a8 bts: Further logging improvements for TFI/TLLI output 2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther e1a075ab59 bts: Pass the Packet_Control_Acknowledgement_t into the recv method 2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther 53a336f0e5 bts: Log the TLLI inside the report we get 2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther 8d0e489484 tbf: Log the TFI and TLLI so we can try to figure out more from logs
<0002> tbf.cpp:444 Poll timeout for DL TBF=0
<0002> tbf.cpp:513 - Timeout for polling PACKET DOWNLINK ACK.
<0002> tbf.cpp:688 - Assignment was on PACCH
<0002> tbf.cpp:694 - Downlink ACK was received
<0008> gprs_bssgp_pcu.cpp:154 LLC [SGSN -> PCU] = TLLI: 0xd6942c78 IMSI: 274080000004765 len: 506
<0002> bts.cpp:974 PACKET DOWNLINK ACK with unknown FN=2213128 TFI=0 (TRX 0 TS 6)
2013-10-30 21:24:13 +01:00
Holger Hans Peter Freyther 15bb1a2a51 misc: Change the logging for the tbf and what to log
TLLIs got printed as TBF. Fix that but also rename things to
TFI. The TFI is not required to be unique per BTS but it is
the indicator we use right now.
2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 5da2014f13 bts: Use Packet_Downlink_Ack_Nack_t as parameter 2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 7a344716a6 bts: Simplify the code and use Packet_Resource_Request_t* 2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther cb5c49b581 bts: Work with the Packet_Measurement_Report_t 2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 750ca67ce9 bts: Move the MT_PACKET_MEASUREMENT_REPORT handling to a new method 2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 842808848c bts: Move handling of MT_PACKET_RESOURCE_REQUEST to a method
Move the code to a new method
2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 5a9658168a bts: Move handling of MT_PACKET_DOWNLINK_ACK_NACK to separate function
Kill the tlli assignment as it is never used.
2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 396f4161cb pdch: Move handling of control_ack to a separate method
Kill the unused tfi parameter
2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther c1ae22694c bts: Count the rach frames we receive 2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 90b87ea5e6 misc: Fix typo.. resource in english only has one 's' 2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 180def907b bts: Print the kind of message type not known. 2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 1997787c52 llc: Count timedout and silently dropped frames
A DL tbf can be discarded and then the already queued LLCs will
be silently dropped. Count this event.
2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther b98dd9e240 sba: Move freeing a sba into a central place 2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 93e048fe27 sba: Count SBA allocation, frees and timeouts
Add a warning about the receive message poking in the internal
of the sba. This will be cleaned up in a follow up commit
2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 158776411b bts: Provide the first set of counters 2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther f537298cca bts: Start creating statistics inside the BTS code 2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 61a0a04d26 tbf: Move gprs_rlcmac_send_data_block_acknowledged into tbf
We can now remove the gprs_rlcmac_data as the code has been
moved into the tbf, pdch and bts.
2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 3dc56a3b34 tbf: Move gprs_rlcmac_send_packet_uplink_assignment into the tbf 2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther ae03f22199 tbf: Move gprs_rlcmac_send_packet_uplink_assignment to the tbf
Again the function was called send but didn't do any sending
2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 22b31190cb tbf: Fix typo in thecomment 2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 46bcb8d59d tbf: Move gprs_rlcmac_send_uplink_ack into the tbf
The method was called send but didn't send anything. Move
it to the tbf
2013-10-30 21:24:12 +01:00
Holger Hans Peter Freyther 02beed5e98 bts: Move gprs_rlcmac_rcv_rach into the BTS class 2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther 2db7e7e7db tbf: Move gprs_rlcmac_send_packet_downlink_assignment into the tbf
This method was called _send_packet_ but actually didn't do any
sending at all. It just formated the right assignment. I replaced
tbf-> with nothing.
2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther 24c1a5ba29 bts: Move gprs_rlcmac_trigger_downlink_assignment into BTS 2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther 42de18f347 debug_dl_ass_idle: Remove the feature as it is obviously broken
strncpy takes three arguments but only two are given. This feature
would also only work if there is one tbf at a time.
2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther d9262b3b55 tbf: Move gprs_rlcmac_poll_timeout into the tbf
Move the gprs_rlcmac_poll_timeout method into the tbf class and
gprs_rlcmac_downlink_assignment into the BTS.
2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther 40cfaa6837 bts: Move rcv_imm_ass_cnf into the bts code 2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther 7292373f92 tbf: kill the indirection and invoke the right method 2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther 70ddde6929 tbf/bts: Move the tfi_find_free into the bts 2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther f63cabd931 tbf/pdch/bts: Move the tbf look-up by tfi into the BTS 2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther a54bbbbf02 pdch/tbf: Add another todo item for badly placed code... 2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther dea63b96e0 sba/pdch: Use thepdch to look up a sba for a frame number 2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther 05f8efc1a2 pdch: Remove the trx_no/ts_no parameter and use/caclulate it on demand
Simplify the depedencies and use the inline functions when we need
to figure out the numbers.
2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther 4f753c64d6 pdch: Remove the bts argument from rcv_control_block 2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther 65be4808af pdch: Remove the the bts parameter from rcv_data_block_acknowledged 2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther 6673005215 tbf: Fix typo in the debug message 2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther 782f6ddf99 tbf: Add todo item tht could help improving debugging 2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther 86300bbeea tbf: Move the timer routine into the class
The timer is used for various timeouts and there is still external
client code that is calling it. In a perfect world the client code
would indicate that an event has happened and the internal timer
will be stopped. The best compromise is the "stop_t3191" method. It
allows to add semantic verification that the timer has been running.
2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther af8094d799 tbf: Move the handle tineout into the tbf class 2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther aa9c326d7e tbf: Move the updating of the data into the tbf class 2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther fcbc702112 pdch: Move the giant switch/case of gprs_rlcmac_rcv_control_block
Move the dispatch into the PDCH. This needs to be split up
further into understandable blocks.
2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther d11290b90b pdch/tbf: Move gprs_rlcmac_rcv_data_block_acknowledged into the pdch
Move the method into the PDCH. Extract the finding of TLLI into a
new class called Decoding. Move the assemble and forward LLC frames
into the TBF as it is poking in the internals of the TBF.
2013-10-30 21:24:11 +01:00
Holger Hans Peter Freyther 6f9f434463 pdch: Move the dispatch of gprs_rlcmac_rcv_block into the pdch 2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 9ae367f639 pdch: Instead of passing bts, trx, ts use the pdch
All dispatching will go through the PDCH. This will clean a lot
of the look-ups inside the gprs_rlcmac_data.c and continue with
adding structure to the pcu code.
2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther b65e08a7be misc: Remove the bts parameter from calls that take a tbf
Mark some of the tbf manipulation that is burried in the data
sending routine.. that it should be moved around.
2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 545876550f sched: Add another warning about trx->arfcn and arfcn.. 2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 6796ed23ab alloc: Remove the ts parameter from the internal interface 2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 09ef27ae04 pdch: Simplify the reset code, rename variables to XYZ_no
Simplify the reset code now that the PDCH can know where it is
located. Rename the variables in the sba to trx_no and ts_no as
it stores the number and not the actual thing.
2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 4ed1dae533 bts: Add backpointers to the PDCH and TRX structures
This will allow to kill various parameters from all the functions
as we can walk back.
2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 1702f102af tbf: First round of removing llc handling from the rlcmac_data
The code in gprs_rlcmac_data should ask the TBF for help in packing
the frames but it really shouldn't poke in the internals of the
tbf structure. This is very bad capsuling and has plenty of copy
and paste.

At the same thime this will be the most dangerous refactoring of
the code base.
2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 7ce21eb042 data: Move gprs_rlcmac_cs to the place it is used and make static 2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 34bd8bdf30 bts/tbf: Move the lists into the BTS and do the look-up from the BTS
The list belongs to the BTS. This makes cleaning this up more easy
and establishes a hierachy of resources that start from the BTS. The
debug_diagram code is now broken.
2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 9f0c1d216a tbf: Keep a pointer from the tbf to the BTS
This is preparing the next commits and will ease working with
the debug_diagram when the global lists are killed. It will
simplify the APIin the long run.
2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther cedf890928 sba: Create a SBAController that will manage the sbas for a BTS
The PollController is a friend of the SBAController and is allowed
to access the internal list. The list is hidden from everyone else.

This is done because the calculation of timeout should belong into
the PollController and not into the SBAController.
2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 621dc2fd01 sba: Move the sba code to a dedicated file
Move the code around and then turn it into an object...
2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 111614a994 ta: Create TimingAdvance class and make it belong to the BTS
This allows us to easily flush the state in case a PCU/BTS
connection is lost.
2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther d6ef5345e5 ta: Move timing advance related code into the ta.cpp file 2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 63f29d6f73 encoding: Move the functions into the encoding class
Add some TODO to this class. E.g. they could all work on the
bitvec and the parameter handling could better.
2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 6ca0a9076e encoding: Move encoding routines into a separate file 2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 9446485e3d pdch: Only say we have paged when this has actually been done 2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther f0984897a5 bts/pdch: Move the adding of paging to the BTS/PDCH objects
Only the gprs_rlcmac_pdch will manipulate the paging list now. There
can be various more refactorings of the code but they can be done
later. E.g. on memory allocation failure we can continue instead
of leaving the code, we should also set any_tbf only after things
have been paged.
2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 24e98d039d pdch: Move paging dequeue into the PDCH object
Rely on packet_paging_request returning NULL in case the queue
is empty. We should move the write_packet_paging_request into
a separate file/object as well.
2013-10-30 21:24:10 +01:00
Holger Hans Peter Freyther 17b0d83a1f pdch: Move enable/disable into the PDCH code
When a PDCH is disabled all resources should be freed. This is
currently not possible as the PDCH does not know where it belongs
to. On top of that the list (and other resources) should be
properly initialized on construction so that disable() is idempotent
and does not check if it was disabled. During the re-factoring I
noticed that during a sysmobts re-start some resources are not
freed. I left a warning in the code to resolve this issue later.
2013-10-30 21:24:09 +01:00
Holger Hans Peter Freyther ba5543fbf3 Add a TODO file with high-level tasks 2013-10-30 21:23:18 +01:00
Holger Hans Peter Freyther b78adcdd11 bts: Introduce a PollController that has the responsibility to poll
For each frame indication received by the BTS the poll controller
is asked to expire timedout entries.
2013-10-30 21:20:45 +01:00
Holger Hans Peter Freyther 9b30c7f46e bts: Move the frame_number into the BTS sructure
The current_frame is an attribute of the BTS. Move it from the
pcu_l1_if.cpp into the BTS. As the next step we can trigger
actions depending on the change of the frame.
2013-10-30 21:20:45 +01:00
Holger Hans Peter Freyther b6acfdaa24 bts: Introduce a singleton for the BTS and use it in the code
Compared to the previous code there will be a branch to get the
global pointer so the code will be slightly slower than the previous
version but it allows us to start creating objects but still use
the code from C. It is best approach I have found so far.

One downside of C++ is that by default talloc will not be used
(unless we override the new operator to use talloc. Right now
we need to memset the C data structure by hand. The benefit of
enforcing a better structure should is more important though.
2013-10-30 21:20:45 +01:00
Holger Hans Peter Freyther 67ed34eedb bts: Move struct gprs_rlcmac_bts and other structs into a bts.h
Begin to make the BTS a real C++ object with real responsibilities.
The biggest issue will be the pcu_vty.c that might not like C++
at all.
2013-10-30 21:20:45 +01:00
Holger Hans Peter Freyther 1b33361bab bts: Remove the global state from the tbf 2013-10-30 21:20:45 +01:00
Holger Hans Peter Freyther b19d7268c3 bts: Remove global state from gprs_rlcmac_rcv_rts_block 2013-10-30 21:20:45 +01:00
Holger Hans Peter Freyther 1af73f6c81 bts: Remove global state from gprs_rlcmac_add_paging 2013-10-30 21:20:45 +01:00
Holger Hans Peter Freyther ee7a535608 bts: Remove global state from write_immediate_assignment 2013-10-30 21:20:45 +01:00
Holger Hans Peter Freyther 485860cc31 bts: Remove global state from write_packet_uplink_assignment 2013-10-30 21:20:45 +01:00
Holger Hans Peter Freyther d507ce6b85 bts: Remove global state from gprs_rlcmac_send_packet_uplink_assignment
This was the last method of gprs_rlcmac_data.cpp to access the
gprs_rlcmac_bts variable.
2013-10-30 21:20:45 +01:00
Holger Hans Peter Freyther 8d7a632eef bts: Remove global state frm gprs_rlcmac_rcv_control_block 2013-10-30 21:20:45 +01:00
Holger Hans Peter Freyther 4ffc260869 bts: Remove global state from gprs_rlcmac_send_data_block_acknowledged 2013-10-30 21:20:45 +01:00
Holger Hans Peter Freyther 9f521cd4af bts: Remove global state from gprs_rlcmac_send_packet_downlink_assignment 2013-10-30 21:20:45 +01:00
Holger Hans Peter Freyther b0250ebeac bts: Remove global state from gprs_rlcmac_rcv_rach 2013-10-30 21:20:45 +01:00
Holger Hans Peter Freyther 698b612188 bts: Remove global state from gprs_rlcmac_rcv_data_block_acknowledged 2013-10-30 21:20:44 +01:00
Holger Hans Peter Freyther ae947fcf67 bts: Remove the global state from write_packet_uplink_ack
Similiar to the previous patches
2013-10-30 21:20:44 +01:00
Holger Hans Peter Freyther 344ff48756 bts: Remove the global state gprs_rlcmac_trigger_downlink_assignment
Remove the global state from gprs_rlcmac_trigger_downlink_assignment
and walk up to the pcu_l1_if.cpp where I find the timeout handling
that should be part of the SBA and TBF functionality. In terms of
hierachies things start to be more clear.

There should be the BTS object. That holds the SBA and TBF Controllers
that can allocate TBFs and SBAs and will handle the timeout polling
for a BTS.
2013-10-30 21:20:44 +01:00
Holger Hans Peter Freyther 173a240a7e bts: Remove global state and the usage of the gprs_rlcmac_bts
Global state makes mocking/writing tests more difficult. Continue
by removing direct usage of the bts and adding it as pointer to
the function calls (showing the true dependencies of the functions)
2013-10-30 21:20:44 +01:00
Holger Hans Peter Freyther 45396a99d9 sched: Make sched_poll static and add a warning about fairness 2013-10-30 21:20:44 +01:00
Holger Hans Peter Freyther bfdd5f285b alloc: Add very basic test case for the alloc_a algorithm 2013-10-30 21:20:44 +01:00
Holger Hans Peter Freyther 948a3d62b1 alloc: Move usf into the scope it is actually used 2013-10-30 14:45:57 +01:00
Holger Hans Peter Freyther b0a0075845 alloc: Factor out the routine to find an enabled PDCH 2013-10-30 14:45:57 +01:00
Holger Hans Peter Freyther 11a748935e alloc: Remove the unimplemented/unfinished time constraint code
The algorithm is already complex enough. No reason to further
introduce the complexity with the branches it has.
2013-10-30 14:45:57 +01:00
Holger Hans Peter Freyther 8481a0553d alloc: Remove code duplication for downlink assignment
There was no difference but there is no point in poking in the
internals of the tbf, pdch and trx from various places. Information
hiding has a real purpose, e.g. compare with reading "the mythical
man month".
2013-10-30 14:45:57 +01:00
Holger Hans Peter Freyther 743bafa50c alloc: Introduce a backpointer from the tbf to the trx and simplify code
Kill all the level of indirections where one needs to have the BTS
the TBF to find the TRX.
2013-10-30 14:45:57 +01:00
Holger Hans Peter Freyther 96efa70a9e alloc: Call it trx_no and not trx as it is not trx object 2013-10-30 14:45:57 +01:00
Holger Hans Peter Freyther 02ab4a8803 alloc: Move the two timeslot alloc algorithms to a dedicated file 2013-10-30 14:45:57 +01:00
Holger Hans Peter Freyther 96173aec93 alloc: Move the "assignment" of the slot into a common function
Vladimir Rolbin pointed out that in case of the alloc_algorithm_a
the usf has not been assigned. For now move all the assignment into
a method and call it from the A and B algorithm.
2013-10-30 14:45:57 +01:00
Holger Hans Peter Freyther 1c344e2668 tbf: Prepare to make thing things private in the tbf, start with the state
There really shouldn't be too many callers of state. Instead the
tbf should dispatch depending on the internal state. For now
introduce state_is and state_is_not accessor functions so we can
start to see who is using the internal state.
2013-10-28 12:54:09 +04:00
Holger Hans Peter Freyther 7380babdba tbf: Move the tbf_timer_cb into the tbf class
Introduce the first instance method for printing out diagonistic
about itself and create a jump function for it.
2013-10-28 12:53:49 +04:00
Holger Hans Peter Freyther 0946f99b08 tbf: Remove dead code that called tbf_free 2013-10-28 12:53:29 +04:00
Holger Hans Peter Freyther 455613076a tbf: Move many routines related to the tbf from gprs_rlcmac to tbf.c 2013-10-28 12:52:59 +04:00
Holger Hans Peter Freyther 4f6a4e5d6d tbf: Add another variant of the free_all now by pdch 2013-10-28 12:52:33 +04:00
Holger Hans Peter Freyther 964ddb6aa0 tbf: Move the code to close all timeslots into the tbf class
Move the code out of the sysmocom_socket (as this is only required
to talk to our hardware) and into the TBF class that actually knows
what a TBF is.
2013-10-28 12:52:15 +04:00
Holger Hans Peter Freyther 9e21d84f1e tbf: Move header definition from gprs_rlcmac to the tbf 2013-10-28 12:52:00 +04:00
Holger Hans Peter Freyther 099535a1a7 tbf: Move the struct to the tbf.h header file
This is the begin of a long march of turning tbf into a C++ class
and properly hiding the secrets inside this implementation instead
of having it spread across various different files.
2013-10-28 12:51:38 +04:00
Holger Hans Peter Freyther 8692128365 tbf: Move the alloc_ul_tbf into tbf.c and change signature
Add the bts parameter to the method list. This would be a static
method of the class (in case the TBF would be a class)
2013-10-18 15:17:43 +04:00
Holger Hans Peter Freyther bb20b2c64c tbf: Add more to do items for refactoring on the dl assignment 2013-10-18 15:17:27 +04:00
Holger Hans Peter Freyther bcafdf8d90 tbf: Warn about copy and paste between different tbf alloc routines
These need to be re-factored to use a common allocation routine.
2013-10-18 15:17:13 +04:00
Holger Hans Peter Freyther 443c822f77 tbf: Move the creation of a new tbf for the downlink to a new method
Move the code that is dedicated to handle the assignment of a new
TFI/TBF for the downlink into a new method.
2013-10-18 15:16:56 +04:00
Holger Hans Peter Freyther d868928888 tbf: Create an assign method for IMSI.
This will allow us to set flags (like IMSI) present and will
be of help when fixing the tbf lookup.
2013-10-18 15:16:42 +04:00
Holger Hans Peter Freyther d1d114f5b7 tbf: Move code that sets the tbf's ms_class to a central place 2013-10-18 15:16:27 +04:00
Holger Hans Peter Freyther 31d0df92ac tbf: Separate the easy path out of the receive path
* Create a look up routine for the TBF that will allow us to
  easily find a TBF by IMSI...
* Separate the code that works on an existing TBF.
2013-10-18 15:16:11 +04:00
Holger Hans Peter Freyther 17c31ce173 tbf: Begin to add some structure to the tbf code
The TBF should use the IMSI to identify a block flow but all
handling is spread across the entire code. Start to clean this
up by moving relevant code into the tbf file. Afterwards one
can clean up and add more internal structure.
2013-10-18 15:15:49 +04:00
Holger Hans Peter Freyther dcc9c39529 sba: Fix memory leak when all PDCHs are disabled 2013-10-17 11:36:11 +02:00
Holger Hans Peter Freyther 70862c9409 misc: Print the payload that is not known
Print the payload type that is not handled as this allows one to
look it up in the specification.
2013-10-16 16:37:22 +02:00
Holger Hans Peter Freyther 26da8361e9 rlc: Add missing break after GPRS_RLCMAC_CONTROL_BLOCK_OPT
Do not claim that the payload is not known. Add the missing break.

Do not print:
<0002> gprs_rlcmac.cpp:1174 GPRS_RLCMAC_CONTROL_BLOCK_OPT block payload is not supported.
<0002> gprs_rlcmac.cpp:1176 Unknown RLCMAC block payload.
2013-10-16 16:36:11 +02:00
Holger Hans Peter Freyther 483f77a275 misc: Fix some typos sed -i s,ressources,resources,g 2013-10-16 16:33:00 +02:00
Holger Hans Peter Freyther 985806030d misc: Fix format and typos in the code 2013-10-16 16:28:16 +02:00
Holger Hans Peter Freyther 52c911b3b4 tbf: constify the IMSI argument for the downlink assignment 2013-10-02 18:09:10 +04:00
Holger Hans Peter Freyther 2023bd18b6 tbf: Make multislot lookup tables static/const 2013-10-02 18:08:51 +04:00
Holger Hans Peter Freyther d6bd91e4e5 tfi: The tfi_alloc doesn't allocate anything, rename the function
Call things by what they do and this function doesn't allocate
anything but it is searching for the first unallocated tbf index.
2013-10-02 18:08:31 +04:00
Holger Hans Peter Freyther 9d938388f6 emu: Add a crash re-producer for the SGSN (and the concept of tests)
Introduce the concept of tests that will be ran one after the other.
This new test will send static message that will lead to the opening
of a PDP context. At this point one should use ping with a large
packet size and suspend/stop the emulator. Once the NS connection is
considered dead the SGSN will crash with a double free.

Reproduce:
0.) Add IMSI 901700000003094 to the ACL
1.) Stop/Suspend the emulation process so the NS Alive times out
2.) Use ping IP -s 2048

This will create a double free...

 #4  0xb7bb2646 in talloc_abort_double_free () at talloc.c:175
 #5  0xb7bbd41a in talloc_chunk_from_ptr (ptr=0x8091208) at talloc.c:190
 #6  _talloc_free (ptr=0x8091208) at talloc.c:517
 #7  talloc_free (ptr=ptr@entry=0x8091208) at talloc.c:990
 #8  0xb7bb319b in msgb_free (m=m@entry=0x8091208) at msgb.c:72
 #9  0x0804db54 in sndcp_send_ud_frag (fs=0xbfffcc6c) at gprs_sndcp.c:423
 #10 sndcp_unitdata_req (msg=msg@entry=0x808eed8, lle=0x808fbc8, nsapi=5 '\005',
    mmcontext=mmcontext@entry=0x80903e8) at gprs_sndcp.c:471
2013-09-04 21:29:00 +04:00
Holger Hans Peter Freyther bc1e52cfbf emu: Use OpenBSC code to decode the LLC and add assertions
Use the OpenBSC SGSN code to parse the LLC data and look into
the data we receive. Add assertions to verify the the sequence
number is increasing.
2013-09-04 21:28:53 +04:00
Holger Hans Peter Freyther 741481d3e0 emu: Send a static GPRS ATTACH request and wait for the reply.
This will send a static message. It will trigger the GMM code
on the SGSN and might ask us for the IMEI/IMSI or send us an
accept. As we are not replying at all the SGSN needs to send new
requests and we can observe if the sequence number is increasing
like it should.
2013-09-04 21:28:53 +04:00
Holger Hans Peter Freyther 4ea940787e emu: Create an app that allows to communicate with a SGSN
This code can open a BSSGP connection toward a SGSN and will
inititate the unblocking. It does not send any user data.
2013-09-04 21:28:53 +04:00
Holger Hans Peter Freyther 416ce69550 bssgp: Add callback UNITDATA.DL messaes with the data 2013-09-04 21:28:53 +04:00
Holger Hans Peter Freyther c0f1644c88 bssgp: Add callbacks for certain BSSGP events
Add a callback called when Unblock Ack is received. This can be
used by a supervisor or the emulation test.
2013-09-04 21:28:53 +04:00
Holger Hans Peter Freyther e8d9a5fa54 bssgp: Return the gprs_bssgp_pcu instance from create/connect
This can be used to install handlers/testcases to register
callbacks and other data.
2013-09-04 21:28:53 +04:00
Holger Hans Peter Freyther b67a8a348a rlcmac: Reduce the depedency on the global gprs_rlcmac_bts variable
For mocking/unit-testing/emulation (and a dual trx-systems) having
global state is quite bad. Cut back on the usage of the global
struct gprs_rlcmac_bts. It also makes the complexity of certain
routines more clear.
2013-09-04 21:28:53 +04:00
Holger Hans Peter Freyther 90d5df4ae7 bssgp: These routines are not public API.. make them static for now 2013-09-04 21:28:53 +04:00
Holger Hans Peter Freyther a9744debd9 bssgp: Re-indent the switch/case statement 2013-09-04 21:28:53 +04:00
Holger Hans Peter Freyther dfe17d7f91 tests: Fix the expected result (re-add whitespace)
As a last minute change I probably ran git rebase --whitespace=fix
on the patch set and broke the test result. Update the expected
file and tests should pass.
2013-08-03 14:59:38 +02:00
Holger Hans Peter Freyther e13298d093 tests: Move the RLCMACTest into the test directory and setup autotest 2013-08-02 13:40:20 +04:00
Holger Hans Peter Freyther bb00704871 tests: Create tests directory and move the VTY testing into there 2013-08-02 13:39:55 +04:00
Holger Hans Peter Freyther 421fe79e39 bssgp: The method creates and the connects.. reflect that in the name
Call things by what they do. This method is creating and then connecting
a BSSGP..
2013-08-02 13:39:30 +04:00
Holger Hans Peter Freyther 939bfaefec bitvector: Address compiler warnings about unsigned/signed
Fixes:
bitvector.cpp: In function 'int bitvec_pack(bitvec*, uint8_t*)':
bitvector.cpp:53:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
bitvector.cpp: In function 'int bitvec_unpack(bitvec*, uint8_t*)':
bitvector.cpp:63:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
bitvector.cpp: In function 'uint64_t bitvec_read_field(bitvec*, unsigned int&, unsigned int)':
bitvector.cpp:91:18: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
bitvector.cpp: In function 'int bitvec_write_field(bitvec*, unsigned int&, uint64_t, unsigned int)':
bitvector.cpp:108:18: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
2013-08-02 13:39:13 +04:00
Holger Hans Peter Freyther f14ddb7830 pcu_l1_if.cpp: Remove unused method and comment
pcu_l1_if.cpp:195:13: warning: 'void pcu_l1if_tx_bcch(uint8_t*, int)' defined but not used [-Wunused-function]
2013-08-02 13:38:48 +04:00
Holger Hans Peter Freyther 997d2ac3fe femtobts: Remove code that is causing warnings with GCC 4.8
femtobts.c:250:2: warning: excess elements in array initializer [enabled by default]
  { SuperFemto_ClkSrcId_NetList, "nwl" },
  ^
femtobts.c:250:2: warning: (near initialization for ‘femtobts_clksrc_names’) [enabled by default]
femtobts.c:251:2: warning: excess elements in array initializer [enabled by default]
  { 0, NULL }
  ^
femtobts.c:251:2: warning: (near initialization for ‘femtobts_clksrc_names’) [enabled by default]
2013-08-02 13:36:52 +04:00
Holger Hans Peter Freyther cf0265a112 csn1: Fix various printf/format issues pointed out by gcc
This might clash with C++11 and literal values but we will
see that once the compilers enable that by default.

Fixes:
csn1.cpp: In function 'gint16 csnStreamDecoder(csnStream_t*, const CSN_DESCR*, bitvec*, unsigned int&, void*)':
csn1.cpp:864:17: warning: format '%d' expects argument of type 'int', but argument 8 has type 'guint64 {aka long unsigned int}' [-Wformat]
csn1.cpp:1144:15: warning: format '%u' expects argument of type 'unsigned int', but argument 7 has type 'uint64_t {aka long long unsigned int}' [-Wformat]
csn1.cpp:1150:15: warning: format '%u' expects argument of type 'unsigned int', but argument 7 has type 'uint64_t {aka long long unsigned int}' [-Wformat]
csn1.cpp: In function 'gint16 csnStreamEncoder(csnStream_t*, const CSN_DESCR*, bitvec*, unsigned int&, void*)':
csn1.cpp:2119:17: warning: format '%d' expects argument of type 'int', but argument 8 has type 'guint64 {aka long unsigned int}' [-Wformat]
2013-08-02 13:36:20 +04:00
Holger Hans Peter Freyther 49be8bce50 misc: Ignore the two osmo-pcu binaries and files created by vim 2013-08-02 13:32:18 +04:00
Holger Hans Peter Freyther 148fb9a3bc csn1: Remove unused code from the file
Fixes:
csn1.cpp:124:20: warning: 'CSN_DESCR_type' defined but not used [-Wunused-variable]
2013-08-02 13:31:20 +04:00
Holger Hans Peter Freyther 98fe945a0d misc: Move the parsing of the ms_class from RA Capabilities to a method
Decrease the number of lines of a single method by splitting things up.
The fewer lines of code, branches and side-effects in a method, the easier
it will be to understand. The other benefit is that one can start creating
unit tests for the some parts of the code.
2013-07-30 12:23:25 +04:00
Holger Hans Peter Freyther 249c7e9431 bssgp: Remove commented out code that is currently not used. 2013-07-30 12:22:51 +04:00
Holger Hans Peter Freyther 40bd0c4b57 bssgp: Reset the BVC and NSVC state in the destroy routine
This might explain the issue of the BVCI > 1 not being unblocked
as the internal state has not been re-set when destroying the bssgp.
2013-07-30 12:22:12 +04:00
Holger Hans Peter Freyther 4b984b14a3 misc: Move the nsvc_unblocked into the struct osmo_pcu 2013-07-30 12:21:16 +04:00
Holger Hans Peter Freyther ed70cb733c misc: Move the bvc_timer into the struct osmo_pcu
This continues with the previous changes to reduce the global state.
2013-07-30 12:20:41 +04:00
Holger Hans Peter Freyther d8157c07df misc: Remove the unused sgsn pointer from the compilation unit 2013-07-30 12:20:15 +04:00
Holger Hans Peter Freyther 90692f93cf misc: Move the struct bssgp_bvc_ctx into the struct osmo_pcu 2013-07-30 12:19:40 +04:00
Holger Hans Peter Freyther 90f08efe58 misc: Introduce a struct osmo_pcu and move things into it.
One of the issues with not properly re-setting everything is that
due the global state it is not clear which variables belong together
and how long it exists. Begin with creating a osmo_pcu and moving
things into this class.

Think of an organic cell, this commit is introducing the cell wall
around it... and defines what is inside and what is outside of it.
2013-07-30 12:18:45 +04:00
Holger Hans Peter Freyther a30f47613a misc: Add an option exit/quit when the BSSGP is supposed to be destroyed
The PCU does not properly re-set the state when the connection to the
BTS is lost (and the SGSN potentially is re-started during that). This
results in the BSSGP BVCI > 1 remaining blocked and no data will be
accepted by the SGSN.

Add the '-e' option and exit the PCU when the BSSGP/NS are getting
destroyed.
2013-07-27 22:15:04 +02:00
Holger Hans Peter Freyther 51c57045e5 misc: Remove if (timer_pending) stop_timer idiom from the code
osmo_timer_del is an idempotent operation. There is no requirement
to check if it is running. If you don't want a timer to run, delete
it. Maybe one should have called the method _unschedule, _cancel to
make this more clear.
2013-07-27 22:14:45 +02:00
Andreas Eversberg a004e6a823 Added timing advance support for up and downlink TBFs
The timing advance of any TBF is stored when it ends. Whenever a new TBF
with the same TLLI is created (downlink TBF), the stored TA is recalled.

This algorithm assumes that the mobile does not move too fast during
transfer. Also the mobile must start a connection in order to get correct
initial timing advance.

This algorithm does not implement the timing advance procedure as defined
in TS 04.60. To implement the standard timing advance procedure, the BTS
must decode RACH on certain bursts, the mobile is expected to send them.
This requires much more complexity to a transceiver like USRP/UmTRX or
Calypso BTS.

The algorithm was tested at TA >= 8 and works quite well.
2013-05-13 16:45:21 +02:00
Andreas Eversberg 783aa4bcb8 Allow to enable realtime priority for the BTS process
I ported the Holger's scheduling patch from sysmobts to osmo-pcu.

This is usefull, if PCU uses direct access to the DSP of sysmobts.
The latency to respond to a PH-READY_TO_SEND.ind may not be higher
than 18ms. Currently we are using nice to increase our priority but
for a heavily loaded cell this is not enough. Add an option to enable
realtime scheduling and use it in the screenrc.

Linux offers two realtime scheduling classes these are SCHED_FIFO
and SCHED_RR. For SCHED_FIFO the process is running as long as possible
(potentially taking all the CPU and never yielding it), for SCHED_RR
the process can still be pre-empted at the end of the timeslice.

Using SCHED_RR appears to be the more safe option as a run-a-way
sysmobts process will not be able to take all the CPU time.
2013-05-09 08:58:09 +02:00
Ivan Kluchnikov b6bb55d88c We shouldn't include the VTY tests in make check by default. 2013-04-24 15:53:25 +04:00
Kat 7dac4862bc Added conditional python-based tests for VTY/config handling 2013-04-06 11:00:20 +02:00
Holger Hans Peter Freyther 72075f0e00 Make the code handle SIGTERM.
This way the pcu can be easily stopped with a sysvinit script.
2013-03-19 18:33:06 +04:00
Andreas Eversberg 81a12be317 Fix: Correctly display SGSN remote IP at debugging 2013-03-17 17:16:44 +01:00
Andreas Eversberg 050ace2fb4 Introduce new file for various measurements
The measurements include:
- DL bandwidth usage
- DL packet loss rate
- DL measurements by mobile
- UL measurements by BTS

In order to receive DL measurements from mobile, it must be enabled via
system information message at BSC.
2013-03-17 17:16:44 +01:00
Andreas Eversberg 570b44b29b Add RSSI of data blocks to PCU interface
This bumps the PCU API version to 5 and requires osmo-bts >= 0.2.0.
2013-03-17 17:16:18 +01:00
Andreas Eversberg 0b874b64ef Use default 'alpha' value of 0
Since we don't know the RX and TX parameters of the BTS that might be used
with PCU, the MS should not adapt the TX power from the RX level. So the
MS should always transmits with same power.

Finding an 'alpha' and 'gamma' value that will result in a constant
RX level at the BTS is a task of deployment.
2013-03-16 15:56:01 +01:00
Harald Welte 30a73d8544 PCU: respect the PCU-side "local port" as configured via L1 IF
This makes sure that the UDP local port of the Gb link is actually
set to what is configured via OML from OpenBSC.
2013-03-10 08:57:03 +00:00
Ivan Kluchnikov 4440845614 Fixed decoding and encoding of CSN_RECURSIVE_TARRAY, CSN_RECURSIVE_TARRAY_1, CSN_RECURSIVE_TARRAY_2.
We should offset pointer to data after each iteration.
2013-03-01 02:46:10 +04:00
Ivan Kluchnikov 5dc29a51ef Fixed memory leaks caused by not freeing msgb in udp_read_cb() function. 2013-02-04 12:57:00 +04:00
Andreas Eversberg 3afe56d7d8 Fixed closing of PDCH when enabling sysmo-bts' direct DSP access
If not compiled with this support, the closing function does not exists,
so it must not be called.

Removed a "#warning", because closing is already supported.
2013-01-25 07:22:59 +01:00
Holger Hans Peter Freyther 827ed55c3b misc: Use the $enableval in the macro and not "yes"
--enable-sysmocom-bts=no does not work because the enableval variable
is not used.
2013-01-23 21:54:19 +01:00
100 changed files with 37787 additions and 4888 deletions

23
.gitignore vendored
View File

@ -1,9 +1,11 @@
*.o
*.lo
*.a
*.sw?
Makefile.in
Makefile
.deps
src/cscope*
aclocal.m4
autom4te.cache
@ -20,3 +22,24 @@ ltmain.sh
core
core.*
osmoappdesc.pyc
# binaries
src/osmo-pcu
src/osmo-pcu-remote
# tests
.dirstamp
tests/atconfig
tests/package.m4
tests/alloc/AllocTest
tests/rlcmac/RLCMACTest
tests/tbf/TbfTest
tests/types/TypesTest
tests/ms/MsTest
tests/llist/LListTest
tests/codel/codel_test
tests/emu/pcu_emu
tests/testsuite
tests/testsuite.log

View File

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

42
TODO Normal file
View File

@ -0,0 +1,42 @@
* Make the TBF ul/dl list per BTS
* Make the SBA per BTS
* Make the tbf point to the BTS so we can kill plenty of parameters
* Group more into in classes.. remove bts pointers.
* Change functions with 100 parameters to get a struct as param
* Move move into the TBF class
* Replace trx/ts with pointers. E.g. a PDCH should know the trx
it is on... then we can omit trx, ts and parameters and just pass
the pdch.
* On global free/reset... also flush the timing advance..
* tbf/llc window code appears to be duplicated and nested in other
methods. This needs to be cleaned.
* Possible race condition:
When scheduling a Downlink Assignment on the UL-TBF we need to make
sure that the assignment is sent _before_ the final ack. With my fairness
changes it gets more likely that this event is trigerred.
* Optimize:
After receiving an ACK/NACK.. schedule another one if the window
is kind of stalled anyway. This way we avoid resending frames that
might have already arrived. It could increase the throughput..
Do not re-transmit after we got ack/nacked and where in the resending
mode... and increase the window.
<0004> tbf.cpp:907 - Sending new block at BSN 111
...
tbf.cpp:858 - Restarting at BSN 48, because all window is stalled.
...
tbf.cpp:1383 - V(B): (V(A)=59)"NNAAAAAAANAAAAANNAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXXX"(V(S)-1=111) A=Acked N=Nacked U=Unacked X=Resend-Unacked I=Invalid
.. retransmitting the nacked.. and then the ones that migh have
already arrived
<0004> tbf.cpp:834 TBF(TFI=0 TLLI=0xd7b78810 DIR=DL) downlink (V(A)==59 .. V(S)==112)
<0004> tbf.cpp:840 - Resending BSN 111
Figure out scheduling issue. Why do we reach the 20 re-transmits and
stil haven't received the ACK/NACK? was it scheduled? The whole
scheduler could be re-worked to be more determestic.. and answer
questions like if it has been sent or not

View File

@ -1,9 +1,10 @@
dnl Process this file with autoconf to produce a configure script
AC_INIT([osmo-pcu],
m4_esyscmd([./git-version-gen .tarball-version]),
[osmocom-pcu@lists.osmocom.org])
[osmocom-net-gprs@lists.osmocom.org])
AM_INIT_AUTOMAKE([dist-bzip2])
AC_CONFIG_TESTDIR(tests)
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@ -26,23 +27,31 @@ PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3)
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.5.1.4)
AC_MSG_CHECKING([whether to enable sysmocom-bts hardware support])
AC_ARG_ENABLE(sysmocom-bts,
AC_HELP_STRING([--enable-sysmocom-bts],
[enable code for sysmocom femto-bts [default=no]]),
[enable_sysmocom_bts="yes"],[enable_sysmocom_bts="no"])
AC_MSG_RESULT([$enable_sysmocom_bts])
AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes")
AC_MSG_CHECKING([whether to enable direct DSP access for PDCH of sysmocom-bts])
AC_ARG_ENABLE(sysmocom-dsp,
AC_HELP_STRING([--enable-sysmocom-dsp],
[enable code for sysmocom DSP [default=no]]),
[enable_sysmocom_dsp="yes"],[enable_sysmocom_dsp="no"])
[enable_sysmocom_dsp="$enableval"],[enable_sysmocom_dsp="no"])
AC_MSG_RESULT([$enable_sysmocom_dsp])
AM_CONDITIONAL(ENABLE_SYSMODSP, test "x$enable_sysmocom_dsp" = "xyes")
AC_ARG_ENABLE([vty_tests],
AC_HELP_STRING([--enable-vty-tests],
[Include the VTY tests in make check [default=no]]),
[enable_vty_tests="$enableval"],[enable_vty_tests="no"])
if test "x$enable_vty_tests" = "xyes" ; then
AM_PATH_PYTHON
AC_CHECK_PROG(OSMOTESTVTY_CHECK,osmotestvty.py,yes)
if test "x$OSMOTESTVTY_CHECK" != "xyes" ; then
AC_MSG_ERROR([Please install osmocom-python to run the vty tests.])
fi
fi
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")
AC_OUTPUT(
src/Makefile
examples/Makefile
tests/Makefile
Makefile)

16
contrib/sysmopcu.service Normal file
View File

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

View File

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

26
osmoappdesc.py Normal file
View File

@ -0,0 +1,26 @@
#!/usr/bin/env python
# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 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 General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
app_configs = {
"osmo-pcu": ["examples/osmo-pcu.cfg"]
}
apps = [(4240, "src/osmo-pcu", "Osmo-PCU", "osmo-pcu"),
]
vty_command = ["src/osmo-pcu", "-c", "examples/osmo-pcu.cfg"]
vty_app = apps[0]

View File

@ -25,6 +25,7 @@ AM_CPPFLAGS += -DENABLE_SYSMODSP
endif
AM_CXXFLAGS = -Wall -ldl -pthread
AM_LDFLAGS = -lrt
noinst_LTLIBRARIES = libgprs.la
@ -34,27 +35,35 @@ libgprs_la_SOURCES = \
gsm_rlcmac.cpp \
gprs_bssgp_pcu.cpp \
gprs_rlcmac.cpp \
gprs_rlcmac_data.cpp \
gprs_rlcmac_sched.cpp \
gprs_rlcmac_meas.cpp \
gprs_rlcmac_ts_alloc.cpp \
gprs_ms.cpp \
gprs_ms_storage.cpp \
gsm_timer.cpp \
bitvector.cpp \
pcu_l1_if.cpp \
pcu_vty.c
if ENABLE_SYSMOBTS
libgprs_la_SOURCES += \
sysmo_sock.cpp
else
libgprs_la_SOURCES += \
openbts_sock.cpp
endif
noinst_PROGRAMS = \
RLCMACTest
pcu_vty.c \
pcu_vty_functions.cpp \
tbf.cpp \
tbf_ul.cpp \
tbf_dl.cpp \
bts.cpp \
poll_controller.cpp \
encoding.cpp \
sba.cpp \
decoding.cpp \
llc.cpp \
rlc.cpp \
osmobts_sock.cpp \
gprs_codel.c \
gprs_coding_scheme.cpp
bin_PROGRAMS = \
osmo-pcu
noinst_PROGRAMS =
if ENABLE_SYSMODSP
noinst_PROGRAMS += \
osmo-pcu-remote
@ -66,19 +75,28 @@ noinst_HEADERS = \
gsm_rlcmac.h \
gprs_bssgp_pcu.h \
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
RLCMACTest_SOURCES = RLCMACTest.cpp
RLCMACTest_LDADD = \
libgprs.la \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
femtobts.h \
tbf.h \
bts.h \
poll_controller.h \
encoding.h \
sba.h \
rlc.h \
decoding.h \
llc.h \
pcu_utils.h \
cxx_linuxlist.h \
gprs_codel.h \
gprs_coding_scheme.h
osmo_pcu_SOURCES = pcu_main.cpp

View File

@ -47,9 +47,9 @@ void bitvec_free(struct bitvec *bv)
talloc_free(bv);
}
int bitvec_pack(struct bitvec *bv, uint8_t *buffer)
unsigned int bitvec_pack(struct bitvec *bv, uint8_t *buffer)
{
int i = 0;
unsigned int i = 0;
for (i = 0; i < bv->data_len; i++)
{
buffer[i] = bv->data[i];
@ -57,9 +57,9 @@ int bitvec_pack(struct bitvec *bv, uint8_t *buffer)
return i;
}
int bitvec_unpack(struct bitvec *bv, uint8_t *buffer)
unsigned int bitvec_unpack(struct bitvec *bv, uint8_t *buffer)
{
int i = 0;
unsigned int i = 0;
for (i = 0; i < bv->data_len; i++)
{
bv->data[i] = buffer[i];
@ -84,7 +84,7 @@ int bitvec_unhex(struct bitvec *bv, const char* src)
uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len)
{
int i;
unsigned int i;
uint64_t ui = 0;
bv->cur_bit = read_index;
@ -103,7 +103,8 @@ 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 i, rc;
unsigned int i;
int rc;
bv->cur_bit = write_index;
for (i = 0; i < len; i++) {
int bit = 0;
@ -116,3 +117,21 @@ int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, u
write_index += len;
return 0;
}
int bitvec_write_field_lh(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++) {
bit_value bit = L;
if (val & ((uint64_t)1 << (len - i - 1)))
bit = H;
rc = bitvec_set_bit(bv, bit);
if (rc)
return rc;
}
write_index += len;
return 0;
}

View File

@ -35,10 +35,11 @@ extern "C" {
struct bitvec *bitvec_alloc(unsigned size);
void bitvec_free(struct bitvec *bv);
int bitvec_unhex(struct bitvec *bv, const char* src);
int bitvec_pack(struct bitvec *bv, uint8_t *buffer);
int bitvec_unpack(struct bitvec *bv, uint8_t *buffer);
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);
/*! }@ */

1528
src/bts.cpp Normal file

File diff suppressed because it is too large Load Diff

499
src/bts.h Normal file
View File

@ -0,0 +1,499 @@
/* bts.h
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stat_item.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/gsmtap.h>
}
#include "poll_controller.h"
#include "sba.h"
#include "tbf.h"
#include "gprs_ms_storage.h"
#include "gprs_coding_scheme.h"
#endif
#include <stdint.h>
#define LLC_CODEL_DISABLE 0
#define LLC_CODEL_USE_DEFAULT (-1)
struct BTS;
struct GprsMs;
/*
* PDCH instance
*/
struct gprs_rlcmac_pdch {
#ifdef __cplusplus
struct gprs_rlcmac_paging *dequeue_paging();
struct msgb *packet_paging_request();
void add_paging(struct gprs_rlcmac_paging *pag);
void free_resources();
bool is_enabled() const;
void enable();
void disable();
/* dispatching of messages */
int rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
struct pcu_l1_meas *meas);
int rcv_block_gprs(uint8_t *data, uint32_t fn,
struct pcu_l1_meas *meas, GprsCodingScheme cs);
int rcv_data_block(uint8_t *data, uint32_t fn,
struct pcu_l1_meas *meas, GprsCodingScheme cs);
gprs_rlcmac_bts *bts_data() const;
BTS *bts() const;
uint8_t trx_no() const;
struct gprs_rlcmac_ul_tbf *ul_tbf_by_tfi(uint8_t tfi);
struct gprs_rlcmac_dl_tbf *dl_tbf_by_tfi(uint8_t tfi);
void attach_tbf(gprs_rlcmac_tbf *tbf);
void detach_tbf(gprs_rlcmac_tbf *tbf);
unsigned num_tbfs(enum gprs_rlcmac_tbf_direction dir) const;
void reserve(enum gprs_rlcmac_tbf_direction dir);
void unreserve(enum gprs_rlcmac_tbf_direction dir);
unsigned num_reserved(enum gprs_rlcmac_tbf_direction dir) const;
uint8_t assigned_usf() const;
uint32_t assigned_tfi(enum gprs_rlcmac_tbf_direction dir) const;
#endif
uint8_t m_is_enabled; /* TS is enabled */
uint8_t tsc; /* TSC of this slot */
uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
uint8_t next_ctrl_prio; /* next kind of ctrl message to schedule */
struct llist_head paging_list; /* list of paging messages */
uint32_t last_rts_fn; /* store last frame number of RTS */
/* back pointers */
struct gprs_rlcmac_trx *trx;
uint8_t ts_no;
#ifdef __cplusplus
private:
int rcv_control_block(bitvec *rlc_block, uint32_t fn);
void rcv_control_ack(Packet_Control_Acknowledgement_t *, uint32_t fn);
void rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *, uint32_t fn);
void rcv_control_egprs_dl_ack_nack(EGPRS_PD_AckNack_t *, uint32_t fn);
void rcv_resource_request(Packet_Resource_Request_t *t, uint32_t fn);
void rcv_measurement_report(Packet_Measurement_Report_t *t, uint32_t fn);
gprs_rlcmac_tbf *tbf_from_list_by_tfi(
LListHead<gprs_rlcmac_tbf> *tbf_list, uint8_t tfi,
enum gprs_rlcmac_tbf_direction dir);
gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi,
enum gprs_rlcmac_tbf_direction dir);
#endif
uint8_t m_num_tbfs[2];
uint8_t m_num_reserved[2];
uint8_t m_assigned_usf; /* bit set */
uint32_t m_assigned_tfi[2]; /* bit set */
struct gprs_rlcmac_tbf *m_tbfs[2][32];
};
struct gprs_rlcmac_trx {
void *fl1h;
uint16_t arfcn;
struct gprs_rlcmac_pdch pdch[8];
/* back pointers */
struct BTS *bts;
uint8_t trx_no;
#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
};
/**
* This is the data from C. As soon as our minimal compiler is gcc 4.7
* we can start to compile pcu_vty.c with c++ and remove the split.
*/
struct gprs_rlcmac_bts {
uint8_t bsic;
uint8_t fc_interval;
uint16_t fc_bucket_time;
uint32_t fc_bvc_bucket_size;
uint32_t fc_bvc_leak_rate;
uint32_t fc_ms_bucket_size;
uint32_t fc_ms_leak_rate;
uint8_t cs1;
uint8_t cs2;
uint8_t cs3;
uint8_t cs4;
uint8_t initial_cs_dl, initial_cs_ul;
uint8_t initial_mcs_dl, initial_mcs_ul;
uint8_t max_cs_dl, max_cs_ul;
uint8_t max_mcs_dl, max_mcs_ul;
uint8_t force_cs; /* 0=use from BTS 1=use from VTY */
uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
uint32_t llc_discard_csec;
uint32_t llc_idle_ack_csec;
uint32_t llc_codel_interval_msec; /* 0=disabled, -1=use default interval */
uint8_t t3142;
uint8_t t3169;
uint8_t t3191;
uint16_t t3193_msec;
uint8_t t3195;
uint8_t n3101;
uint8_t n3103;
uint8_t n3105;
struct gsmtap_inst *gsmtap;
struct gprs_rlcmac_trx trx[8];
int (*alloc_algorithm)(struct gprs_rlcmac_bts *bts,
struct GprsMs *ms,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
int use_tbf);
uint32_t alloc_algorithm_curst; /* options to customize algorithm */
uint8_t force_two_phase;
uint8_t alpha, gamma;
uint8_t egprs_enabled;
uint32_t dl_tbf_idle_msec; /* hold time for idle DL TBFs */
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];
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 */
/* State for dynamic algorithm selection */
int multislot_disabled;
/**
* Point back to the C++ object. This is used during the transition
* period.
*/
struct BTS *bts;
};
#ifdef __cplusplus
/**
* I represent a GSM BTS. I have one or more TRX, I know the current
* GSM time and I have controllers that help with allocating resources
* on my TRXs.
*/
struct BTS {
public:
enum {
CTR_TBF_DL_ALLOCATED,
CTR_TBF_DL_FREED,
CTR_TBF_DL_ABORTED,
CTR_TBF_UL_ALLOCATED,
CTR_TBF_UL_FREED,
CTR_TBF_UL_ABORTED,
CTR_TBF_REUSED,
CTR_TBF_ALLOC_ALGO_A,
CTR_TBF_ALLOC_ALGO_B,
CTR_TBF_FAILED_EGPRS_ONLY,
CTR_RLC_SENT,
CTR_RLC_RESENT,
CTR_RLC_RESTARTED,
CTR_RLC_STALLED,
CTR_RLC_NACKED,
CTR_RLC_ASS_TIMEDOUT,
CTR_RLC_ASS_FAILED,
CTR_RLC_ACK_TIMEDOUT,
CTR_RLC_ACK_FAILED,
CTR_RLC_REL_TIMEDOUT,
CTR_RLC_LATE_BLOCK,
CTR_DECODE_ERRORS,
CTR_SBA_ALLOCATED,
CTR_SBA_FREED,
CTR_SBA_TIMEDOUT,
CTR_LLC_FRAME_TIMEDOUT,
CTR_LLC_FRAME_DROPPED,
CTR_LLC_FRAME_SCHED,
CTR_RACH_REQUESTS,
};
enum {
STAT_MS_PRESENT,
};
enum {
TIMER_T3190_MSEC = 5000,
};
BTS();
~BTS();
static BTS* main_bts();
struct gprs_rlcmac_bts *bts_data();
SBAController *sba();
/** TODO: change the number to unsigned */
void set_current_frame_number(int frame_number);
void set_current_block_frame_number(int frame_number, unsigned max_delay);
int current_frame_number() const;
/** add paging to paging queue(s) */
int add_paging(uint8_t chan_needed, uint8_t *identity_lv);
gprs_rlcmac_dl_tbf *dl_tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts);
gprs_rlcmac_ul_tbf *ul_tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts);
gprs_rlcmac_dl_tbf *dl_tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts);
gprs_rlcmac_ul_tbf *ul_tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts);
int tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx);
int rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn);
int rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
void trigger_dl_ass(gprs_rlcmac_dl_tbf *tbf, gprs_rlcmac_tbf *old_tbf);
void snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi);
GprsMsStorage &ms_store();
GprsMs *ms_by_tlli(uint32_t tlli, uint32_t old_tlli = 0);
GprsMs *ms_by_imsi(const char *imsi);
GprsMs *ms_alloc(uint8_t ms_class, uint8_t egprs_ms_class = 0);
/*
* Statistics
*/
void tbf_dl_created();
void tbf_dl_freed();
void tbf_dl_aborted();
void tbf_ul_created();
void tbf_ul_freed();
void tbf_ul_aborted();
void tbf_reused();
void tbf_alloc_algo_a();
void tbf_alloc_algo_b();
void tbf_failed_egprs_only();
void rlc_sent();
void rlc_resent();
void rlc_restarted();
void rlc_stalled();
void rlc_nacked();
void rlc_ass_timedout();
void rlc_ass_failed();
void rlc_ack_timedout();
void rlc_ack_failed();
void rlc_rel_timedout();
void rlc_late_block();
void decode_error();
void sba_allocated();
void sba_freed();
void sba_timedout();
void llc_timedout_frame();
void llc_dropped_frame();
void llc_frame_sched();
void rach_frame();
void ms_present(int32_t n);
int32_t ms_present_get();
/*
* Below for C interface for the VTY
*/
struct rate_ctr_group *rate_counters() const;
struct osmo_stat_item_group *stat_items() const;
LListHead<gprs_rlcmac_tbf>& ul_tbfs();
LListHead<gprs_rlcmac_tbf>& dl_tbfs();
private:
int m_cur_fn;
int m_cur_blk_fn;
struct gprs_rlcmac_bts m_bts;
PollController m_pollController;
SBAController m_sba;
struct rate_ctr_group *m_ratectrs;
struct osmo_stat_item_group *m_statg;
GprsMsStorage m_ms_store;
/* list of uplink TBFs */
LListHead<gprs_rlcmac_tbf> m_ul_tbfs;
/* list of downlink TBFs */
LListHead<gprs_rlcmac_tbf> m_dl_tbfs;
private:
/* disable copying to avoid slicing */
BTS(const BTS&);
BTS& operator=(const BTS&);
};
inline int BTS::current_frame_number() const
{
return m_cur_fn;
}
inline SBAController *BTS::sba()
{
return &m_sba;
}
inline GprsMsStorage &BTS::ms_store()
{
return m_ms_store;
}
inline GprsMs *BTS::ms_by_tlli(uint32_t tlli, uint32_t old_tlli)
{
return ms_store().get_ms(tlli, old_tlli);
}
inline GprsMs *BTS::ms_by_imsi(const char *imsi)
{
return ms_store().get_ms(0, 0, imsi);
}
inline LListHead<gprs_rlcmac_tbf>& BTS::ul_tbfs()
{
return m_ul_tbfs;
}
inline LListHead<gprs_rlcmac_tbf>& BTS::dl_tbfs()
{
return m_dl_tbfs;
}
inline BTS *gprs_rlcmac_pdch::bts() const
{
return trx->bts;
}
inline unsigned gprs_rlcmac_pdch::num_tbfs(enum gprs_rlcmac_tbf_direction dir) const
{
return m_num_tbfs[dir];
}
inline unsigned gprs_rlcmac_pdch::num_reserved(
enum gprs_rlcmac_tbf_direction dir) const
{
return gprs_rlcmac_pdch::m_num_reserved[dir];
}
inline uint8_t gprs_rlcmac_pdch::assigned_usf() const
{
return m_assigned_usf;
}
inline uint32_t gprs_rlcmac_pdch::assigned_tfi(
enum gprs_rlcmac_tbf_direction dir) const
{
return m_assigned_tfi[dir];
}
inline struct rate_ctr_group *BTS::rate_counters() const
{
return m_ratectrs;
}
inline struct osmo_stat_item_group *BTS::stat_items() const
{
return m_statg;
}
#define CREATE_COUNT_INLINE(func_name, ctr_name) \
inline void BTS::func_name() {\
rate_ctr_inc(&m_ratectrs->ctr[ctr_name]); \
}
CREATE_COUNT_INLINE(tbf_dl_created, CTR_TBF_DL_ALLOCATED)
CREATE_COUNT_INLINE(tbf_dl_freed, CTR_TBF_DL_FREED)
CREATE_COUNT_INLINE(tbf_dl_aborted, CTR_TBF_DL_ABORTED)
CREATE_COUNT_INLINE(tbf_ul_created, CTR_TBF_UL_ALLOCATED)
CREATE_COUNT_INLINE(tbf_ul_freed, CTR_TBF_UL_FREED)
CREATE_COUNT_INLINE(tbf_ul_aborted, CTR_TBF_UL_ABORTED)
CREATE_COUNT_INLINE(tbf_reused, CTR_TBF_REUSED)
CREATE_COUNT_INLINE(tbf_alloc_algo_a, CTR_TBF_ALLOC_ALGO_A)
CREATE_COUNT_INLINE(tbf_alloc_algo_b, CTR_TBF_ALLOC_ALGO_B)
CREATE_COUNT_INLINE(tbf_failed_egprs_only, CTR_TBF_FAILED_EGPRS_ONLY)
CREATE_COUNT_INLINE(rlc_sent, CTR_RLC_SENT)
CREATE_COUNT_INLINE(rlc_resent, CTR_RLC_RESENT)
CREATE_COUNT_INLINE(rlc_restarted, CTR_RLC_RESTARTED)
CREATE_COUNT_INLINE(rlc_stalled, CTR_RLC_STALLED)
CREATE_COUNT_INLINE(rlc_nacked, CTR_RLC_NACKED)
CREATE_COUNT_INLINE(rlc_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(decode_error, CTR_DECODE_ERRORS)
CREATE_COUNT_INLINE(sba_allocated, CTR_SBA_ALLOCATED)
CREATE_COUNT_INLINE(sba_freed, CTR_SBA_FREED)
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_INLINE(rach_frame, CTR_RACH_REQUESTS);
#undef CREATE_COUNT_INLINE
#define CREATE_STAT_INLINE(func_name, func_name_get, stat_name) \
inline void BTS::func_name(int32_t val) {\
osmo_stat_item_set(m_statg->items[stat_name], val); \
} \
inline int32_t BTS::func_name_get() {\
return osmo_stat_item_get_last(m_statg->items[stat_name]); \
}
CREATE_STAT_INLINE(ms_present, ms_present_get, STAT_MS_PRESENT);
#undef CREATE_STAT_INLINE
inline gprs_rlcmac_bts *gprs_rlcmac_pdch::bts_data() const
{
return trx->bts->bts_data();
}
inline uint8_t gprs_rlcmac_pdch::trx_no() const
{
return trx->trx_no;
}
#endif
#ifdef __cplusplus
extern "C" {
#endif
struct gprs_rlcmac_bts *bts_main_data();
struct rate_ctr_group *bts_main_data_stats();
struct osmo_stat_item_group *bts_main_data_stat_items();
#ifdef __cplusplus
}
inline bool gprs_rlcmac_pdch::is_enabled() const
{
return m_is_enabled;
}
#endif

View File

@ -32,6 +32,8 @@
#include <cstdlib>
#include <assert.h>
#include <string.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include "csn1.h"
#include <gprs_debug.h>
@ -87,6 +89,7 @@ csnStreamInit(csnStream_t* ar, gint bit_offset, gint remaining_bits_len)
{
ar->remaining_bits_len = remaining_bits_len;
ar->bit_offset = bit_offset;
ar->direction = 0;
}
static const char* ErrCodes[] =
@ -118,44 +121,6 @@ ProcessError( unsigned readIndex, const char* sz, gint16 err, const CSN_DESCR* p
return err;
}
//#if 0
static const char* CSN_DESCR_type[]=
{
"CSN_END",
"CSN_BIT",
"CSN_UINT",
"CSN_TYPE",
"CSN_CHOICE",
"CSN_UNION",
"CSN_UNION_LH",
"CSN_UINT_ARRAY",
"CSN_TYPE_ARRAY",
"CSN_BITMAP",
"CSN_VARIABLE_BITMAP",
"CSN_VARIABLE_BITMAP_1",
"CSN_LEFT_ALIGNED_VAR_BMP",
"CSN_LEFT_ALIGNED_VAR_BMP_1",
"CSN_VARIABLE_ARRAY",
"CSN_VARIABLE_TARRAY",
"CSN_VARIABLE_TARRAY_OFFSET",
"CSN_RECURSIVE_ARRAY",
"CSN_RECURSIVE_TARRAY",
"CSN_RECURSIVE_TARRAY_1",
"CSN_RECURSIVE_TARRAY_2",
"CSN_EXIST",
"CSN_EXIST_LH",
"CSN_NEXT_EXIST",
"CSN_NEXT_EXIST_LH",
"CSN_NULL",
"CSN_FIXED",
"CSN_CALLBACK",
"CSN_UINT_OFFSET",
"CSN_UINT_LH",
"CSN_SERIALIZE",
"CSN_TRAP_ERROR"
"CSN_???"
};
//#endif
/**
* ================================================================================================
@ -579,11 +544,12 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
guint8 length = bitvec_read_field(vector, readIndex, length_len);
LOGPC(DCSN1, LOGL_NOTICE, "%s length = %d | ", pDescr->sz , (int)length);
arT.direction = 1;
bit_offset += length_len;
remaining_bits_len -= length_len;
csnStreamInit(&arT, bit_offset, length);
arT.direction = 1;
LOGPC(DCSN1, LOGL_NOTICE, "ptr = %p | offset = %d | ", (void *)data, (int)pDescr->offset);
Status = serialize(&arT, vector, readIndex, pvDATA(data, pDescr->offset));
if (Status >= 0)
@ -899,7 +865,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
guint64 ui64 = bitvec_read_field(vector, readIndex, no_of_bits);
pui64 = pui64DATA(data, pDescr->offset);
*pui64 = ui64;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui64);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %lu | ", pDescr->sz , *pui64);
}
else
{
@ -1179,13 +1145,13 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
guint8 bits_to_handle = remaining_bits_len%8;
if (bits_to_handle > 0)
{
LOGPC(DCSN1, LOGL_NOTICE, "%u|", bitvec_read_field(vector, readIndex, bits_to_handle));
LOGPC(DCSN1, LOGL_NOTICE, "%" PRIu64 "|", bitvec_read_field(vector, readIndex, bits_to_handle));
remaining_bits_len -= bits_to_handle;
bit_offset += bits_to_handle;
}
else if (bits_to_handle == 0)
{
LOGPC(DCSN1, LOGL_NOTICE, "%u|", bitvec_read_field(vector, readIndex, 8));
LOGPC(DCSN1, LOGL_NOTICE, "%" PRIu64 "|", bitvec_read_field(vector, readIndex, 8));
remaining_bits_len -= 8;
bit_offset += 8;
}
@ -1305,7 +1271,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
csnStream_t arT = *ar;
gint16 Status;
csnStreamInit(&arT, bit_offset, remaining_bits_len);
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pvDATA(data, pDescr->offset));
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
if (Status >= 0)
{ /* successful completion */
@ -1366,7 +1332,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
LOGPC(DCSN1, LOGL_NOTICE, "%s { | ", pDescr->sz);
csnStreamInit(&arT, bit_offset, remaining_bits_len);
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pvDATA(data, pDescr->offset));
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
if (Status >= 0)
{ /* successful completion */
@ -2154,7 +2120,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
{
pui64 = pui64DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui64, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui64);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %lu | ", pDescr->sz , *pui64);
}
else
{
@ -2562,7 +2528,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
csnStream_t arT = *ar;
gint16 Status;
csnStreamInit(&arT, bit_offset, remaining_bits_len);
Status = csnStreamEncoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, writeIndex, pvDATA(data, pDescr->offset));
Status = csnStreamEncoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, writeIndex, pui8);
if (Status >= 0)
{ /* successful completion */
@ -2628,7 +2594,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
ElementCount--;
LOGPC(DCSN1, LOGL_NOTICE, "%s { | ", pDescr->sz);
csnStreamInit(&arT, bit_offset, remaining_bits_len);
Status = csnStreamEncoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, writeIndex, pvDATA(data, pDescr->offset));
Status = csnStreamEncoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, writeIndex, pui8);
LOGPC(DCSN1, LOGL_NOTICE, "%s } | ", pDescr->sz);
if (Status >= 0)
{ /* successful completion */

133
src/cxx_linuxlist.h Normal file
View File

@ -0,0 +1,133 @@
/* cxx_linuxlist.h
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
extern "C" {
#include <osmocom/core/linuxlist.h>
}
template <typename T>
struct LListHead {
typedef T entry_t;
/* This must match the declaration of struct llist_head */
LListHead<T> *next;
LListHead<T> *prev;
LListHead() : m_back(0) { INIT_LLIST_HEAD(this); }
LListHead(T* entry) : m_back(entry) {
next = (LListHead<T> *)LLIST_POISON1;
prev = (LListHead<T> *)LLIST_POISON2;
}
T *entry() {return m_back;}
const T *entry() const {return m_back;}
llist_head &llist() {
return *static_cast<llist_head *>(static_cast<void *>(this));
}
const llist_head &llist() const {
return *static_cast<const llist_head *>(static_cast<const void *>(this));
}
private:
T *const m_back;
};
/* Define a family of casting functions */
template <typename T>
llist_head &llist(LListHead<T> &l)
{
return l->llist();
}
template <typename T>
const llist_head &llist(const LListHead<T> &l)
{
return l->llist();
}
template <typename T>
llist_head *llptr(LListHead<T> *l)
{
return &(l->llist());
}
template <typename T>
const llist_head *llptr(const LListHead<T> *l)
{
return &(l->llist());
}
/* Define type-safe wrapper for the existing linux_list.h functions */
template <typename T>
inline void llist_add(LListHead<T> *new_, LListHead<T> *head)
{
llist_add(llptr(new_), llptr(head));
}
template <typename T>
inline void llist_add_tail(LListHead<T> *new_, LListHead<T> *head)
{
llist_add_tail(llptr(new_), llptr(head));
}
template <typename T>
inline void llist_del(LListHead<T> *entry)
{
llist_del(llptr(entry));
}
template <typename T>
inline void llist_del_init(LListHead<T> *entry)
{
llist_del_init(llptr(entry));
}
template <typename T>
inline void llist_move(LListHead<T> *list, LListHead<T> *head)
{
llist_move(llptr(list), llptr(head));
}
template <typename T>
inline void llist_move_tail(LListHead<T> *list, LListHead<T> *head)
{
llist_move_tail(llptr(list), llptr(head));
}
template <typename T>
inline int llist_empty(const LListHead<T> *head)
{
return llist_empty(llptr(head));
}
template <typename T>
inline void llist_splice(LListHead<T> *list, LListHead<T> *head)
{
llist_splice(llptr(list), llptr(head));
}
template <typename T>
inline void llist_splice_init(LListHead<T> *list, LListHead<T> *head)
{
llist_splice_init(llptr(list), llptr(head));
}

677
src/decoding.cpp Normal file
View File

@ -0,0 +1,677 @@
/* decoding
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <decoding.h>
#include <rlc.h>
#include <gprs_debug.h>
extern "C" {
#include <osmocom/core/utils.h>
#include <osmocom/core/bitcomp.h>
}
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#define LENGTH_TO_END 255
/*
* \returns num extensions fields (num frames == offset) on success,
* -errno otherwise.
*/
static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len,
unsigned int *offs,
bool is_last_block,
Decoding::RlcData *chunks, unsigned int chunks_size)
{
const struct rlc_li_field_egprs *li;
uint8_t e;
unsigned int num_chunks = 0;
e = 0;
while (!e) {
if (*offs > data_len) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
"but no more data\n");
return -EINVAL;
}
/* get new E */
li = (struct rlc_li_field_egprs *)&data[*offs];
e = li->e;
*offs += 1;
if (!chunks)
continue;
if (num_chunks == chunks_size) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
"but no more chunks possible\n");
return -ENOSPC;
}
if (li->li == 0 && num_chunks == 0 && li->e == 0) {
/* TS 44.060, table 10.4.14a.1, row 2a */
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 */
break;
} else if (li->li > 0) {
/* TS 44.060, table 10.4.14a.1, row 1 & 2b */
chunks[num_chunks].length = li->li;
chunks[num_chunks].is_complete = true;
} else {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI contains "
"invalid extension octet: LI=%d, E=%d, count=%d\n",
li->li, li->e, num_chunks);
return -EINVAL;
}
num_chunks += 1;
if (e == 1) {
/* There is space after the last chunk, add a final one */
if (num_chunks == chunks_size) {
LOGP(DRLCMACUL, LOGL_NOTICE,
"UL DATA LI possibly extended, "
"but no more chunks possible\n");
return -ENOSPC;
}
chunks[num_chunks].length = LENGTH_TO_END;
chunks[num_chunks].is_complete = is_last_block;
num_chunks += 1;
}
}
return num_chunks;
}
static int parse_extensions_gprs(const uint8_t *data, unsigned int data_len,
unsigned int *offs,
bool is_last_block,
Decoding::RlcData *chunks, unsigned int chunks_size)
{
const struct rlc_li_field *li;
uint8_t m, e;
unsigned int num_chunks = 0;
e = 0;
while (!e) {
if (*offs > data_len) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
"but no more data\n");
return -EINVAL;
}
/* get new E */
li = (const struct rlc_li_field *)&data[*offs];
e = li->e;
m = li->m;
*offs += 1;
if (li->li == 0) {
/* TS 44.060, 10.4.14, par 6 */
e = 1;
m = 0;
}
/* TS 44.060, table 10.4.13.1 */
if (m == 0 && e == 0) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA "
"ignored, because M='0' and E='0'.\n");
return 0;
}
if (!chunks)
continue;
if (num_chunks == chunks_size) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
"but no more chunks possible\n");
return -ENOSPC;
}
if (li->li == 0)
/* e is 1 here */
chunks[num_chunks].length = LENGTH_TO_END;
else
chunks[num_chunks].length = li->li;
chunks[num_chunks].is_complete = li->li || is_last_block;
num_chunks += 1;
if (e == 1 && m == 1) {
if (num_chunks == chunks_size) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
"but no more chunks possible\n");
return -ENOSPC;
}
/* TS 44.060, 10.4.13.1, row 4 */
chunks[num_chunks].length = LENGTH_TO_END;
chunks[num_chunks].is_complete = is_last_block;
num_chunks += 1;
}
}
return num_chunks;
}
int Decoding::rlc_data_from_ul_data(
const struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
const uint8_t *data, RlcData *chunks, unsigned int chunks_size,
uint32_t *tlli)
{
uint8_t e;
unsigned int data_len = rdbi->data_len;
int num_chunks = 0, i;
unsigned int offs = 0;
bool is_last_block = (rdbi->cv == 0);
if (!chunks)
chunks_size = 0;
e = rdbi->e;
if (e) {
if (chunks_size > 0) {
chunks[num_chunks].offset = offs;
chunks[num_chunks].length = LENGTH_TO_END;
chunks[num_chunks].is_complete = is_last_block;
num_chunks += 1;
} else if (chunks) {
LOGP(DRLCMACUL, LOGL_NOTICE, "No extension, "
"but no more chunks possible\n");
return -ENOSPC;
}
} else if (cs.isEgprs()) {
/* if E is not set (LI follows), EGPRS */
num_chunks = parse_extensions_egprs(data, data_len, &offs,
is_last_block,
chunks, chunks_size);
} else {
/* if E is not set (LI follows), GPRS */
num_chunks = parse_extensions_gprs(data, data_len, &offs,
is_last_block,
chunks, chunks_size);
}
if (num_chunks < 0)
return num_chunks;
/* TLLI */
if (rdbi->ti) {
uint32_t tlli_enc;
if (offs + 4 > data_len) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of block "
"border\n");
return -EINVAL;
}
memcpy(&tlli_enc, data + offs, sizeof(tlli_enc));
if (cs.isGprs())
/* The TLLI is encoded in big endian for GPRS (see
* TS 44.060, figure 10.2.2.1, note) */
*tlli = be32toh(tlli_enc);
else
/* The TLLI is encoded in little endian for EGPRS (see
* TS 44.060, figure 10.3a.2.1, note 2) */
*tlli = le32toh(tlli_enc);
offs += sizeof(tlli_enc);
} else {
*tlli = 0;
}
/* PFI */
if (rdbi->pi) {
LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, "
"please disable in SYSTEM INFORMATION\n");
return -ENOTSUP;
/* TODO: Skip all extensions with E=0 (see TS 44.060, 10.4.11 */
}
if (chunks_size == 0)
return num_chunks;
/* LLC */
for (i = 0; i < num_chunks; i++) {
chunks[i].offset = offs;
if (chunks[i].length == LENGTH_TO_END) {
if (offs == data_len) {
/* There is no place for an additional chunk,
* so drop it (this may happen with EGPRS since
* there is no M flag. */
num_chunks -= 1;
break;;
}
chunks[i].length = data_len - offs;
}
offs += chunks[i].length;
if (offs > data_len) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA out of block "
"border, chunk idx: %d, size: %d\n",
i, chunks[i].length);
return -EINVAL;
}
}
return num_chunks;
}
uint8_t Decoding::get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
{
int i;
for (i = 0; i < cap->Count_MS_RA_capability_value; i++) {
if (!cap->MS_RA_capability_value[i].u.Content.Exist_Multislot_capability)
continue;
if (!cap->MS_RA_capability_value[i].u.Content.Multislot_capability.Exist_GPRS_multislot_class)
continue;
return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.GPRS_multislot_class;
}
return 0;
}
uint8_t Decoding::get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
{
int i;
for (i = 0; i < cap->Count_MS_RA_capability_value; i++) {
if (!cap->MS_RA_capability_value[i].u.Content.Exist_Multislot_capability)
continue;
if (!cap->MS_RA_capability_value[i].u.Content.Multislot_capability.Exist_EGPRS_multislot_class)
continue;
return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.EGPRS_multislot_class;
}
return 0;
}
/**
* show_rbb needs to be an array with 65 elements
* The index of the array is the bit position in the rbb
* (show_rbb[63] relates to BSN ssn-1)
*/
void Decoding::extract_rbb(const uint8_t *rbb, char *show_rbb)
{
for (int i = 0; i < 64; i++) {
uint8_t bit;
bit = !!(rbb[i/8] & (1<<(7-i%8)));
show_rbb[i] = bit ? 'R' : 'I';
}
show_rbb[64] = '\0';
}
void Decoding::extract_rbb(const struct bitvec *rbb, char *show_rbb)
{
unsigned int i;
for (i = 0; i < rbb->cur_bit; i++) {
uint8_t bit;
bit = bitvec_get_bit_pos(rbb, i);
show_rbb[i] = bit == 1 ? 'R' : 'I';
}
show_rbb[i] = '\0';
}
int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
const uint8_t *data, GprsCodingScheme cs)
{
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;
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;
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",
cs.name());
return -ENOTSUP;
};
return cur_bit;
}
/**
* \brief Copy LSB bitstream RLC data block to byte aligned buffer.
*
* Note that the bitstream is encoded in LSB first order, so the two octets
* 654321xx xxxxxx87 contain the octet 87654321 starting at bit position 3
* (LSB has bit position 1). This is a different order than the one used by
* CSN.1.
*
* \param data_block_idx The block index, 0..1 for header type 1, 0 otherwise
* \param src A pointer to the start of the RLC block (incl. the header)
* \param buffer A data area of a least the size of the RLC block
* \returns the number of bytes copied
*/
unsigned int Decoding::rlc_copy_to_aligned_buffer(
const struct gprs_rlc_data_info *rlc,
unsigned int data_block_idx,
const uint8_t *src, uint8_t *buffer)
{
unsigned int hdr_bytes;
unsigned int extra_bits;
unsigned int i;
uint8_t c, last_c;
uint8_t *dst;
const struct gprs_rlc_data_block_info *rdbi;
OSMO_ASSERT(data_block_idx < rlc->num_data_blocks);
rdbi = &rlc->block_info[data_block_idx];
hdr_bytes = rlc->data_offs_bits[data_block_idx] >> 3;
extra_bits = (rlc->data_offs_bits[data_block_idx] & 7);
if (extra_bits == 0) {
/* It is aligned already */
memmove(buffer, src + hdr_bytes, rdbi->data_len);
return rdbi->data_len;
}
dst = buffer;
src = src + hdr_bytes;
last_c = *(src++);
for (i = 0; i < rdbi->data_len; i++) {
c = src[i];
*(dst++) = (last_c >> extra_bits) | (c << (8 - extra_bits));
last_c = c;
}
return rdbi->data_len;
}
/**
* \brief Get a pointer to byte aligned RLC data.
*
* Since the RLC data may not be byte aligned to the RLC block data such that a
* single RLC data byte is spread over two RLC block bytes, this function
* eventually uses the provided buffer as data storage.
*
* \param src A pointer to the start of the RLC block (incl. the header)
* \param buffer A data area of a least the size of the RLC block
* \returns A pointer to the RLC data start within src if it is aligned, and
* buffer otherwise.
*/
const uint8_t *Decoding::rlc_get_data_aligned(
const struct gprs_rlc_data_info *rlc,
unsigned int data_block_idx,
const uint8_t *src, uint8_t *buffer)
{
unsigned int hdr_bytes;
unsigned int extra_bits;
OSMO_ASSERT(data_block_idx < ARRAY_SIZE(rlc->data_offs_bits));
hdr_bytes = rlc->data_offs_bits[data_block_idx] >> 3;
extra_bits = (rlc->data_offs_bits[data_block_idx] & 7);
if (extra_bits == 0)
/* It is aligned already, return a pointer that refers to the
* original data. */
return src + hdr_bytes;
Decoding::rlc_copy_to_aligned_buffer(rlc, data_block_idx, src, buffer);
return buffer;
}
static int handle_final_ack(bitvec *bits, int *bsn_begin, int *bsn_end,
gprs_rlc_dl_window *window)
{
int num_blocks, i;
num_blocks = window->mod_sns(window->v_s() - window->v_a());
for (i = 0; i < num_blocks; i++)
bitvec_set_bit(bits, ONE);
*bsn_begin = window->v_a();
*bsn_end = window->mod_sns(*bsn_begin + num_blocks);
return num_blocks;
}
int Decoding::decode_egprs_acknack_bits(const EGPRS_AckNack_Desc_t *desc,
bitvec *bits, int *bsn_begin, int *bsn_end, gprs_rlc_dl_window *window)
{
int urbb_len = desc->URBB_LENGTH;
int crbb_len = 0;
int num_blocks = 0;
struct bitvec urbb;
int i;
bool have_bitmap;
int implicitly_acked_blocks;
int ssn = desc->STARTING_SEQUENCE_NUMBER;
int rc;
if (desc->FINAL_ACK_INDICATION)
return handle_final_ack(bits, bsn_begin, bsn_end, window);
if (desc->Exist_CRBB)
crbb_len = desc->CRBB_LENGTH;
have_bitmap = (urbb_len + crbb_len) > 0;
/*
* bow & bitmap present:
* V(A)-> [ 11111...11111 0 SSN-> BBBBB...BBBBB ] (SSN+Nbits) .... V(S)
* bow & not bitmap present:
* V(A)-> [ 11111...11111 ] . SSN .... V(S)
* not bow & bitmap present:
* V(A)-> ... [ 0 SSN-> BBBBB...BBBBB ](SSN+N) .... V(S)
* not bow & not bitmap present:
* V(A)-> ... [] . SSN .... V(S)
*/
if (desc->BEGINNING_OF_WINDOW) {
implicitly_acked_blocks = window->mod_sns(ssn - 1 - window->v_a());
for (i = 0; i < implicitly_acked_blocks; i++)
bitvec_set_bit(bits, ONE);
num_blocks += implicitly_acked_blocks;
}
if (!have_bitmap)
goto aborted;
/* next bit refers to V(Q) and thus is always zero (and not
* transmitted) */
bitvec_set_bit(bits, ZERO);
num_blocks += 1;
if (crbb_len > 0) {
int old_len = bits->cur_bit;
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);
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));
/* We don't know the SSN offset for the URBB,
* return what we have so far and assume the
* bitmap has stopped here */
goto aborted;
}
LOGP(DRLCMACDL, LOGL_DEBUG,
"CRBB len: %d, decoded len: %d, cc: %d, crbb: '%s'\n",
desc->CRBB_LENGTH, bits->cur_bit - old_len,
desc->CRBB_STARTING_COLOR_CODE,
osmo_hexdump(
desc->CRBB, (desc->CRBB_LENGTH + 7)/8)
);
num_blocks += (bits->cur_bit - old_len);
}
urbb.cur_bit = 0;
urbb.data = (uint8_t *)desc->URBB;
urbb.data_len = sizeof(desc->URBB);
for (i = urbb_len; i > 0; i--) {
/*
* Set bit at the appropriate position (see 3GPP TS
* 44.060 12.3.1)
*/
int is_ack = bitvec_get_bit_pos(&urbb, i-1);
bitvec_set_bit(bits, is_ack == 1 ? ONE : ZERO);
}
num_blocks += urbb_len;
aborted:
*bsn_begin = window->v_a();
*bsn_end = window->mod_sns(*bsn_begin + num_blocks);
return num_blocks;
}
int Decoding::decode_gprs_acknack_bits(const Ack_Nack_Description_t *desc,
bitvec *bits, int *bsn_begin, int *bsn_end, gprs_rlc_dl_window *window)
{
int urbb_len = RLC_GPRS_WS;
int num_blocks;
struct bitvec urbb;
if (desc->FINAL_ACK_INDICATION)
return handle_final_ack(bits, bsn_begin, bsn_end, window);
*bsn_begin = window->v_a();
*bsn_end = desc->STARTING_SEQUENCE_NUMBER;
num_blocks = window->mod_sns(*bsn_end - *bsn_begin);
if (num_blocks < 0 || num_blocks > urbb_len) {
*bsn_end = *bsn_begin;
LOGP(DRLCMACUL, LOGL_NOTICE,
"Invalid GPRS Ack/Nack window %d:%d (length %d)\n",
*bsn_begin, *bsn_end, num_blocks);
return -EINVAL;
}
urbb.cur_bit = 0;
urbb.data = (uint8_t *)desc->RECEIVED_BLOCK_BITMAP;
urbb.data_len = sizeof(desc->RECEIVED_BLOCK_BITMAP);
/*
* TS 44.060, 12.3:
* BSN = (SSN - bit_number) modulo 128, for bit_number = 1 to 64.
* The BSN values represented range from (SSN - 1) mod 128 to (SSN - 64) mod 128.
*
* We are only interested in the range from V(A) to SSN-1 which is
* num_blocks large. The RBB is laid out as
* [SSN-1] [SSN-2] ... [V(A)] ... [SSN-64]
* so we want to start with [V(A)] and go backwards until we reach
* [SSN-1] to get the needed BSNs in an increasing order. Note that
* the bit numbers are counted from the end of the buffer.
*/
for (int i = num_blocks; i > 0; i--) {
int is_ack = bitvec_get_bit_pos(&urbb, urbb_len - i);
bitvec_set_bit(bits, is_ack == 1 ? ONE : ZERO);
}
return num_blocks;
}

65
src/decoding.h Normal file
View File

@ -0,0 +1,65 @@
/* decoding
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
#include <gsm_rlcmac.h>
#include "rlc.h"
#include <stdint.h>
struct bitvec;
class Decoding {
public:
struct RlcData {
uint8_t offset;
uint8_t length;
bool is_complete;
};
static int rlc_data_from_ul_data(
const struct gprs_rlc_data_block_info *rdbi,
GprsCodingScheme cs, const uint8_t *data, RlcData *chunks,
unsigned int chunks_size, uint32_t *tlli);
static uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
static uint8_t get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
static void extract_rbb(const uint8_t *rbb, char *extracted_rbb);
static void extract_rbb(const struct bitvec *rbb, char *show_rbb);
static int rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
const uint8_t *data, GprsCodingScheme cs);
static unsigned int rlc_copy_to_aligned_buffer(
const struct gprs_rlc_data_info *rlc,
unsigned int data_block_idx,
const uint8_t *src, uint8_t *buffer);
static const uint8_t *rlc_get_data_aligned(
const struct gprs_rlc_data_info *rlc,
unsigned int data_block_idx,
const uint8_t *src, uint8_t *buffer);
static int decode_egprs_acknack_bits(
const EGPRS_AckNack_Desc_t *desc,
struct bitvec *bits, int *bsn_begin, int *bsn_end,
struct gprs_rlc_dl_window *window);
static int decode_gprs_acknack_bits(
const Ack_Nack_Description_t *desc,
bitvec *bits, int *bsn_begin, int *bsn_end,
gprs_rlc_dl_window *window);
};

1175
src/encoding.cpp Normal file

File diff suppressed because it is too large Load Diff

96
src/encoding.h Normal file
View File

@ -0,0 +1,96 @@
/* encoding.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
#include <stdint.h>
#include <gsm_rlcmac.h>
#include <gprs_coding_scheme.h>
struct gprs_rlcmac_bts;
struct gprs_rlcmac_tbf;
struct bitvec;
struct gprs_llc;
struct gprs_rlc_data_block_info;
/**
* I help with encoding data into CSN1 messages.
* TODO: Nobody can remember a function signature like this. One should
* fill out a struct with the request parameters and then hand the struct
* to the code.
*/
class Encoding {
public:
static int write_immediate_assignment(
struct gprs_rlcmac_tbf *tbf,
bitvec * dest, uint8_t downlink, uint8_t ra,
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts,
uint8_t tsc, uint8_t usf, uint8_t polling,
uint32_t fn, uint8_t alpha, uint8_t gamma,
int8_t ta_idx);
static void write_packet_uplink_assignment(
struct gprs_rlcmac_bts *bts,
bitvec * dest, uint8_t old_tfi,
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
struct gprs_rlcmac_ul_tbf *tbf, uint8_t poll, uint8_t rrbp,
uint8_t alpha, uint8_t gamma, int8_t ta_idx,
int8_t use_egprs);
static void write_packet_downlink_assignment(RlcMacDownlink_t * block,
bool old_tfi_is_valid, uint8_t old_tfi, uint8_t old_downlink,
struct gprs_rlcmac_tbf *tbf, uint8_t poll, uint8_t rrbp,
uint8_t alpha, uint8_t gamma,
int8_t ta_idx, uint8_t ta_ts, bool use_egprs);
static void encode_rbb(const char *show_rbb, uint8_t *rbb);
static void write_packet_uplink_ack(
struct gprs_rlcmac_bts *bts, bitvec * dest,
struct gprs_rlcmac_ul_tbf *tbf, bool is_final,
uint8_t rrbp);
static int write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len);
static unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
uint8_t *identity, uint8_t chan_needed);
static unsigned write_packet_paging_request(bitvec * dest);
static int rlc_write_dl_data_header(
const struct gprs_rlc_data_info *rlc,
uint8_t *data);
static unsigned int rlc_copy_from_aligned_buffer(
const struct gprs_rlc_data_info *rlc,
unsigned int data_block_idx,
uint8_t *dst, const uint8_t *buffer);
enum AppendResult {
AR_NEED_MORE_BLOCKS,
AR_COMPLETED_SPACE_LEFT,
AR_COMPLETED_BLOCK_FILLED,
};
static AppendResult rlc_data_to_dl_append(
struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
gprs_llc *llc, int *offset, int *num_chunks,
uint8_t *data,
bool is_final);
};

View File

@ -238,19 +238,6 @@ const struct value_string femtobts_tch_pl_names[] = {
{ 0, NULL }
};
const struct value_string femtobts_clksrc_names[] = {
{ SuperFemto_ClkSrcId_None, "None" },
{ SuperFemto_ClkSrcId_Ocxo, "ocxo" },
{ SuperFemto_ClkSrcId_Tcxo, "tcxo" },
{ SuperFemto_ClkSrcId_External, "ext" },
{ SuperFemto_ClkSrcId_GpsPps, "gps" },
{ SuperFemto_ClkSrcId_Trx, "trx" },
{ SuperFemto_ClkSrcId_Rx, "rx" },
{ SuperFemto_ClkSrcId_Edge, "edge" },
{ SuperFemto_ClkSrcId_NetList, "nwl" },
{ 0, NULL }
};
const struct value_string femtobts_dir_names[] = {
{ GsmL1_Dir_TxDownlink, "TxDL" },
{ GsmL1_Dir_TxUplink, "TxUL" },

View File

@ -33,8 +33,6 @@ const struct value_string femtobts_tracef_names[29];
const struct value_string femtobts_tch_pl_names[15];
const struct value_string femtobts_clksrc_names[8];
const struct value_string femtobts_dir_names[6];
enum pdch_cs {

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
/* gprs_bssgp_pcu.h
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -41,20 +42,54 @@ struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
#define NS_HDR_LEN 4
#define IE_LLC_PDU 14
extern struct bssgp_bvc_ctx *bctx;
struct gprs_rlcmac_bts;
int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp);
struct gprs_bssgp_pcu {
struct gprs_nsvc *nsvc;
struct bssgp_bvc_ctx *bctx;
int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx);
struct gprs_rlcmac_bts *bts;
int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx);
struct osmo_timer_list bvc_timer;
int gprs_bssgp_pcu_rcvmsg(struct msgb *msg);
int nsvc_unblocked;
int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, uint16_t lac,
uint16_t rac, uint16_t cell_id);
int bvc_sig_reset;
int bvc_reset;
int bvc_unblocked;
/* Flow control */
struct timeval queue_delay_sum;
unsigned queue_delay_count;
uint8_t fc_tag;
unsigned queue_frames_sent;
unsigned queue_bytes_recv;
unsigned queue_frames_recv;
/** callbacks below */
/* The BSSGP has been unblocked */
void (*on_unblock_ack)(struct gprs_bssgp_pcu *pcu);
/* When BSSGP data arrives. The msgb is not only for reference */
void (*on_dl_unit_data)(struct gprs_bssgp_pcu *pcu, struct msgb *msg,
struct tlv_parsed *tp);
};
struct gprs_bssgp_pcu *gprs_bssgp_create_and_connect(struct gprs_rlcmac_bts *bts,
uint16_t local_port,
uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc,
uint16_t lac, uint16_t rac, uint16_t cell_id);
void gprs_bssgp_destroy(void);
int gprs_ns_reconnect(struct gprs_nsvc *nsvc);
struct bssgp_bvc_ctx *gprs_bssgp_pcu_current_bctx(void);
void gprs_bssgp_update_queue_delay(const struct timeval *tv_recv,
const struct timeval *tv_now);
void gprs_bssgp_update_frames_sent();
void gprs_bssgp_update_bytes_received(unsigned bytes_recv, unsigned frames_recv);
#endif // GPRS_BSSGP_PCU_H

179
src/gprs_codel.c Normal file
View File

@ -0,0 +1,179 @@
/* gprs_codel.cpp
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "gprs_codel.h"
#include "gprs_debug.h"
#include <osmocom/core/utils.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
static void control_law(struct gprs_codel *state, struct timeval *delta)
{
/* 256 / sqrt(x), limited to 255 */
static uint8_t inv_sqrt_tab[] = {255,
255, 181, 147, 128, 114, 104, 96, 90, 85, 80, 77, 73, 71, 68,
66, 64, 62, 60, 58, 57, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
45, 45, 44, 43, 43, 42, 42, 41, 40, 40, 39, 39, 39, 38, 38, 37,
37, 36, 36, 36, 35, 35, 35, 34, 34, 34, 33, 33, 33, 33, 32, 32,
32, 32, 31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29, 29, 28,
28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26,
26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 24, 24, 24, 24, 24, 24,
24, 24, 24, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 18, 18, 18, 18, 18, 18, 18, 18, 18,
18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17,
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
17, 17, 17, 17
};
uint_fast32_t delta_usecs;
uint_fast32_t inv_sqrt;
div_t q;
if (state->count >= ARRAY_SIZE(inv_sqrt_tab))
inv_sqrt = 16;
else
inv_sqrt = inv_sqrt_tab[state->count];
/* delta = state->interval / sqrt(count) */
delta_usecs = state->interval.tv_sec * 1000000 + state->interval.tv_usec;
delta_usecs = delta_usecs * inv_sqrt / 256;
q = div(delta_usecs, 1000000);
delta->tv_sec = q.quot;
delta->tv_usec = q.rem;
}
void gprs_codel_init(struct gprs_codel *state)
{
static const struct gprs_codel init_state = {0};
*state = init_state;
gprs_codel_set_interval(state, -1);
gprs_codel_set_maxpacket(state, -1);
}
void gprs_codel_set_interval(struct gprs_codel *state, int interval_ms)
{
div_t q;
if (interval_ms <= 0)
interval_ms = GPRS_CODEL_DEFAULT_INTERVAL_MS;
q = div(interval_ms, 1000);
state->interval.tv_sec = q.quot;
state->interval.tv_usec = q.rem * 1000;
/* target ~ 5% of interval */
q = div(interval_ms * 13 / 256, 1000);
state->target.tv_sec = q.quot;
state->target.tv_usec = q.rem * 1000;
}
void gprs_codel_set_maxpacket(struct gprs_codel *state, int maxpacket)
{
if (maxpacket < 0)
maxpacket = GPRS_CODEL_DEFAULT_MAXPACKET;
state->maxpacket = maxpacket;
}
/*
* This is an broken up variant of the algorithm being described in
* http://queue.acm.org/appendices/codel.html
*/
int gprs_codel_control(struct gprs_codel *state, const struct timeval *recv,
const struct timeval *now, int bytes)
{
struct timeval sojourn_time;
struct timeval delta;
if (recv == NULL)
goto stop_dropping;
timersub(now, recv, &sojourn_time);
if (timercmp(&sojourn_time, &state->target, <))
goto stop_dropping;
if (bytes >= 0 && (unsigned)bytes <= state->maxpacket)
goto stop_dropping;
if (!timerisset(&state->first_above_time)) {
timeradd(now, &state->interval, &state->first_above_time);
goto not_ok_to_drop;
}
if (timercmp(now, &state->first_above_time, <))
goto not_ok_to_drop;
/* Ok to drop */
if (!state->dropping) {
int recently = 0;
int in_drop_cycle = 0;
if (timerisset(&state->drop_next)) {
timersub(now, &state->drop_next, &delta);
in_drop_cycle = timercmp(&delta, &state->interval, <);
recently = in_drop_cycle;
}
if (!recently) {
timersub(now, &state->first_above_time, &delta);
recently = !timercmp(&delta, &state->interval, <);
};
if (!recently)
return 0;
state->dropping = 1;
if (in_drop_cycle && state->count > 2)
state->count -= 2;
else
state->count = 1;
state->drop_next = *now;
} else {
if (timercmp(now, &state->drop_next, <))
return 0;
state->count += 1;
}
control_law(state, &delta);
timeradd(&state->drop_next, &delta, &state->drop_next);
#if 1
LOGP(DRLCMAC, LOGL_INFO,
"CoDel decided to drop packet, window = %d.%03dms, count = %d\n",
(int)delta.tv_sec, (int)(delta.tv_usec / 1000), state->count);
#endif
return 1;
stop_dropping:
timerclear(&state->first_above_time);
not_ok_to_drop:
state->dropping = 0;
return 0;
}

108
src/gprs_codel.h Normal file
View File

@ -0,0 +1,108 @@
/* gprs_codel.h
*
* This is an implementation of the CoDel algorithm based on the reference
* pseudocode (see http://queue.acm.org/appendices/codel.html).
* Instead of abstracting the queue itself, the following implementation
* provides a time stamp based automaton. The main work is done by a single
* decision function which updates the state and tells whether to pass or to
* drop a packet after it has been taken from the queue.
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
#include <sys/time.h>
/* Spec default values */
#define GPRS_CODEL_DEFAULT_INTERVAL_MS 100
#define GPRS_CODEL_DEFAULT_MAXPACKET 512
#ifdef __cplusplus
extern "C" {
#endif
struct gprs_codel {
int dropping;
unsigned count;
struct timeval first_above_time;
struct timeval drop_next;
struct timeval target;
struct timeval interval;
unsigned maxpacket;
};
/*!
* \brief Decide about packet drop and update CoDel state
*
* This function takes timing information and decides whether the packet in
* question should be dropped in order to keep related queue in a 'good' state.
* The function is meant to be called when the packet is dequeued.
*
* The CoDel state is updated by this function.
*
* \param state A pointer to the CoDel state of this queue
* \param recv The time when the packet has entered the queue,
* use NULL if dequeueing was not possible because the queue is
* empty
* \param now The current (dequeueing) time
* \param bytes The number of bytes currently stored in the queue (-1 if
* unknown)
*
* \return != 0 if the packet should be dropped, 0 otherwise
*/
int gprs_codel_control(struct gprs_codel *state, const struct timeval *recv,
const struct timeval *now, int bytes);
/*!
* \brief Initialise CoDel state
*
* This function initialises the CoDel state object. It sets the interval time
* to the default value (GPRS_CODEL_DEFAULT_INTERVAL_MS).
*
* \param state A pointer to the CoDel state of this queue
*/
void gprs_codel_init(struct gprs_codel *state);
/*!
* \brief Set interval time
*
* This function changes the interval time.
* The target time is derived from the interval time as proposed in the spec
* (5% of interval time).
*
* \param state A pointer to the CoDel state of this queue
* \param interval_ms The initial interval in ms to be used (<= 0 selects the
* default value)
*/
void gprs_codel_set_interval(struct gprs_codel *state, int interval_ms);
/*!
* \brief Set max packet size
*
* This function changes the maxpacket value. If no more than this number of
* bytes are still stored in the queue, no dropping will be done.
*
* \param state A pointer to the CoDel state of this queue
* \param maxpacket The value in bytes
*/
void gprs_codel_set_maxpacket(struct gprs_codel *state, int maxpacket);
#ifdef __cplusplus
}
#endif

286
src/gprs_coding_scheme.cpp Normal file
View File

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

216
src/gprs_coding_scheme.h Normal file
View File

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

View File

@ -40,7 +40,8 @@ static const struct log_info_cat default_categories[] = {
{"DRLCMACDL", "\033[1;33m", "GPRS RLC/MAC layer Downlink (RLCMAC)", LOGL_NOTICE, 1},
{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Uplink (RLCMAC)", LOGL_NOTICE, 1},
{"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Scheduling (RLCMAC)", LOGL_NOTICE, 1},
{"DRLCMACBW", "\033[1;31m", "GPRS RLC/MAC layer Bandwidth (RLCMAC)", LOGL_INFO, 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},
};

View File

@ -38,7 +38,8 @@ enum {
DRLCMACDL,
DRLCMACUL,
DRLCMACSCHED,
DRLCMACBW,
DRLCMACMEAS,
DNS,
DBSSGP,
DPCU,
aDebug_LastEntry

821
src/gprs_ms.cpp Normal file
View File

@ -0,0 +1,821 @@
/* gprs_ms.cpp
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "gprs_ms.h"
#include "bts.h"
#include "tbf.h"
#include "gprs_debug.h"
#include "gprs_codel.h"
#include "pcu_utils.h"
#include <time.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
}
#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
extern void *tall_pcu_ctx;
static int64_t now_msec()
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return int64_t(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
}
struct GprsMsDefaultCallback: public GprsMs::Callback {
virtual void ms_idle(class GprsMs *ms) {
delete ms;
}
virtual void ms_active(class GprsMs *) {}
};
static GprsMsDefaultCallback gprs_default_cb;
GprsMs::Guard::Guard(GprsMs *ms) :
m_ms(ms ? ms->ref() : NULL)
{
}
GprsMs::Guard::~Guard()
{
if (m_ms)
m_ms->unref();
}
bool GprsMs::Guard::is_idle() const
{
if (!m_ms)
return true;
return !m_ms->m_ul_tbf && !m_ms->m_dl_tbf && m_ms->m_ref == 1;
}
void GprsMs::timeout(void *priv_)
{
GprsMs *ms = static_cast<GprsMs *>(priv_);
LOGP(DRLCMAC, LOGL_INFO, "Timeout for MS object, TLLI = 0x%08x\n",
ms->tlli());
if (ms->m_timer.data) {
ms->m_timer.data = NULL;
ms->unref();
}
}
GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
m_bts(bts),
m_cb(&gprs_default_cb),
m_ul_tbf(NULL),
m_dl_tbf(NULL),
m_tlli(tlli),
m_new_ul_tlli(0),
m_new_dl_tlli(0),
m_ta(0),
m_ms_class(0),
m_egprs_ms_class(0),
m_is_idle(true),
m_ref(0),
m_list(this),
m_delay(0),
m_nack_rate_dl(0),
m_reserved_dl_slots(0),
m_reserved_ul_slots(0),
m_current_trx(NULL),
m_codel_state(NULL),
m_mode(GprsCodingScheme::GPRS)
{
int codel_interval = LLC_CODEL_USE_DEFAULT;
LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
m_imsi[0] = 0;
memset(&m_timer, 0, sizeof(m_timer));
m_timer.cb = GprsMs::timeout;
m_llc_queue.init();
set_mode(m_mode);
if (m_bts)
codel_interval = m_bts->bts_data()->llc_codel_interval_msec;
if (codel_interval) {
if (codel_interval == LLC_CODEL_USE_DEFAULT)
codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
m_codel_state = talloc(this, struct gprs_codel);
gprs_codel_init(m_codel_state);
gprs_codel_set_interval(m_codel_state, codel_interval);
}
m_last_cs_not_low = now_msec();
}
GprsMs::~GprsMs()
{
LListHead<gprs_rlcmac_tbf> *pos, *tmp;
LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
set_reserved_slots(NULL, 0, 0);
if (osmo_timer_pending(&m_timer))
osmo_timer_del(&m_timer);
if (m_ul_tbf) {
m_ul_tbf->set_ms(NULL);
m_ul_tbf = NULL;
}
if (m_dl_tbf) {
m_dl_tbf->set_ms(NULL);
m_dl_tbf = NULL;
}
llist_for_each_safe(pos, tmp, &m_old_tbfs)
pos->entry()->set_ms(NULL);
m_llc_queue.clear(m_bts);
}
void* GprsMs::operator new(size_t size)
{
static void *tall_ms_ctx = NULL;
if (!tall_ms_ctx)
tall_ms_ctx = talloc_named_const(tall_pcu_ctx, 0, __PRETTY_FUNCTION__);
return talloc_size(tall_ms_ctx, size);
}
void GprsMs::operator delete(void* p)
{
talloc_free(p);
}
GprsMs *GprsMs::ref()
{
m_ref += 1;
return this;
}
void GprsMs::unref()
{
OSMO_ASSERT(m_ref >= 0);
m_ref -= 1;
if (m_ref == 0)
update_status();
}
void GprsMs::start_timer()
{
if (m_delay == 0)
return;
if (!m_timer.data)
m_timer.data = ref();
osmo_timer_schedule(&m_timer, m_delay, 0);
}
void GprsMs::stop_timer()
{
if (!m_timer.data)
return;
osmo_timer_del(&m_timer);
m_timer.data = NULL;
unref();
}
void GprsMs::set_mode(GprsCodingScheme::Mode mode)
{
m_mode = mode;
if (!m_bts)
return;
switch (m_mode) {
case GprsCodingScheme::GPRS:
if (!m_current_cs_ul.isGprs()) {
m_current_cs_ul = GprsCodingScheme::getGprsByNum(
m_bts->bts_data()->initial_cs_ul);
if (!m_current_cs_ul.isValid())
m_current_cs_ul = GprsCodingScheme::CS1;
}
if (!m_current_cs_dl.isGprs()) {
m_current_cs_dl = GprsCodingScheme::getGprsByNum(
m_bts->bts_data()->initial_cs_dl);
if (!m_current_cs_dl.isValid())
m_current_cs_dl = GprsCodingScheme::CS1;
}
break;
case GprsCodingScheme::EGPRS_GMSK:
case GprsCodingScheme::EGPRS:
if (!m_current_cs_ul.isEgprs()) {
m_current_cs_ul = GprsCodingScheme::getEgprsByNum(
m_bts->bts_data()->initial_mcs_ul);
if (!m_current_cs_dl.isValid())
m_current_cs_ul = GprsCodingScheme::MCS1;
}
if (!m_current_cs_dl.isEgprs()) {
m_current_cs_dl = GprsCodingScheme::getEgprsByNum(
m_bts->bts_data()->initial_mcs_dl);
if (!m_current_cs_dl.isValid())
m_current_cs_dl = GprsCodingScheme::MCS1;
}
break;
}
}
void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
{
if (tbf->direction == GPRS_RLCMAC_DL_TBF)
attach_dl_tbf(as_dl_tbf(tbf));
else
attach_ul_tbf(as_ul_tbf(tbf));
}
void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
{
if (m_ul_tbf == tbf)
return;
LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
tlli(), tbf->name());
Guard guard(this);
if (m_ul_tbf)
llist_add_tail(&m_ul_tbf->ms_list(), &m_old_tbfs);
m_ul_tbf = tbf;
if (tbf)
stop_timer();
}
void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
{
if (m_dl_tbf == tbf)
return;
LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
tlli(), tbf->name());
Guard guard(this);
if (m_dl_tbf)
llist_add_tail(&m_dl_tbf->ms_list(), &m_old_tbfs);
m_dl_tbf = tbf;
if (tbf)
stop_timer();
}
void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
{
if (tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf)) {
m_ul_tbf = NULL;
} else if (tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf)) {
m_dl_tbf = NULL;
} else {
bool found = false;
LListHead<gprs_rlcmac_tbf> *pos, *tmp;
llist_for_each_safe(pos, tmp, &m_old_tbfs) {
if (pos->entry() == tbf) {
llist_del(pos);
found = true;
break;
}
}
/* Protect against recursive calls via set_ms() */
if (!found)
return;
}
LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
tlli(), tbf->name());
if (tbf->ms() == this)
tbf->set_ms(NULL);
if (!m_dl_tbf && !m_ul_tbf) {
set_reserved_slots(NULL, 0, 0);
if (tlli() != 0)
start_timer();
}
update_status();
}
void GprsMs::update_status()
{
if (m_ref > 0)
return;
if (is_idle() && !m_is_idle) {
m_is_idle = true;
m_cb->ms_idle(this);
/* this can be deleted by now, do not access it */
return;
}
if (!is_idle() && m_is_idle) {
m_is_idle = false;
m_cb->ms_active(this);
}
}
void GprsMs::reset()
{
LOGP(DRLCMAC, LOGL_INFO,
"Clearing MS object, TLLI: 0x%08x, IMSI: '%s'\n",
tlli(), imsi());
stop_timer();
m_tlli = 0;
m_new_dl_tlli = 0;
m_new_ul_tlli = 0;
m_imsi[0] = '\0';
}
void GprsMs::merge_old_ms(GprsMs *old_ms)
{
if (old_ms == this)
return;
if (strlen(imsi()) == 0 && strlen(old_ms->imsi()) != 0)
set_imsi(old_ms->imsi());
if (!ms_class() && old_ms->ms_class())
set_ms_class(old_ms->ms_class());
m_llc_queue.move_and_merge(&old_ms->m_llc_queue);
old_ms->reset();
}
void GprsMs::set_tlli(uint32_t tlli)
{
if (tlli == m_tlli || tlli == m_new_ul_tlli)
return;
if (tlli != m_new_dl_tlli) {
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
"not yet confirmed\n",
this->tlli(), tlli);
m_new_ul_tlli = tlli;
return;
}
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
"already confirmed partly\n",
m_tlli, tlli);
m_tlli = tlli;
m_new_dl_tlli = 0;
m_new_ul_tlli = 0;
}
bool GprsMs::confirm_tlli(uint32_t tlli)
{
if (tlli == m_tlli || tlli == m_new_dl_tlli)
return false;
if (tlli != m_new_ul_tlli) {
/* The MS has not sent a message with the new TLLI, which may
* happen according to the spec [TODO: add reference]. */
LOGP(DRLCMAC, LOGL_INFO,
"The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
"partly confirmed\n", tlli);
/* Use the network's idea of TLLI as candidate, this does not
* change the result value of tlli() */
m_new_dl_tlli = tlli;
return false;
}
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
m_tlli = tlli;
m_new_dl_tlli = 0;
m_new_ul_tlli = 0;
return true;
}
void GprsMs::set_imsi(const char *imsi)
{
if (!imsi) {
LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
return;
}
if (imsi[0] && strlen(imsi) < 3) {
LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
imsi);
return;
}
if (strcmp(imsi, m_imsi) == 0)
return;
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
tlli(), m_imsi, imsi);
strncpy(m_imsi, imsi, sizeof(m_imsi));
m_imsi[sizeof(m_imsi) - 1] = '\0';
}
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_;
}
void GprsMs::set_ms_class(uint8_t ms_class_)
{
if (ms_class_ == m_ms_class)
return;
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
tlli(), m_ms_class, ms_class_);
m_ms_class = ms_class_;
}
void GprsMs::set_egprs_ms_class(uint8_t ms_class_)
{
if (ms_class_ == m_egprs_ms_class)
return;
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
tlli(), m_egprs_ms_class, ms_class_);
m_egprs_ms_class = ms_class_;
}
void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
{
struct gprs_rlcmac_bts *bts_data;
int64_t now;
GprsCodingScheme max_cs_dl = this->max_cs_dl();
OSMO_ASSERT(max_cs_dl);
bts_data = m_bts->bts_data();
if (error_rate < 0)
return;
now = now_msec();
/* TODO: Check for TBF direction */
/* TODO: Support different CS values for UL and DL */
m_nack_rate_dl = error_rate;
if (error_rate > bts_data->cs_adj_upper_limit) {
if (m_current_cs_dl.to_num() > 1) {
m_current_cs_dl.dec(mode());
LOGP(DRLCMACDL, LOGL_INFO,
"MS (IMSI %s): High error rate %d%%, "
"reducing CS level to %s\n",
imsi(), error_rate, m_current_cs_dl.name());
m_last_cs_not_low = now;
}
} else if (error_rate < bts_data->cs_adj_lower_limit) {
if (m_current_cs_dl < max_cs_dl) {
if (now - m_last_cs_not_low > 1000) {
m_current_cs_dl.inc(mode());
LOGP(DRLCMACDL, LOGL_INFO,
"MS (IMSI %s): Low error rate %d%%, "
"increasing DL CS level to %s\n",
imsi(), error_rate,
m_current_cs_dl.name());
m_last_cs_not_low = now;
} else {
LOGP(DRLCMACDL, LOGL_DEBUG,
"MS (IMSI %s): Low error rate %d%%, "
"ignored (within blocking period)\n",
imsi(), error_rate);
}
}
} else {
LOGP(DRLCMACDL, LOGL_DEBUG,
"MS (IMSI %s): Medium error rate %d%%, ignored\n",
imsi(), error_rate);
m_last_cs_not_low = now;
}
}
GprsCodingScheme GprsMs::max_cs_ul() const
{
struct gprs_rlcmac_bts *bts_data;
OSMO_ASSERT(m_bts != NULL);
bts_data = m_bts->bts_data();
if (m_current_cs_ul.isGprs()) {
if (!bts_data->max_cs_ul)
return GprsCodingScheme(GprsCodingScheme::CS4);
return GprsCodingScheme::getGprsByNum(bts_data->max_cs_ul);
}
if (!m_current_cs_ul.isEgprs())
return GprsCodingScheme(); /* UNKNOWN */
if (bts_data->max_mcs_ul)
return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_ul);
else if (bts_data->max_cs_ul)
return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_ul);
return GprsCodingScheme(GprsCodingScheme::MCS4);
}
GprsCodingScheme GprsMs::max_cs_dl() const
{
struct gprs_rlcmac_bts *bts_data;
OSMO_ASSERT(m_bts != NULL);
bts_data = m_bts->bts_data();
if (m_current_cs_dl.isGprs()) {
if (!bts_data->max_cs_dl)
return GprsCodingScheme(GprsCodingScheme::CS4);
return GprsCodingScheme::getGprsByNum(bts_data->max_cs_dl);
}
if (!m_current_cs_dl.isEgprs())
return GprsCodingScheme(); /* UNKNOWN */
if (bts_data->max_mcs_dl)
return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_dl);
else if (bts_data->max_cs_dl)
return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_dl);
return GprsCodingScheme(GprsCodingScheme::MCS4);
}
void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
{
struct gprs_rlcmac_bts *bts_data;
GprsCodingScheme max_cs_ul = this->max_cs_ul();
int old_link_qual;
int low;
int high;
GprsCodingScheme new_cs_ul = m_current_cs_ul;
unsigned current_cs_num = m_current_cs_ul.to_num();
bts_data = m_bts->bts_data();
if (!max_cs_ul) {
LOGP(DRLCMACDL, LOGL_ERROR,
"max_cs_ul cannot be derived (current UL CS: %s)\n",
m_current_cs_ul.name());
return;
}
if (!m_current_cs_ul)
return;
if (!meas->have_link_qual)
return;
old_link_qual = meas->link_qual;
if (m_current_cs_ul.isGprs()) {
low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
} else if (m_current_cs_ul.isEgprs()) {
/* TODO, use separate table */
if (current_cs_num > 4)
current_cs_num = 4;
low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
} else {
return;
}
if (m_l1_meas.have_link_qual)
old_link_qual = m_l1_meas.link_qual;
if (meas->link_qual < low && old_link_qual < low)
new_cs_ul.dec(mode());
else if (meas->link_qual > high && old_link_qual > high &&
m_current_cs_ul < max_cs_ul)
new_cs_ul.inc(mode());
if (m_current_cs_ul != new_cs_ul) {
LOGP(DRLCMACDL, LOGL_INFO,
"MS (IMSI %s): "
"Link quality %ddB (%ddB) left window [%d, %d], "
"modifying uplink CS level: %s -> %s\n",
imsi(), meas->link_qual, old_link_qual,
low, high,
m_current_cs_ul.name(), new_cs_ul.name());
m_current_cs_ul = new_cs_ul;
}
}
void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
{
unsigned i;
update_cs_ul(meas);
if (meas->have_rssi)
m_l1_meas.set_rssi(meas->rssi);
if (meas->have_bto)
m_l1_meas.set_bto(meas->bto);
if (meas->have_ber)
m_l1_meas.set_ber(meas->ber);
if (meas->have_link_qual)
m_l1_meas.set_link_qual(meas->link_qual);
if (meas->have_ms_rx_qual)
m_l1_meas.set_ms_rx_qual(meas->ms_rx_qual);
if (meas->have_ms_c_value)
m_l1_meas.set_ms_c_value(meas->ms_c_value);
if (meas->have_ms_sign_var)
m_l1_meas.set_ms_sign_var(meas->ms_sign_var);
if (meas->have_ms_i_level) {
for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
if (meas->ts[i].have_ms_i_level)
m_l1_meas.set_ms_i_level(i, meas->ts[i].ms_i_level);
else
m_l1_meas.ts[i].have_ms_i_level = 0;
}
}
}
GprsCodingScheme GprsMs::current_cs_dl() const
{
GprsCodingScheme cs = m_current_cs_dl;
size_t unencoded_octets;
if (!m_bts)
return cs;
unencoded_octets = m_llc_queue.octets();
/* If the DL TBF is active, add number of unencoded chunk octets */
if (m_dl_tbf)
unencoded_octets += m_dl_tbf->m_llc.chunk_size();
/* There are many unencoded octets, don't reduce */
if (unencoded_octets >= m_bts->bts_data()->cs_downgrade_threshold)
return cs;
/* RF conditions are good, don't reduce */
if (m_nack_rate_dl < m_bts->bts_data()->cs_adj_lower_limit)
return cs;
/* The throughput would probably be better if the CS level was reduced */
cs.dec(mode());
/* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
if (cs == GprsCodingScheme(GprsCodingScheme::CS2))
cs.dec(mode());
return cs;
}
int GprsMs::first_common_ts() const
{
if (m_dl_tbf)
return m_dl_tbf->first_common_ts;
if (m_ul_tbf)
return m_ul_tbf->first_common_ts;
return -1;
}
uint8_t GprsMs::dl_slots() const
{
uint8_t slots = 0;
if (m_dl_tbf)
slots |= m_dl_tbf->dl_slots();
if (m_ul_tbf)
slots |= m_ul_tbf->dl_slots();
return slots;
}
uint8_t GprsMs::ul_slots() const
{
uint8_t slots = 0;
if (m_dl_tbf)
slots |= m_dl_tbf->ul_slots();
if (m_ul_tbf)
slots |= m_ul_tbf->ul_slots();
return slots;
}
uint8_t GprsMs::current_pacch_slots() const
{
uint8_t slots = 0;
bool is_dl_active = m_dl_tbf && m_dl_tbf->is_tfi_assigned();
bool is_ul_active = m_ul_tbf && m_ul_tbf->is_tfi_assigned();
if (!is_dl_active && !is_ul_active)
return 0;
/* see TS 44.060, 8.1.1.2.2 */
if (is_dl_active && !is_ul_active)
slots = m_dl_tbf->dl_slots();
else if (!is_dl_active && is_ul_active)
slots = m_ul_tbf->ul_slots();
else
slots = m_ul_tbf->ul_slots() & m_dl_tbf->dl_slots();
/* Assume a multislot class 1 device */
/* TODO: For class 2 devices, this could be removed */
slots = pcu_lsb(slots);
return slots;
}
void GprsMs::set_reserved_slots(gprs_rlcmac_trx *trx,
uint8_t ul_slots, uint8_t dl_slots)
{
if (m_current_trx) {
m_current_trx->unreserve_slots(GPRS_RLCMAC_DL_TBF,
m_reserved_dl_slots);
m_current_trx->unreserve_slots(GPRS_RLCMAC_UL_TBF,
m_reserved_ul_slots);
m_reserved_dl_slots = 0;
m_reserved_ul_slots = 0;
}
m_current_trx = trx;
if (trx) {
m_reserved_dl_slots = dl_slots;
m_reserved_ul_slots = ul_slots;
m_current_trx->reserve_slots(GPRS_RLCMAC_DL_TBF,
m_reserved_dl_slots);
m_current_trx->reserve_slots(GPRS_RLCMAC_UL_TBF,
m_reserved_ul_slots);
}
}
gprs_rlcmac_tbf *GprsMs::tbf(enum gprs_rlcmac_tbf_direction dir) const
{
switch (dir) {
case GPRS_RLCMAC_DL_TBF: return m_dl_tbf;
case GPRS_RLCMAC_UL_TBF: return m_ul_tbf;
}
return NULL;
}

278
src/gprs_ms.h Normal file
View File

@ -0,0 +1,278 @@
/* gprs_ms.h
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
struct gprs_rlcmac_tbf;
struct gprs_rlcmac_dl_tbf;
struct gprs_rlcmac_ul_tbf;
struct gprs_codel;
#include "cxx_linuxlist.h"
#include "llc.h"
#include "tbf.h"
#include "pcu_l1_if.h"
extern "C" {
#include <osmocom/core/timer.h>
}
#include <stdint.h>
#include <stddef.h>
struct BTS;
struct gprs_rlcmac_trx;
class GprsMs {
public:
struct Callback {
virtual void ms_idle(class GprsMs *) = 0;
virtual void ms_active(class GprsMs *) = 0;
};
class Guard {
public:
Guard(GprsMs *ms);
~Guard();
bool is_idle() const;
private:
GprsMs * const m_ms;
};
GprsMs(BTS *bts, uint32_t tlli);
~GprsMs();
void set_callback(Callback *cb) {m_cb = cb;}
void merge_old_ms(GprsMs *old_ms);
gprs_rlcmac_ul_tbf *ul_tbf() const {return m_ul_tbf;}
gprs_rlcmac_dl_tbf *dl_tbf() const {return m_dl_tbf;}
gprs_rlcmac_tbf *tbf(enum gprs_rlcmac_tbf_direction dir) const;
uint32_t tlli() const;
void set_tlli(uint32_t tlli);
bool confirm_tlli(uint32_t tlli);
bool check_tlli(uint32_t tlli);
void reset();
GprsCodingScheme::Mode mode() const;
void set_mode(GprsCodingScheme::Mode mode);
const char *imsi() const;
void set_imsi(const char *imsi);
uint8_t ta() const;
void set_ta(uint8_t ta);
uint8_t ms_class() const;
uint8_t egprs_ms_class() const;
void set_ms_class(uint8_t ms_class);
void set_egprs_ms_class(uint8_t ms_class);
GprsCodingScheme current_cs_ul() const;
GprsCodingScheme current_cs_dl() const;
GprsCodingScheme max_cs_ul() const;
GprsCodingScheme max_cs_dl() const;
int first_common_ts() const;
uint8_t dl_slots() const;
uint8_t ul_slots() const;
uint8_t reserved_dl_slots() const;
uint8_t reserved_ul_slots() const;
uint8_t current_pacch_slots() const;
gprs_rlcmac_trx *current_trx() const;
void set_reserved_slots(gprs_rlcmac_trx *trx,
uint8_t ul_slots, uint8_t dl_slots);
gprs_llc_queue *llc_queue();
const gprs_llc_queue *llc_queue() const;
gprs_codel *codel_state() const;
void set_timeout(unsigned secs);
void attach_tbf(gprs_rlcmac_tbf *tbf);
void attach_ul_tbf(gprs_rlcmac_ul_tbf *tbf);
void attach_dl_tbf(gprs_rlcmac_dl_tbf *tbf);
void detach_tbf(gprs_rlcmac_tbf *tbf);
void update_error_rate(gprs_rlcmac_tbf *tbf, int percent);
bool is_idle() const;
bool need_dl_tbf() const;
void* operator new(size_t num);
void operator delete(void* p);
LListHead<GprsMs>& list() {return this->m_list;}
const LListHead<GprsMs>& list() const {return this->m_list;}
const LListHead<gprs_rlcmac_tbf>& old_tbfs() const {return m_old_tbfs;}
void update_l1_meas(const pcu_l1_meas *meas);
const pcu_l1_meas* l1_meas() const {return &m_l1_meas;};
unsigned nack_rate_dl() const;
/* internal use */
static void timeout(void *priv_);
protected:
void update_status();
GprsMs *ref();
void unref();
void start_timer();
void stop_timer();
void update_cs_ul(const pcu_l1_meas*);
private:
BTS *m_bts;
Callback * m_cb;
gprs_rlcmac_ul_tbf *m_ul_tbf;
gprs_rlcmac_dl_tbf *m_dl_tbf;
LListHead<gprs_rlcmac_tbf> m_old_tbfs;
uint32_t m_tlli;
uint32_t m_new_ul_tlli;
uint32_t m_new_dl_tlli;
/* store IMSI for look-up and PCH retransmission */
char m_imsi[16];
uint8_t m_ta;
uint8_t m_ms_class;
uint8_t m_egprs_ms_class;
/* current coding scheme */
GprsCodingScheme m_current_cs_ul;
GprsCodingScheme m_current_cs_dl;
gprs_llc_queue m_llc_queue;
bool m_is_idle;
int m_ref;
LListHead<GprsMs> m_list;
struct osmo_timer_list m_timer;
unsigned m_delay;
int64_t m_last_cs_not_low;
pcu_l1_meas m_l1_meas;
unsigned m_nack_rate_dl;
uint8_t m_reserved_dl_slots;
uint8_t m_reserved_ul_slots;
gprs_rlcmac_trx *m_current_trx;
struct gprs_codel *m_codel_state;
GprsCodingScheme::Mode m_mode;
};
inline bool GprsMs::is_idle() const
{
return !m_ul_tbf && !m_dl_tbf && !m_ref && llist_empty(&m_old_tbfs);
}
inline bool GprsMs::need_dl_tbf() const
{
if (dl_tbf() != NULL && dl_tbf()->state_is_not(GPRS_RLCMAC_WAIT_RELEASE))
return false;
return llc_queue()->size() > 0;
}
inline uint32_t GprsMs::tlli() const
{
return m_new_ul_tlli ? m_new_ul_tlli :
m_tlli ? m_tlli :
m_new_dl_tlli;
}
inline bool GprsMs::check_tlli(uint32_t tlli)
{
return tlli != 0 &&
(tlli == m_tlli || tlli == m_new_ul_tlli || tlli == m_new_dl_tlli);
}
inline const char *GprsMs::imsi() const
{
return m_imsi;
}
inline uint8_t GprsMs::ta() const
{
return m_ta;
}
inline uint8_t GprsMs::ms_class() const
{
return m_ms_class;
}
inline uint8_t GprsMs::egprs_ms_class() const
{
return m_egprs_ms_class;
}
inline GprsCodingScheme GprsMs::current_cs_ul() const
{
return m_current_cs_ul;
}
inline GprsCodingScheme::Mode GprsMs::mode() const
{
return m_mode;
}
inline void GprsMs::set_timeout(unsigned secs)
{
m_delay = secs;
}
inline gprs_llc_queue *GprsMs::llc_queue()
{
return &m_llc_queue;
}
inline const gprs_llc_queue *GprsMs::llc_queue() const
{
return &m_llc_queue;
}
inline gprs_codel *GprsMs::codel_state() const
{
return m_codel_state;
}
inline unsigned GprsMs::nack_rate_dl() const
{
return m_nack_rate_dl;
}
inline uint8_t GprsMs::reserved_dl_slots() const
{
return m_reserved_dl_slots;
}
inline uint8_t GprsMs::reserved_ul_slots() const
{
return m_reserved_ul_slots;
}
inline gprs_rlcmac_trx *GprsMs::current_trx() const
{
return m_current_trx;
}

122
src/gprs_ms_storage.cpp Normal file
View File

@ -0,0 +1,122 @@
/* gprs_ms_storage.cpp
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "gprs_ms_storage.h"
#include "tbf.h"
#include "bts.h"
#include "gprs_debug.h"
#define GPRS_UNDEFINED_IMSI "000"
GprsMsStorage::GprsMsStorage(BTS *bts) :
m_bts(bts)
{
}
GprsMsStorage::~GprsMsStorage()
{
cleanup();
}
void GprsMsStorage::cleanup()
{
LListHead<GprsMs> *pos, *tmp;
llist_for_each_safe(pos, tmp, &m_list) {
GprsMs *ms = pos->entry();
ms->set_callback(NULL);
ms_idle(ms);
}
}
void GprsMsStorage::ms_idle(class GprsMs *ms)
{
llist_del(&ms->list());
if (m_bts)
m_bts->ms_present(m_bts->ms_present_get() - 1);
if (ms->is_idle())
delete ms;
}
void GprsMsStorage::ms_active(class GprsMs *ms)
{
/* Nothing to do */
}
GprsMs *GprsMsStorage::get_ms(uint32_t tlli, uint32_t old_tlli, const char *imsi) const
{
GprsMs *ms;
LListHead<GprsMs> *pos;
if (tlli || old_tlli) {
llist_for_each(pos, &m_list) {
ms = pos->entry();
if (ms->check_tlli(tlli))
return ms;
if (ms->check_tlli(old_tlli))
return ms;
}
}
/* not found by TLLI */
if (imsi && imsi[0] && strcmp(imsi, GPRS_UNDEFINED_IMSI) != 0) {
llist_for_each(pos, &m_list) {
ms = pos->entry();
if (strcmp(imsi, ms->imsi()) == 0)
return ms;
}
}
return NULL;
}
GprsMs *GprsMsStorage::create_ms()
{
GprsMs *ms;
ms = new GprsMs(m_bts, 0);
ms->set_callback(this);
llist_add(&ms->list(), &m_list);
if (m_bts)
m_bts->ms_present(m_bts->ms_present_get() + 1);
return ms;
}
GprsMs *GprsMsStorage::create_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir)
{
GprsMs *ms = get_ms(tlli);
if (ms)
return ms;
ms = create_ms();
if (dir == GPRS_RLCMAC_UL_TBF)
ms->set_tlli(tlli);
else
ms->confirm_tlli(tlli);
return ms;
}

49
src/gprs_ms_storage.h Normal file
View File

@ -0,0 +1,49 @@
/* gprs_ms_storage.h
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
#include "gprs_ms.h"
#include "cxx_linuxlist.h"
#include "tbf.h"
#include <stdint.h>
#include <stddef.h>
struct BTS;
class GprsMsStorage : public GprsMs::Callback {
public:
GprsMsStorage(BTS *bts);
~GprsMsStorage();
void cleanup();
virtual void ms_idle(class GprsMs *);
virtual void ms_active(class GprsMs *);
GprsMs *get_ms(uint32_t tlli, uint32_t old_tlli = 0, const char *imsi = 0) const;
GprsMs *create_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir);
GprsMs *create_ms();
const LListHead<GprsMs>& ms_list() const {return m_list;}
private:
BTS *m_bts;
LListHead<GprsMs> m_list;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
/* gprs_rlcmac.h
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -31,216 +32,19 @@ extern "C" {
}
#endif
/* generate a diagram for debugging timing issues */
//#define DEBUG_DIAGRAM
/* This special feature will delay assignment of downlink TBF by one second,
* in case there is already a TBF.
* This is usefull to debug downlink establishment during packet idle mode.
*/
//#define DEBUG_DL_ASS_IDLE
/*
* PDCH instanc
*/
struct gprs_rlcmac_tbf;
struct gprs_rlcmac_pdch {
uint8_t enable; /* TS is enabled */
uint8_t tsc; /* TSC of this slot */
uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */
struct llist_head paging_list; /* list of paging messages */
uint32_t last_rts_fn; /* store last frame number of RTS */
};
struct gprs_rlcmac_trx {
void *fl1h;
uint16_t arfcn;
struct gprs_rlcmac_pdch pdch[8];
struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */
};
struct gprs_rlcmac_bts {
uint8_t bsic;
uint8_t fc_interval;
uint8_t cs1;
uint8_t cs2;
uint8_t cs3;
uint8_t cs4;
uint8_t initial_cs_dl, initial_cs_ul;
uint8_t force_cs; /* 0=use from BTS 1=use from VTY */
uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
uint8_t t3142;
uint8_t t3169;
uint8_t t3191;
uint16_t t3193_msec;
uint8_t t3195;
uint8_t n3101;
uint8_t n3103;
uint8_t n3105;
struct gprs_rlcmac_trx trx[8];
int (*alloc_algorithm)(struct gprs_rlcmac_tbf *old_tbf,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single);
uint32_t alloc_algorithm_curst; /* options to customize algorithm */
uint8_t force_two_phase;
uint8_t alpha, gamma;
};
extern struct gprs_rlcmac_bts *gprs_rlcmac_bts;
struct gprs_rlcmac_bts;
struct BTS;
struct GprsMs;
#ifdef __cplusplus
/*
* TBF instance
*/
#define LLC_MAX_LEN 1543
#define RLC_MAX_SNS 128 /* GPRS, must be power of 2 */
#define RLC_MAX_WS 64 /* max window size */
#define RLC_MAX_LEN 54 /* CS-4 including spare bits */
#define Tassign_agch 0,200000 /* waiting after IMM.ASS confirm */
#define Tassign_pacch 2,0 /* timeout for pacch assigment */
enum gprs_rlcmac_tbf_state {
GPRS_RLCMAC_NULL = 0, /* new created TBF */
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 */
GPRS_RLCMAC_RELEASING, /* releasing, wait to free TBI/USF */
};
enum gprs_rlcmac_tbf_poll_state {
GPRS_RLCMAC_POLL_NONE = 0,
GPRS_RLCMAC_POLL_SCHED, /* a polling was scheduled */
};
enum gprs_rlcmac_tbf_dl_ass_state {
GPRS_RLCMAC_DL_ASS_NONE = 0,
GPRS_RLCMAC_DL_ASS_SEND_ASS, /* send downlink assignment on next RTS */
GPRS_RLCMAC_DL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
};
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_WAIT_ACK, /* wait for PACKET CONTROL ACK */
};
enum gprs_rlcmac_tbf_ul_ack_state {
GPRS_RLCMAC_UL_ACK_NONE = 0,
GPRS_RLCMAC_UL_ACK_SEND_ACK, /* send acknowledge on next RTS */
GPRS_RLCMAC_UL_ACK_WAIT_ACK, /* wait for PACKET CONTROL ACK */
};
enum gprs_rlcmac_tbf_direction {
GPRS_RLCMAC_DL_TBF,
GPRS_RLCMAC_UL_TBF
};
#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 */
#define GPRS_RLCMAC_FLAG_DL_ACK 3 /* downlink acknowledge received */
#define GPRS_RLCMAC_FLAG_TO_UL_ACK 4
#define GPRS_RLCMAC_FLAG_TO_DL_ACK 5
#define GPRS_RLCMAC_FLAG_TO_UL_ASS 6
#define GPRS_RLCMAC_FLAG_TO_DL_ASS 7
#define GPRS_RLCMAC_FLAG_TO_MASK 0xf0 /* timeout bits */
struct gprs_rlcmac_tbf {
struct llist_head list;
enum gprs_rlcmac_tbf_state state;
uint32_t state_flags;
enum gprs_rlcmac_tbf_direction direction;
uint8_t tfi;
uint32_t tlli;
uint8_t tlli_valid;
uint8_t trx;
uint16_t arfcn;
uint8_t tsc;
uint8_t first_ts; /* first TS used by TBF */
uint8_t first_common_ts; /* first TS that the phone can send and
reveive simultaniously */
uint8_t control_ts; /* timeslot control messages and polling */
uint8_t ms_class;
struct gprs_rlcmac_pdch *pdch[8]; /* list of PDCHs allocated to TBF */
uint16_t ta;
uint8_t llc_frame[LLC_MAX_LEN]; /* current DL or UL frame */
uint16_t llc_index; /* current write/read position of frame */
uint16_t llc_length; /* len of current DL LLC_frame, 0 == no frame */
struct llist_head llc_queue; /* queued LLC DL data */
enum gprs_rlcmac_tbf_dl_ass_state dl_ass_state;
enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
enum gprs_rlcmac_tbf_poll_state poll_state;
uint32_t poll_fn; /* frame number to poll */
uint16_t ws; /* window size */
uint16_t sns; /* sequence number space */
/* Please note that all variables here will be reset when changing
* from WAIT RELEASE back to FLOW state (re-use of TBF).
* All states that need reset must be in this struct, so this is why
* variables are in both (dl and ul) structs and not outside union.
*/
union {
struct {
uint16_t bsn; /* block sequence number */
uint16_t v_s; /* send state */
uint16_t v_a; /* ack state */
char v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
int32_t tx_counter; /* count all transmitted blocks */
char imsi[16]; /* store IMSI for PCH retransmission */
uint8_t wait_confirm; /* wait for CCCH IMM.ASS cnf */
} dl;
struct {
uint16_t bsn; /* block sequence number */
uint16_t v_r; /* receive state */
uint16_t v_q; /* receive window state */
char v_n[RLC_MAX_SNS/2]; /* receive state array */
int32_t rx_counter; /* count all received blocks */
uint8_t n3103; /* N3103 counter */
uint8_t usf[8]; /* list USFs per PDCH (timeslot) */
uint8_t contention_resolution_done; /* set after done */
uint8_t final_ack_sent; /* set if we sent final ack */
} ul;
} dir;
uint8_t rlc_block[RLC_MAX_SNS/2][RLC_MAX_LEN]; /* block history */
uint8_t rlc_block_len[RLC_MAX_SNS/2]; /* block len of history */
uint8_t n3105; /* N3105 counter */
struct osmo_timer_list timer;
unsigned int T; /* Txxxx number */
unsigned int num_T_exp; /* number of consecutive T expirations */
struct osmo_gsm_timer_list gsm_timer;
unsigned int fT; /* fTxxxx number */
unsigned int num_fT_exp; /* number of consecutive fT expirations */
struct timeval bw_tv; /* timestamp for bandwidth calculation */
uint32_t bw_octets; /* number of octets transmitted since bw_tv */
uint8_t cs; /* current coding scheme */
#ifdef DEBUG_DIAGRAM
int diag; /* number where TBF is presented in diagram */
int diag_new; /* used to format output of new TBF */
#endif
};
extern struct llist_head gprs_rlcmac_ul_tbfs; /* list of uplink TBFs */
extern struct llist_head gprs_rlcmac_dl_tbfs; /* list of downlink TBFs */
extern struct llist_head gprs_rlcmac_sbas; /* list of single block allocs */
/*
* paging entry
*/
@ -250,17 +54,6 @@ struct gprs_rlcmac_paging {
uint8_t identity_lv[9];
};
/*
* single block allocation entry
*/
struct gprs_rlcmac_sba {
struct llist_head list;
uint8_t trx;
uint8_t ts;
uint32_t fn;
uint8_t ta;
};
/*
* coding scheme info
*/
@ -270,46 +63,18 @@ struct gprs_rlcmac_cs {
uint8_t block_payload;
};
extern struct gprs_rlcmac_cs gprs_rlcmac_cs[];
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
uint16_t lost);
#ifdef DEBUG_DIAGRAM
void debug_diagram(int diag, const char *format, ...);
#else
#define debug_diagram(a, b, args...) ;
#endif
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf);
int sba_alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta);
int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr);
struct gprs_rlcmac_sba *sba_find(uint8_t trx, uint8_t ts, uint32_t fn);
int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi);
int tfi_alloc(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx,
int8_t use_trx);
int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf);
struct gprs_rlcmac_tbf *tbf_alloc(struct gprs_rlcmac_tbf *old_tbf,
enum gprs_rlcmac_tbf_direction dir, uint8_t tfi, uint8_t trx,
uint8_t ms_class, uint8_t single_slot);
struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, uint8_t trx,
enum gprs_rlcmac_tbf_direction dir);
struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli,
enum gprs_rlcmac_tbf_direction dir);
struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts);
void tbf_free(struct gprs_rlcmac_tbf *tbf);
int tbf_update(struct gprs_rlcmac_tbf *tbf);
int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
void tbf_new_state(struct gprs_rlcmac_tbf *tbf,
enum gprs_rlcmac_tbf_state state);
void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
unsigned int seconds, unsigned int microseconds);
void tbf_timer_stop(struct gprs_rlcmac_tbf *tbf);
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets);
/* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */
enum gprs_rlcmac_block_type {
@ -319,89 +84,34 @@ enum gprs_rlcmac_block_type {
GPRS_RLCMAC_RESERVED = 0x3
};
int gprs_rlcmac_rcv_block(uint8_t trx, uint8_t ts, uint8_t *data, uint8_t len,
uint32_t fn);
int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling,
uint32_t fn, uint8_t single_block, uint8_t alpha, uint8_t gamma,
int8_t ta_idx);
void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
struct gprs_rlcmac_tbf *tbf, uint8_t poll, uint8_t alpha,
uint8_t gamma, int8_t ta_idx);
void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll,
uint8_t alpha, uint8_t gamma, int8_t ta_idx, uint8_t ta_ts);
void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf,
uint8_t final);
int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf);
void tbf_timer_cb(void *_tbf);
int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf);
int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint8_t trx, uint8_t ts,
uint32_t fn);
struct msgb *gprs_rlcmac_send_packet_uplink_assignment(
struct gprs_rlcmac_tbf *tbf, uint32_t fn);
struct msgb *gprs_rlcmac_send_packet_downlink_assignment(
struct gprs_rlcmac_tbf *tbf, uint32_t fn);
void gprs_rlcmac_trigger_downlink_assignment(struct gprs_rlcmac_tbf *tbf,
struct gprs_rlcmac_tbf *old_tbf, char *imsi);
int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
uint8_t ssn, uint8_t *rbb);
int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
const char *imsi);
unsigned write_packet_paging_request(bitvec * dest);
unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
uint8_t *identity, uint8_t chan_needed);
int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t trx, uint8_t ts,
uint8_t *data, uint8_t len);
struct msgb *gprs_rlcmac_send_data_block_acknowledged(
struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf,
uint32_t fn);
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr);
int gprs_rlcmac_imm_ass_cnf(uint8_t *data, uint32_t fn);
int gprs_rlcmac_add_paging(uint8_t chan_needed, uint8_t *identity_lv);
struct gprs_rlcmac_paging *gprs_rlcmac_dequeue_paging(
struct gprs_rlcmac_pdch *pdch);
struct msgb *gprs_rlcmac_send_packet_paging_request(
struct gprs_rlcmac_pdch *pdch);
int gprs_alloc_max_dl_slots_per_ms(struct gprs_rlcmac_bts *bts,
uint8_t ms_class = 0);
extern "C" {
#endif
int alloc_algorithm_a(struct gprs_rlcmac_tbf *old_tbf,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single);
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts,
struct GprsMs *ms,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
int use_trx);
int alloc_algorithm_b(struct gprs_rlcmac_tbf *old_tbf,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single);
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts,
struct GprsMs *ms,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
int use_trx);
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts,
struct GprsMs *ms,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
int use_trx);
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

192
src/gprs_rlcmac_meas.cpp Normal file
View File

@ -0,0 +1,192 @@
/* Measurements
*
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <gprs_rlcmac.h>
#include <gprs_debug.h>
#include <pcu_l1_if.h>
#include <tbf.h>
#include <string.h>
#include <errno.h>
/*
* downlink measurement
*/
#warning "TODO: trigger the measurement report from the pollcontroller and use it for flow control"
/* received Measurement Report */
int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr)
{
NC_Measurement_Report_t *ncr;
NC_Measurements_t *nc;
int i;
LOGP(DRLCMACMEAS, LOGL_INFO, "Measuement Report of TLLI=0x%08x:",
pmr->TLLI);
switch (pmr->UnionType) {
case 0:
ncr = &pmr->u.NC_Measurement_Report;
LOGPC(DRLCMACMEAS, LOGL_INFO, " NC%u Serv %d dbm",
ncr->NC_MODE + 1,
ncr->Serving_Cell_Data.RXLEV_SERVING_CELL - 110);
for (i = 0; i < ncr->NUMBER_OF_NC_MEASUREMENTS; i++) {
nc = &ncr->NC_Measurements[i];
LOGPC(DRLCMACMEAS, LOGL_DEBUG, ", Neigh %u %d dbm",
nc->FREQUENCY_N, nc->RXLEV_N - 110);
}
LOGPC(DRLCMACMEAS, LOGL_INFO, "\n");
break;
case 1:
LOGPC(DRLCMACMEAS, LOGL_INFO,
" <EXT Reporting not supported!>\n");
break;
}
return 0;
}
/*
* uplink measurement
*/
/* RSSI values received from MS */
int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi)
{
struct timeval now_tv, *rssi_tv = &tbf->meas.rssi_tv;
uint32_t elapsed;
tbf->meas.rssi_sum += rssi;
tbf->meas.rssi_num++;
gettimeofday(&now_tv, NULL);
elapsed = ((now_tv.tv_sec - rssi_tv->tv_sec) << 7)
+ ((now_tv.tv_usec - rssi_tv->tv_usec) << 7) / 1000000;
if (elapsed < 128)
return 0;
gprs_rlcmac_rssi_rep(tbf);
/* reset rssi values and timestamp */
memcpy(rssi_tv, &now_tv, sizeof(struct timeval));
tbf->meas.rssi_sum = 0;
tbf->meas.rssi_num = 0;
return 0;
}
/* Give RSSI report */
int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf)
{
/* No measurement values */
if (!tbf->meas.rssi_num)
return -EINVAL;
LOGP(DRLCMACMEAS, LOGL_INFO, "UL RSSI of TLLI=0x%08x: %d dBm\n",
tbf->tlli(), tbf->meas.rssi_sum / tbf->meas.rssi_num);
return 0;
}
/*
* lost frames
*/
/* Lost frames reported from RLCMAC layer */
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
uint16_t lost)
{
struct timeval now_tv, *loss_tv = &tbf->m_bw.dl_loss_tv;
uint32_t elapsed;
uint16_t sum = received + lost;
/* No measurement values */
if (!sum)
return -EINVAL;
LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL Loss of TLLI 0x%08x: Received: %4d "
"Lost: %4d Sum: %4d\n", tbf->tlli(), received, lost, sum);
tbf->m_bw.dl_loss_received += received;
tbf->m_bw.dl_loss_lost += lost;
gettimeofday(&now_tv, NULL);
elapsed = ((now_tv.tv_sec - loss_tv->tv_sec) << 7)
+ ((now_tv.tv_usec - loss_tv->tv_usec) << 7) / 1000000;
if (elapsed < 128)
return 0;
gprs_rlcmac_lost_rep(tbf);
/* reset lost values and timestamp */
memcpy(loss_tv, &now_tv, sizeof(struct timeval));
tbf->m_bw.dl_loss_received = 0;
tbf->m_bw.dl_loss_lost = 0;
return 0;
}
/* Give Lost report */
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf)
{
uint16_t sum = tbf->m_bw.dl_loss_lost + tbf->m_bw.dl_loss_received;
/* No measurement values */
if (!sum)
return -EINVAL;
LOGP(DRLCMACMEAS, LOGL_INFO, "DL packet loss of IMSI=%s / TLLI=0x%08x: "
"%d%%\n", tbf->imsi(), tbf->tlli(),
tbf->m_bw.dl_loss_lost * 100 / sum);
return 0;
}
/*
* downlink bandwidth
*/
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets)
{
struct timeval now_tv, *bw_tv = &tbf->m_bw.dl_bw_tv;
uint32_t elapsed;
tbf->m_bw.dl_bw_octets += octets;
gettimeofday(&now_tv, NULL);
elapsed = ((now_tv.tv_sec - bw_tv->tv_sec) << 7)
+ ((now_tv.tv_usec - bw_tv->tv_usec) << 7) / 1000000;
if (elapsed < 128)
return 0;
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);
/* reset bandwidth values timestamp */
memcpy(bw_tv, &now_tv, sizeof(struct timeval));
tbf->m_bw.dl_bw_octets = 0;
return 0;
}

View File

@ -20,14 +20,21 @@
#include <gprs_bssgp_pcu.h>
#include <gprs_rlcmac.h>
#include <pcu_l1_if.h>
#include <bts.h>
#include <tbf.h>
uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
#include "pcu_utils.h"
static uint32_t sched_poll(BTS *bts,
uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
struct gprs_rlcmac_tbf **poll_tbf,
struct gprs_rlcmac_tbf **ul_ass_tbf,
struct gprs_rlcmac_tbf **dl_ass_tbf,
struct gprs_rlcmac_tbf **ul_ack_tbf)
struct gprs_rlcmac_ul_tbf **ul_ack_tbf)
{
struct gprs_rlcmac_tbf *tbf;
struct gprs_rlcmac_ul_tbf *ul_tbf;
struct gprs_rlcmac_dl_tbf *dl_tbf;
LListHead<gprs_rlcmac_tbf> *pos;
uint32_t poll_fn;
/* check special TBF for events */
@ -35,86 +42,71 @@ uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
if ((block_nr % 3) == 2)
poll_fn ++;
poll_fn = poll_fn % 2715648;
llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
llist_for_each(pos, &bts->ul_tbfs()) {
ul_tbf = as_ul_tbf(pos->entry());
OSMO_ASSERT(ul_tbf);
/* this trx, this ts */
if (tbf->trx != trx || tbf->control_ts != ts)
if (ul_tbf->trx->trx_no != trx || !ul_tbf->is_control_ts(ts))
continue;
/* polling for next uplink block */
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
&& tbf->poll_fn == poll_fn)
*poll_tbf = tbf;
if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_SEND_ACK)
*ul_ack_tbf = tbf;
if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
*dl_ass_tbf = tbf;
if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
*ul_ass_tbf = tbf;
if (ul_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
&& ul_tbf->poll_fn == poll_fn)
*poll_tbf = ul_tbf;
if (ul_tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_SEND_ACK)
*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)
*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?"
}
llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
llist_for_each(pos, &bts->dl_tbfs()) {
dl_tbf = as_dl_tbf(pos->entry());
OSMO_ASSERT(dl_tbf);
/* this trx, this ts */
if (tbf->trx != trx || tbf->control_ts != ts)
if (dl_tbf->trx->trx_no != trx || !dl_tbf->is_control_ts(ts))
continue;
/* polling for next uplink block */
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
&& tbf->poll_fn == poll_fn)
*poll_tbf = tbf;
if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
*dl_ass_tbf = tbf;
if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
*ul_ass_tbf = tbf;
if (dl_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
&& dl_tbf->poll_fn == poll_fn)
*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)
*ul_ass_tbf = dl_tbf;
}
return poll_fn;
}
uint32_t sched_sba(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr)
{
uint32_t sba_fn;
struct gprs_rlcmac_sba *sba;
/* check special TBF for events */
sba_fn = fn + 4;
if ((block_nr % 3) == 2)
sba_fn ++;
sba_fn = sba_fn % 2715648;
sba = sba_find(trx, ts, sba_fn);
if (sba) {
llist_del(&sba->list);
talloc_free(sba);
return sba_fn;
}
return 0xffffffff;
}
uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
static uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
{
struct gprs_rlcmac_tbf *tbf;
struct gprs_rlcmac_ul_tbf *tbf;
uint8_t usf = 0x07;
uint8_t i, tfi;
/* select uplink ressource */
/* select uplink resource */
for (i = 0, tfi = pdch->next_ul_tfi; i < 32;
i++, tfi = (tfi + 1) & 31) {
tbf = pdch->ul_tbf[tfi];
tbf = pdch->ul_tbf_by_tfi(tfi);
/* no TBF for this tfi, go next */
if (!tbf)
continue;
/* no UL ressources needed, go next */
/* we don't need to give ressources in FINISHED state,
/* no UL resources needed, go next */
/* we don't need to give resources in FINISHED state,
* because we have received all blocks and only poll
* for packet control ack. */
if (tbf->state != GPRS_RLCMAC_FLOW)
if (tbf->state_is_not(GPRS_RLCMAC_FLOW))
continue;
/* use this USF */
usf = tbf->dir.ul.usf[ts];
usf = tbf->m_usf[ts];
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
"TS=%d FN=%d block_nr=%d scheduling USF=%d for "
"required uplink ressource of UL TBF=%d\n", trx, ts, fn,
"required uplink resource of UL TFI=%d\n", trx, ts, fn,
block_nr, usf, tfi);
/* next TBF to handle ressource is the next one */
/* next TBF to handle resource is the next one */
pdch->next_ul_tfi = (tfi + 1) & 31;
break;
}
@ -122,41 +114,70 @@ uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
return usf;
}
struct msgb *sched_select_ctrl_msg(uint8_t trx, uint8_t ts, uint32_t fn,
static struct msgb *sched_select_ctrl_msg(
uint8_t trx, uint8_t ts, uint32_t fn,
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch,
struct gprs_rlcmac_tbf *ul_ass_tbf,
struct gprs_rlcmac_tbf *dl_ass_tbf,
struct gprs_rlcmac_tbf *ul_ack_tbf)
struct gprs_rlcmac_ul_tbf *ul_ack_tbf)
{
struct msgb *msg = NULL;
struct gprs_rlcmac_tbf *tbf = NULL;
struct gprs_rlcmac_tbf *next_list[3] = { ul_ass_tbf, dl_ass_tbf, ul_ack_tbf };
/* schedule PACKET UPLINK ASSIGNMENT (1st priority) */
if (ul_ass_tbf) {
tbf = ul_ass_tbf;
msg = gprs_rlcmac_send_packet_uplink_assignment(tbf, fn);
for (size_t i = 0; i < ARRAY_SIZE(next_list); ++i) {
tbf = next_list[(pdch->next_ctrl_prio + i) % 3];
if (!tbf)
continue;
/*
* Assignments for the same direction have lower precedence,
* because they may kill the TBF when the CONTOL 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);
else if (tbf == dl_ass_tbf && tbf->direction == GPRS_RLCMAC_UL_TBF)
msg = dl_ass_tbf->create_dl_ass(fn, ts);
else if (tbf == ul_ack_tbf)
msg = ul_ack_tbf->create_ul_ack(fn, ts);
if (!msg) {
tbf = NULL;
continue;
}
pdch->next_ctrl_prio += 1;
pdch->next_ctrl_prio %= 3;
break;
}
/* schedule PACKET DOWNLINK ASSIGNMENT (2nd priotiry) */
if (!msg && dl_ass_tbf) {
tbf = dl_ass_tbf;
msg = gprs_rlcmac_send_packet_downlink_assignment(tbf, fn);
}
/* schedule PACKET UPLINK ACK (3rd priority) */
if (!msg && ul_ack_tbf) {
tbf = ul_ack_tbf;
msg = gprs_rlcmac_send_uplink_ack(tbf, fn);
if (!msg) {
/*
* If one of these is left, the response (CONTROL ACK) from the
* MS will kill the current TBF, only one of them can be
* non-NULL
*/
if (dl_ass_tbf) {
tbf = dl_ass_tbf;
msg = dl_ass_tbf->create_dl_ass(fn, ts);
} else if (ul_ass_tbf) {
tbf = ul_ass_tbf;
msg = ul_ass_tbf->create_ul_ass(fn, ts);
}
}
/* any message */
if (msg) {
tbf->rotate_in_list();
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
"message at RTS for %s TBF=%d (TRX=%d, TS=%d)\n",
(tbf->direction == GPRS_RLCMAC_UL_TBF)
? "UL" : "DL", tbf->tfi, trx, ts);
"message at RTS for %s (TRX=%d, TS=%d)\n",
tbf_name(tbf), trx, ts);
return msg;
}
/* schedule PACKET PAGING REQUEST */
if (!llist_empty(&pdch->paging_list))
msg = gprs_rlcmac_send_packet_paging_request(pdch);
msg = pdch->packet_paging_request();
if (msg) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling paging request "
"message at RTS for (TRX=%d, TS=%d)\n", trx, ts);
@ -166,45 +187,88 @@ struct msgb *sched_select_ctrl_msg(uint8_t trx, uint8_t ts, uint32_t fn,
return NULL;
}
struct msgb *sched_select_downlink(uint8_t trx, uint8_t ts, uint32_t fn,
static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts, uint32_t fn,
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
{
struct msgb *msg = NULL;
struct gprs_rlcmac_tbf *tbf = NULL;
uint8_t i, tfi;
struct gprs_rlcmac_dl_tbf *tbf, *prio_tbf = NULL;
enum {
DL_PRIO_NONE,
DL_PRIO_SENT_DATA, /* the data has been sent and not (yet) nacked */
DL_PRIO_LOW_AGE, /* the age has reached the first threshold */
DL_PRIO_NEW_DATA, /* the data has not been sent yet or nacked */
DL_PRIO_HIGH_AGE, /* the age has reached the second threshold */
DL_PRIO_CONTROL, /* a control block needs to be sent */
} prio, max_prio = DL_PRIO_NONE;
/* select downlink ressource */
uint8_t i, tfi, prio_tfi;
int age;
const int age_thresh1 = msecs_to_frames(200);
const int high_prio_msecs =
OSMO_MIN(BTS::TIMER_T3190_MSEC/2, bts->dl_tbf_idle_msec);
const int age_thresh2 = msecs_to_frames(high_prio_msecs);
/* select downlink resource */
for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
i++, tfi = (tfi + 1) & 31) {
tbf = pdch->dl_tbf[tfi];
tbf = pdch->dl_tbf_by_tfi(tfi);
/* no TBF for this tfi, go next */
if (!tbf)
continue;
/* no DL TBF, go next */
if (tbf->direction != GPRS_RLCMAC_DL_TBF)
continue;
/* no DL ressources needed, go next */
if (tbf->state != GPRS_RLCMAC_FLOW
&& tbf->state != GPRS_RLCMAC_FINISHED)
/* no DL resources needed, go next */
if (tbf->state_is_not(GPRS_RLCMAC_FLOW)
&& tbf->state_is_not(GPRS_RLCMAC_FINISHED))
continue;
/* waiting for CCCH IMM.ASS confirm */
if (tbf->dir.dl.wait_confirm)
if (tbf->m_wait_confirm)
continue;
age = tbf->frames_since_last_poll(fn);
/* compute priority */
if (tbf->is_control_ts(ts) && tbf->need_control_ts())
prio = DL_PRIO_CONTROL;
else if (tbf->is_control_ts(ts) &&
age > age_thresh2 && age_thresh2 > 0)
prio = DL_PRIO_HIGH_AGE;
else if ((tbf->state_is(GPRS_RLCMAC_FLOW) && tbf->have_data()) ||
tbf->m_window.resend_needed() >= 0)
prio = DL_PRIO_NEW_DATA;
else if (tbf->is_control_ts(ts) &&
age > age_thresh1 && tbf->keep_open(fn))
prio = DL_PRIO_LOW_AGE;
else if (!tbf->m_window.window_empty())
prio = DL_PRIO_SENT_DATA;
else
continue;
/* get the TBF with the highest priority */
if (prio > max_prio) {
prio_tfi = tfi;
prio_tbf = tbf;
max_prio = prio;
}
}
if (prio_tbf) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data message at "
"RTS for DL TBF=%d (TRX=%d, TS=%d)\n", tfi, trx, ts);
/* next TBF to handle ressource is the next one */
pdch->next_dl_tfi = (tfi + 1) & 31;
"RTS for DL TFI=%d (TRX=%d, TS=%d) prio=%d\n",
prio_tfi, trx, ts, max_prio);
/* next TBF to handle resource is the next one */
pdch->next_dl_tfi = (prio_tfi + 1) & 31;
/* generate DL data block */
msg = gprs_rlcmac_send_data_block_acknowledged(tbf, fn,
ts);
break;
msg = prio_tbf->create_dl_acked_block(fn, ts);
}
return msg;
}
static uint8_t rlcmac_dl_idle[23] = {
static const uint8_t rlcmac_dl_idle[23] = {
0x47, /* control without optional header octets, no polling, USF=111 */
0x94, /* dummy downlink control message, paging mode 00 */
0x2b, /* no persistance level, 7 bits spare pattern */
@ -212,7 +276,7 @@ static uint8_t rlcmac_dl_idle[23] = {
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
};
struct msgb *sched_dummy(void)
static struct msgb *sched_dummy(void)
{
struct msgb *msg;
@ -224,22 +288,25 @@ struct msgb *sched_dummy(void)
return msg;
}
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_pdch *pdch;
struct gprs_rlcmac_tbf *poll_tbf = NULL, *dl_ass_tbf = NULL,
*ul_ass_tbf = NULL, *ul_ack_tbf = NULL;
*ul_ass_tbf = NULL;
struct gprs_rlcmac_ul_tbf *ul_ack_tbf = NULL;
uint8_t usf = 0x7;
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];
if (!pdch->enable) {
if (!pdch->is_enabled()) {
LOGP(DRLCMACSCHED, LOGL_ERROR, "Received RTS on disabled PDCH: "
"TRX=%d TS=%d\n", trx, ts);
return -EIO;
@ -248,25 +315,24 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
/* store last frame number of RTS */
pdch->last_rts_fn = fn;
poll_fn = sched_poll(trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
poll_fn = sched_poll(bts->bts, trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
&dl_ass_tbf, &ul_ack_tbf);
/* check uplink ressource for polling */
/* check uplink resource for polling */
if (poll_tbf)
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
"TS=%d FN=%d block_nr=%d scheduling free USF for "
"polling at FN=%d of %s TFI=%d\n", trx, ts, fn,
"polling at FN=%d of %s\n", trx, ts, fn,
block_nr, poll_fn,
(poll_tbf->direction == GPRS_RLCMAC_UL_TBF)
? "UL" : "DL", poll_tbf->tfi);
tbf_name(poll_tbf));
/* use free USF */
/* else. check for sba */
else if ((sba_fn = sched_sba(trx, ts, fn, block_nr) != 0xffffffff))
else if ((sba_fn = bts->bts->sba()->sched(trx, ts, fn, block_nr) != 0xffffffff))
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
"TS=%d FN=%d block_nr=%d scheduling free USF for "
"single block allocation at FN=%d\n", trx, ts, fn,
block_nr, sba_fn);
/* use free USF */
/* else, we search for uplink ressource */
/* else, we search for uplink resource */
else
usf = sched_select_uplink(trx, ts, fn, block_nr, pdch);
@ -276,7 +342,7 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
/* Prio 2: select data message for downlink */
if (!msg)
msg = sched_select_downlink(trx, ts, fn, block_nr, pdch);
msg = sched_select_downlink(bts, trx, ts, fn, block_nr, pdch);
/* Prio 3: send dummy contol message */
if (!msg)
@ -287,8 +353,12 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
/* msg is now available */
/* set USF */
OSMO_ASSERT(msgb_length(msg) > 0);
msg->data[0] = (msg->data[0] & 0xf8) | usf;
/* Used to measure the leak rate, count all blocks */
gprs_bssgp_update_frames_sent();
/* send PDTCH/PACCH to L1 */
pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);

1083
src/gprs_rlcmac_ts_alloc.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,7 @@
*/
#include <iostream>
#include <cstdlib>
#include <assert.h>
#include <gprs_debug.h>
using namespace std;
@ -5436,6 +5437,7 @@ void decode_gsm_rlcmac_uplink_data(bitvec * vector, RlcMacUplinkDataBlock_t * da
}
unsigned dataLen = 23 - readIndex/8;
LOGPC(DRLCMACDATA, LOGL_NOTICE, "DATA[%u] = ", dataLen);
assert(dataLen <= 20);
for (unsigned i = 0; i < dataLen; i++)
{
data->RLC_DATA[i] = bitvec_read_field(vector, readIndex, 8);
@ -5498,6 +5500,7 @@ void encode_gsm_rlcmac_downlink_data(bitvec * vector, RlcMacDownlinkDataBlock_t
}
unsigned dataNumOctets = 23 - writeIndex/8;
LOGPC(DRLCMACDATA, LOGL_NOTICE, "DATA[%u] = ", dataNumOctets);
assert(dataNumOctets <= 20);
for (unsigned i = 0; i < dataNumOctets; i++)
{
bitvec_write_field(vector, writeIndex, data->RLC_DATA[i], 8);
@ -5506,3 +5509,11 @@ void encode_gsm_rlcmac_downlink_data(bitvec * vector, RlcMacDownlinkDataBlock_t
LOGPC(DRLCMACDATA, LOGL_NOTICE, "\n");
}
}
void decode_gsm_ra_cap(bitvec * vector, MS_Radio_Access_capability_t *data)
{
csnStream_t ar;
unsigned readIndex = 0;
csnStreamInit(&ar, 0, 8 * vector->data_len);
/*ret =*/ csnStreamDecoder(&ar, CSNDESCR(MS_Radio_Access_capability_t), vector, readIndex, data);
}

View File

@ -476,7 +476,7 @@ typedef struct
typedef struct
{
guint8 LENGTH;
/* guint8 LENGTH; */
EGPRS_AckNack_Desc_t Desc;
} EGPRS_AckNack_w_len_t;
@ -5136,4 +5136,5 @@ typedef struct
void encode_gsm_rlcmac_uplink(bitvec * vector, RlcMacUplink_t * data);
void decode_gsm_rlcmac_uplink_data(bitvec * vector, RlcMacUplinkDataBlock_t * data);
void encode_gsm_rlcmac_downlink_data(bitvec * vector, RlcMacDownlinkDataBlock_t * data);
void decode_gsm_ra_cap(bitvec * vector, MS_Radio_Access_capability_t * data);
#endif /* __PACKET_GSM_RLCMAC_H__ */

View File

@ -33,9 +33,21 @@ static int *nearest_p;
#include <limits.h>
#include <gsm_timer.h>
#include <pcu_l1_if.h>
#include <bts.h>
static struct rb_root timer_root = RB_ROOT;
/*
* TODO: make this depend on the BTS. This means that
* all time functions schedule based on the BTS they
* are scheduled on.
*/
static int get_current_fn()
{
return BTS::main_bts()->current_frame_number();
}
static void __add_gsm_timer(struct osmo_gsm_timer_list *timer)
{
struct rb_node **new_node = &(timer_root.rb_node);

253
src/llc.cpp Normal file
View File

@ -0,0 +1,253 @@
/* Copied from tbf.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <tbf.h>
#include <bts.h>
#include <stdio.h>
extern "C" {
#include <osmocom/core/msgb.h>
}
/* reset LLC frame */
void gprs_llc::reset()
{
m_index = 0;
m_length = 0;
memset(frame, 0x42, sizeof(frame));
}
void gprs_llc::reset_frame_space()
{
m_index = 0;
}
/* Put an Unconfirmed Information (UI) Dummy command, see GSM 44.064, 6.4.2.2 */
void gprs_llc::put_dummy_frame(size_t req_len)
{
/* The shortest dummy command (the spec requests at least 6 octets) */
static const uint8_t llc_dummy_command[] = {
0x43, 0xc0, 0x01, 0x2b, 0x2b, 0x2b
};
static const size_t max_dummy_command_len = 79;
put_frame(llc_dummy_command, sizeof(llc_dummy_command));
if (req_len > max_dummy_command_len)
req_len = max_dummy_command_len;
/* Add further stuffing, if the requested length exceeds the minimum
* dummy command length */
while (m_length < req_len)
frame[m_length++] = 0x2b;
}
void gprs_llc::put_frame(const uint8_t *data, size_t len)
{
/* only put frames when we are empty */
OSMO_ASSERT(m_index == 0 && m_length == 0);
append_frame(data, len);
}
void gprs_llc::append_frame(const uint8_t *data, size_t len)
{
/* TODO: bounds check */
memcpy(frame + m_length, data, len);
m_length += len;
}
void gprs_llc::init()
{
reset();
}
bool gprs_llc::is_user_data_frame(uint8_t *data, size_t len)
{
if (len < 2)
return false;
if ((data[0] & 0x0f) == 1 /* GPRS_SAPI_GMM */)
return false;
if ((data[0] & 0xe0) != 0xc0 /* LLC UI */)
/* It is not an LLC UI frame, see TS 44.064, 6.3 */
return false;
return true;
}
void gprs_llc_queue::init()
{
INIT_LLIST_HEAD(&m_queue);
m_queue_size = 0;
m_queue_octets = 0;
m_avg_queue_delay = 0;
}
void gprs_llc_queue::enqueue(struct msgb *llc_msg, const MetaInfo *info)
{
static const MetaInfo def_meta = {{0}};
MetaInfo *meta_storage;
osmo_static_assert(sizeof(*info) <= sizeof(llc_msg->cb), info_does_not_fit);
m_queue_size += 1;
m_queue_octets += msgb_length(llc_msg);
meta_storage = (MetaInfo *)&llc_msg->cb[0];
*meta_storage = info ? *info : def_meta;
msgb_enqueue(&m_queue, llc_msg);
}
void gprs_llc_queue::clear(BTS *bts)
{
struct msgb *msg;
while ((msg = msgb_dequeue(&m_queue))) {
if (bts)
bts->llc_dropped_frame();
msgb_free(msg);
}
m_queue_size = 0;
m_queue_octets = 0;
}
void gprs_llc_queue::move_and_merge(gprs_llc_queue *o)
{
struct msgb *msg, *msg1 = NULL, *msg2 = NULL;
struct llist_head new_queue;
size_t queue_size = 0;
size_t queue_octets = 0;
INIT_LLIST_HEAD(&new_queue);
while (1) {
if (msg1 == NULL)
msg1 = msgb_dequeue(&m_queue);
if (msg2 == NULL)
msg2 = msgb_dequeue(&o->m_queue);
if (msg1 == NULL && msg2 == NULL)
break;
if (msg1 == NULL) {
msg = msg2;
msg2 = NULL;
} else if (msg2 == NULL) {
msg = msg1;
msg1 = NULL;
} else {
const MetaInfo *mi1 = (MetaInfo *)&msg1->cb[0];
const MetaInfo *mi2 = (MetaInfo *)&msg2->cb[0];
if (timercmp(&mi2->recv_time, &mi1->recv_time, >)) {
msg = msg1;
msg1 = NULL;
} else {
msg = msg2;
msg2 = NULL;
}
}
msgb_enqueue(&new_queue, msg);
queue_size += 1;
queue_octets += msgb_length(msg);
}
OSMO_ASSERT(llist_empty(&m_queue));
OSMO_ASSERT(llist_empty(&o->m_queue));
o->m_queue_size = 0;
o->m_queue_octets = 0;
llist_splice_init(&new_queue, &m_queue);
m_queue_size = queue_size;
m_queue_octets = queue_octets;
}
#define ALPHA 0.5f
struct msgb *gprs_llc_queue::dequeue(const MetaInfo **info)
{
struct msgb *msg;
struct timeval *tv, tv_now, tv_result;
uint32_t lifetime;
const MetaInfo *meta_storage;
msg = msgb_dequeue(&m_queue);
if (!msg)
return NULL;
meta_storage = (MetaInfo *)&msg->cb[0];
if (info)
*info = meta_storage;
m_queue_size -= 1;
m_queue_octets -= msgb_length(msg);
/* take the second time */
gettimeofday(&tv_now, NULL);
tv = (struct timeval *)&msg->data[sizeof(*tv)];
timersub(&tv_now, &meta_storage->recv_time, &tv_result);
lifetime = tv_result.tv_sec*1000 + tv_result.tv_usec/1000;
m_avg_queue_delay = m_avg_queue_delay * ALPHA + lifetime * (1-ALPHA);
return msg;
}
void gprs_llc_queue::calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec, struct timeval *tv)
{
uint16_t delay_csec;
if (bts->bts_data()->force_llc_lifetime)
delay_csec = bts->bts_data()->force_llc_lifetime;
else
delay_csec = pdu_delay_csec;
/* keep timestamp at 0 for infinite delay */
if (delay_csec == 0xffff) {
memset(tv, 0, sizeof(*tv));
return;
}
/* calculate timestamp of timeout */
struct timeval now, csec;
gettimeofday(&now, NULL);
csec.tv_usec = (delay_csec % 100) * 10000;
csec.tv_sec = delay_csec / 100;
timeradd(&now, &csec, tv);
}
bool gprs_llc_queue::is_frame_expired(const struct timeval *tv_now,
const struct timeval *tv)
{
/* Timeout is infinite */
if (tv->tv_sec == 0 && tv->tv_usec == 0)
return false;
return timercmp(tv_now, tv, >);
}

136
src/llc.h Normal file
View File

@ -0,0 +1,136 @@
/*
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
extern "C" {
#include <osmocom/core/linuxlist.h>
}
#include <stdint.h>
#include <string.h>
#include <sys/time.h>
#define LLC_MAX_LEN 1543
struct BTS;
struct timeval;
struct msgb;
/**
* I represent the LLC data to a MS
*/
struct gprs_llc {
static bool is_user_data_frame(uint8_t *data, size_t len);
void init();
void reset();
void reset_frame_space();
void put_frame(const uint8_t *data, size_t len);
void put_dummy_frame(size_t req_len);
void append_frame(const uint8_t *data, size_t len);
void consume(size_t len);
void consume(uint8_t *data, size_t len);
uint16_t chunk_size() const;
uint16_t remaining_space() const;
uint16_t frame_length() const;
bool fits_in_current_frame(uint8_t size) const;
uint8_t frame[LLC_MAX_LEN]; /* current DL or UL frame */
uint16_t m_index; /* current write/read position of frame */
uint16_t m_length; /* len of current DL LLC_frame, 0 == no frame */
};
/**
* I store the LLC frames that come from the SGSN.
*/
struct gprs_llc_queue {
struct MetaInfo {
struct timeval recv_time;
struct timeval expire_time;
};
static void calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec,
struct timeval *tv);
static bool is_frame_expired(const struct timeval *now,
const struct timeval *tv);
static bool is_user_data_frame(uint8_t *data, size_t len);
void init();
void enqueue(struct msgb *llc_msg, const MetaInfo *info = 0);
struct msgb *dequeue(const MetaInfo **info = 0);
void clear(BTS *bts);
void move_and_merge(gprs_llc_queue *o);
size_t size() const;
size_t octets() const;
private:
uint32_t m_avg_queue_delay; /* Average delay of data going through the queue */
size_t m_queue_size;
size_t m_queue_octets;
struct llist_head m_queue; /* queued LLC DL data */
};
inline uint16_t gprs_llc::chunk_size() const
{
return m_length - m_index;
}
inline uint16_t gprs_llc::remaining_space() const
{
return LLC_MAX_LEN - m_length;
}
inline uint16_t gprs_llc::frame_length() const
{
return m_length;
}
inline void gprs_llc::consume(size_t len)
{
m_index += len;
}
inline void gprs_llc::consume(uint8_t *data, size_t len)
{
/* copy and increment index */
memcpy(data, frame + m_index, len);
consume(len);
}
inline bool gprs_llc::fits_in_current_frame(uint8_t chunk_size) const
{
return m_length + chunk_size <= LLC_MAX_LEN;
}
inline size_t gprs_llc_queue::size() const
{
return this ? m_queue_size : 0;
}
inline size_t gprs_llc_queue::octets() const
{
return this ? m_queue_octets : 0;
}

View File

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

View File

@ -1,4 +1,4 @@
/* sysmo_sock.cpp
/* osmobts_sock.cpp
*
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
*
@ -36,6 +36,8 @@ extern "C" {
#include <gprs_debug.h>
#include <gprs_bssgp_pcu.h>
#include <pcuif_proto.h>
#include <bts.h>
#include <tbf.h>
extern void *tall_pcu_ctx;
@ -44,7 +46,7 @@ int l1if_close_pdch(void *obj);
}
/*
* SYSMO-PCU socket functions
* osmo-bts PCU socket functions
*/
struct pcu_sock_state {
@ -80,9 +82,8 @@ int pcu_sock_send(struct msgb *msg)
static void pcu_sock_close(struct pcu_sock_state *state, int lost)
{
struct osmo_fd *bfd = &state->conn_bfd;
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_tbf *tbf;
uint8_t trx, ts, tfi;
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t trx, ts;
LOGP(DL1IF, LOGL_NOTICE, "PCU socket has %s connection\n",
(lost) ? "LOST" : "closed");
@ -99,28 +100,20 @@ 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
if (bts->trx[trx].fl1h) {
l1if_close_pdch(bts->trx[trx].fl1h);
bts->trx[trx].fl1h = NULL;
}
#endif
for (ts = 0; ts < 8; ts++)
bts->trx[trx].pdch[ts].enable = 0;
for (tfi = 0; tfi < 32; tfi++) {
tbf = bts->trx[trx].ul_tbf[tfi];
if (tbf)
tbf_free(tbf);
tbf = bts->trx[trx].dl_tbf[tfi];
if (tbf)
tbf_free(tbf);
}
bts->trx[trx].pdch[ts].disable();
#warning "NOT ALL RESOURCES are freed in this case... inconsistent with the other code. Share the code with pcu_l1if.c for the reset."
gprs_rlcmac_tbf::free_all(&bts->trx[trx]);
}
gprs_bssgp_destroy();
if (lost) {
state->timer.cb = pcu_sock_timeout;
osmo_timer_schedule(&state->timer, 5, 0);
}
exit(0);
}
static int pcu_sock_read(struct osmo_fd *bfd)
@ -231,6 +224,8 @@ int pcu_l1if_open(void)
unsigned int namelen;
int rc;
LOGP(DL1IF, LOGL_INFO, "Opening OsmoPCU L1 interface to OsmoBTS\n");
state = pcu_sock_state;
if (!state) {
state = talloc_zero(tall_pcu_ctx, struct pcu_sock_state);
@ -243,7 +238,7 @@ int pcu_l1if_open(void)
bfd->fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (bfd->fd < 0) {
LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU-SYSMO socket.\n");
LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU socket.\n");
talloc_free(state);
return -1;
}
@ -265,8 +260,8 @@ int pcu_l1if_open(void)
#endif
rc = connect(bfd->fd, (struct sockaddr *) &local, namelen);
if (rc != 0) {
LOGP(DL1IF, LOGL_ERROR, "Failed to Connect the PCU-SYSMO "
"socket, delaying... '%s'\n", local.sun_path);
LOGP(DL1IF, LOGL_ERROR, "Failed to connect to the osmo-bts"
" PCU socket, delaying... '%s'\n", local.sun_path);
pcu_sock_state = state;
close(bfd->fd);
bfd->fd = -1;
@ -287,7 +282,7 @@ int pcu_l1if_open(void)
return rc;
}
LOGP(DL1IF, LOGL_NOTICE, "PCU-SYSMO socket has been connected\n");
LOGP(DL1IF, LOGL_NOTICE, "osmo-bts PCU socket has been connected\n");
pcu_sock_state = state;
@ -302,8 +297,7 @@ void pcu_l1if_close(void)
if (!state)
return;
if (osmo_timer_pending(&state->timer))
osmo_timer_del(&state->timer);
osmo_timer_del(&state->timer);
bfd = &state->conn_bfd;
if (bfd->fd > 0)

View File

@ -25,10 +25,13 @@
#include <assert.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/gsmtap.h>
}
#include <gprs_rlcmac.h>
@ -36,11 +39,12 @@ extern "C" {
#include <gprs_debug.h>
#include <gprs_bssgp_pcu.h>
#include <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);
int l1if_close_pdch(void *obj);
void *l1if_open_pdch(void *priv, 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);
@ -48,19 +52,6 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
extern void *tall_pcu_ctx;
// Variable for storage current FN.
int frame_number;
int get_current_fn()
{
return frame_number;
}
void set_current_fn(int fn)
{
frame_number = fn;
}
/*
* PCU messages
*/
@ -135,15 +126,18 @@ static int pcu_tx_data_req(uint8_t trx, uint8_t ts, uint8_t sapi,
void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
#ifdef ENABLE_SYSMODSP
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
if (bts->trx[trx].fl1h)
#ifdef ENABLE_SYSMODSP
if (bts->trx[trx].fl1h) {
l1if_pdch_req(bts->trx[trx].fl1h, ts, 0, fn, arfcn, block_nr,
msg->data, msg->len);
else
msgb_free(msg);
return;
}
#endif
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr,
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);
}
@ -151,15 +145,18 @@ void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
#ifdef ENABLE_SYSMODSP
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
if (bts->trx[trx].fl1h)
#ifdef ENABLE_SYSMODSP
if (bts->trx[trx].fl1h) {
l1if_pdch_req(bts->trx[trx].fl1h, ts, 1, fn, arfcn, block_nr,
msg->data, msg->len);
else
msgb_free(msg);
return;
}
#endif
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
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);
}
@ -174,7 +171,7 @@ void pcu_l1if_tx_agch(bitvec * block, int plen)
pcu_tx_data_req(0, 0, PCU_IF_SAPI_AGCH, 0, 0, 0, data, 23);
}
void pcu_l1if_tx_pch(bitvec * block, int plen, char *imsi)
void pcu_l1if_tx_pch(bitvec * block, int plen, const char *imsi)
{
uint8_t data[23+3]; /* prefix PLEN */
@ -191,31 +188,49 @@ void pcu_l1if_tx_pch(bitvec * block, int plen, char *imsi)
pcu_tx_data_req(0, 0, PCU_IF_SAPI_PCH, 0, 0, 0, data, 23+3);
}
// FIXME: remove this, when changed from c++ to c.
static void pcu_l1if_tx_bcch(uint8_t *data, int len)
extern "C" void pcu_rx_block_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no)
{
pcu_tx_data_req(0, 0, PCU_IF_SAPI_BCCH, 0, 0, 0, data, len);
BTS::main_bts()->set_current_block_frame_number(fn, 0);
}
extern "C" int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data,
uint8_t len, uint32_t fn)
extern "C" void pcu_rx_ra_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no)
{
return gprs_rlcmac_rcv_block(trx, ts, data, len, fn);
/* access bursts may arrive some bursts earlier */
BTS::main_bts()->set_current_block_frame_number(fn, 5);
}
static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
extern "C" int pcu_rx_data_ind_pdtch(uint8_t trx_no, uint8_t ts_no, uint8_t *data,
uint8_t len, uint32_t fn, struct pcu_l1_meas *meas)
{
int rc = 0;
struct gprs_rlcmac_pdch *pdch;
pdch = &bts_main_data()->trx[trx_no].pdch[ts_no];
return pdch->rcv_block(data, len, fn, meas);
}
static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind, struct gsmtap_inst *gsmtap)
{
int rc;
pcu_l1_meas meas;
meas.set_rssi(data_ind->rssi);
LOGP(DL1IF, LOGL_DEBUG, "Data indication received: sapi=%d arfcn=%d "
"block=%d data=%s\n", data_ind->sapi,
data_ind->arfcn, data_ind->block_nr,
osmo_hexdump(data_ind->data, data_ind->len));
rc = gsmtap_send(gsmtap, data_ind->arfcn | GSMTAP_ARFCN_F_UPLINK, data_ind->ts_nr,
GSMTAP_CHANNEL_PACCH, 0, data_ind->fn, 0, 0, data_ind->data, data_ind->len);
if (rc < 0)
LOGP(DL1IF, LOGL_ERROR, "Sending RX data via GSMTAP failed: %d\n", rc);
rc = 0;
switch (data_ind->sapi) {
case PCU_IF_SAPI_PDTCH:
rc = pcu_rx_data_ind_pdtch(data_ind->trx_nr, data_ind->ts_nr,
data_ind->data, data_ind->len, data_ind->fn);
data_ind->data, data_ind->len, data_ind->fn,
&meas);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with "
@ -236,8 +251,7 @@ static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
switch (data_cnf->sapi) {
case PCU_IF_SAPI_PCH:
if (data_cnf->data[2] == 0x3f)
rc = gprs_rlcmac_imm_ass_cnf(data_cnf->data,
data_cnf->fn);
BTS::main_bts()->rcv_imm_ass_cnf(data_cnf->data, data_cnf->fn);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received PCU data confirm with "
@ -252,7 +266,8 @@ static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
return gprs_rlcmac_rcv_rts_block(trx, ts, arfcn, fn, block_nr);
return gprs_rlcmac_rcv_rts_block(bts_main_data(),
trx, ts, arfcn, fn, block_nr);
}
static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
@ -296,7 +311,8 @@ static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
switch (rach_ind->sapi) {
case PCU_IF_SAPI_RACH:
rc = gprs_rlcmac_rcv_rach(rach_ind->ra, rach_ind->fn,
rc = BTS::main_bts()->rcv_rach(
rach_ind->ra, rach_ind->fn,
rach_ind->qta);
break;
default:
@ -308,40 +324,12 @@ static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
return rc;
}
int flush_pdch(struct gprs_rlcmac_pdch *pdch, uint8_t trx, uint8_t ts)
{
uint8_t tfi;
struct gprs_rlcmac_tbf *tbf;
struct gprs_rlcmac_paging *pag;
struct gprs_rlcmac_sba *sba, *sba2;
/* kick all TBF on slot */
for (tfi = 0; tfi < 32; tfi++) {
tbf = pdch->ul_tbf[tfi];
if (tbf)
tbf_free(tbf);
tbf = pdch->dl_tbf[tfi];
if (tbf)
tbf_free(tbf);
}
/* flush all pending paging messages */
while ((pag = gprs_rlcmac_dequeue_paging(pdch)))
talloc_free(pag);
llist_for_each_entry_safe(sba, sba2, &gprs_rlcmac_sbas, list) {
if (sba->trx == trx && sba->ts == ts) {
llist_del(&sba->list);
talloc_free(sba);
}
}
return 0;
}
static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
struct gprs_bssgp_pcu *pcu;
struct gprs_rlcmac_pdch *pdch;
struct in_addr ia;
int rc = 0;
int trx, ts;
int i;
@ -361,14 +349,11 @@ bssgp_failed:
/* free all TBF */
for (trx = 0; trx < 8; trx++) {
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
for (ts = 0; ts < 8; ts++) {
if (bts->trx[trx].pdch[ts].enable)
flush_pdch(&bts->trx[trx].pdch[ts],
trx, ts);
}
for (ts = 0; ts < 8; ts++)
bts->trx[trx].pdch[ts].free_resources();
}
gprs_bssgp_destroy();
return 0;
exit(0);
}
LOGP(DL1IF, LOGL_INFO, "BTS available\n");
LOGP(DL1IF, LOGL_DEBUG, " mcc=%x\n", info_ind->mcc);
@ -419,13 +404,15 @@ bssgp_failed:
LOGP(DL1IF, LOGL_DEBUG, " nsvci=%d\n", info_ind->nsvci[0]);
LOGP(DL1IF, LOGL_DEBUG, " local_port=%d\n", info_ind->local_port[0]);
LOGP(DL1IF, LOGL_DEBUG, " remote_port=%d\n", info_ind->remote_port[0]);
LOGP(DL1IF, LOGL_DEBUG, " remote_ip=%d\n", info_ind->remote_ip[0]);
ia.s_addr = htonl(info_ind->remote_ip[0]);
LOGP(DL1IF, LOGL_DEBUG, " remote_ip=%s\n", inet_ntoa(ia));
rc = gprs_bssgp_create(info_ind->remote_ip[0], info_ind->remote_port[0],
pcu = gprs_bssgp_create_and_connect(bts, info_ind->local_port[0],
info_ind->remote_ip[0], info_ind->remote_port[0],
info_ind->nsei, info_ind->nsvci[0], info_ind->bvci,
info_ind->mcc, info_ind->mnc, info_ind->lac, info_ind->rac,
info_ind->cell_id);
if (rc < 0) {
if (!pcu) {
LOGP(DL1IF, LOGL_NOTICE, "SGSN not available\n");
goto bssgp_failed;
}
@ -464,7 +451,8 @@ bssgp_failed:
if (!bts->trx[trx].fl1h)
bts->trx[trx].fl1h = l1if_open_pdch(
(void *)trx,
info_ind->trx[trx].hlayer1);
info_ind->trx[trx].hlayer1,
bts->gsmtap);
if (!bts->trx[trx].fl1h) {
LOGP(DL1IF, LOGL_FATAL, "Failed to open direct "
"DSP access for PDCH.\n");
@ -476,14 +464,13 @@ bssgp_failed:
"BTS. Please deactivate it!\n");
exit(0);
#endif
#warning close TBD
}
for (ts = 0; ts < 8; ts++) {
pdch = &bts->trx[trx].pdch[ts];
if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
/* FIXME: activate dynamically at RLCMAC */
if (!pdch->enable) {
if (!pdch->is_enabled()) {
#ifdef ENABLE_SYSMODSP
if ((info_ind->flags &
PCU_IF_FLAG_SYSMO))
@ -491,17 +478,16 @@ bssgp_failed:
bts->trx[trx].fl1h, ts);
#endif
pcu_tx_act_req(trx, ts, 1);
INIT_LLIST_HEAD(&pdch->paging_list);
pdch->enable = 1;
pdch->enable();
}
pdch->tsc = info_ind->trx[trx].tsc[ts];
LOGP(DL1IF, LOGL_INFO, "PDCH: trx=%d ts=%d\n",
trx, ts);
} else {
if (pdch->enable) {
if (pdch->is_enabled()) {
pcu_tx_act_req(trx, ts, 0);
pdch->enable = 0;
flush_pdch(pdch, trx, ts);
pdch->free_resources();
pdch->disable();
}
}
}
@ -512,8 +498,6 @@ bssgp_failed:
static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
{
struct gprs_rlcmac_tbf *tbf;
uint32_t elapsed;
uint8_t fn13 = time_ind->fn % 13;
/* omit frame numbers not starting at a MAC block */
@ -523,24 +507,7 @@ static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
// LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n",
// time_ind->fn % 52);
set_current_fn(time_ind->fn);
/* check for poll timeout */
llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
elapsed = (frame_number - tbf->poll_fn) % 2715648;
if (elapsed >= 20 && elapsed < 200)
gprs_rlcmac_poll_timeout(tbf);
}
}
llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
elapsed = (frame_number - tbf->poll_fn) % 2715648;
if (elapsed >= 20 && elapsed < 200)
gprs_rlcmac_poll_timeout(tbf);
}
}
BTS::main_bts()->set_current_frame_number(time_ind->fn);
return 0;
}
@ -549,17 +516,18 @@ static int pcu_rx_pag_req(struct gsm_pcu_if_pag_req *pag_req)
LOGP(DL1IF, LOGL_DEBUG, "Paging request received: chan_needed=%d "
"length=%d\n", pag_req->chan_needed, pag_req->identity_lv[0]);
return gprs_rlcmac_add_paging(pag_req->chan_needed,
return BTS::main_bts()->add_paging(pag_req->chan_needed,
pag_req->identity_lv);
}
int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim)
{
int rc = 0;
struct gprs_rlcmac_bts *bts = bts_main_data();
switch (msg_type) {
case PCU_IF_MSG_DATA_IND:
rc = pcu_rx_data_ind(&pcu_prim->u.data_ind);
rc = pcu_rx_data_ind(&pcu_prim->u.data_ind, bts->gsmtap);
break;
case PCU_IF_MSG_DATA_CNF:
rc = pcu_rx_data_cnf(&pcu_prim->u.data_cnf);

View File

@ -31,16 +31,88 @@ extern "C" {
#include <osmocom/gsm/gsm_utils.h>
#ifdef __cplusplus
}
#endif
int get_current_fn();
/*
* L1 Measurement values
*/
struct pcu_l1_meas_ts {
unsigned have_ms_i_level:1;
int16_t ms_i_level; /* I_LEVEL in dB */
#ifdef __cplusplus
pcu_l1_meas_ts& set_ms_i_level(int16_t v) {
ms_i_level = v; have_ms_i_level = 1; return *this;
}
pcu_l1_meas_ts() :
have_ms_i_level(0)
{}
#endif
};
struct pcu_l1_meas {
unsigned have_rssi:1;
unsigned have_ber:1;
unsigned have_bto:1;
unsigned have_link_qual:1;
unsigned have_ms_rx_qual:1;
unsigned have_ms_c_value:1;
unsigned have_ms_sign_var:1;
unsigned have_ms_i_level:1;
int8_t rssi; /* RSSI in dBm */
uint8_t ber; /* Bit error rate in % */
int16_t bto; /* Burst timing offset in quarter bits */
int16_t link_qual; /* Link quality in dB */
int16_t ms_rx_qual; /* MS RXQUAL value in % */
int16_t ms_c_value; /* C value in dB */
int16_t ms_sign_var; /* SIGN_VAR in dB */
struct pcu_l1_meas_ts ts[8];
#ifdef __cplusplus
pcu_l1_meas& set_rssi(int8_t v) { rssi = v; have_rssi = 1; return *this;}
pcu_l1_meas& set_ber(uint8_t v) { ber = v; have_ber = 1; return *this;}
pcu_l1_meas& set_bto(int16_t v) { bto = v; have_bto = 1; return *this;}
pcu_l1_meas& set_link_qual(int16_t v) {
link_qual = v; have_link_qual = 1; return *this;
}
pcu_l1_meas& set_ms_rx_qual(int16_t v) {
ms_rx_qual = v; have_ms_rx_qual = 1; return *this;
}
pcu_l1_meas& set_ms_c_value(int16_t v) {
ms_c_value = v; have_ms_c_value = 1; return *this;
}
pcu_l1_meas& set_ms_sign_var(int16_t v) {
ms_sign_var = v; have_ms_sign_var = 1; return *this;
}
pcu_l1_meas& set_ms_i_level(size_t idx, int16_t v) {
ts[idx].set_ms_i_level(v); have_ms_i_level = 1; return *this;
}
pcu_l1_meas() :
have_rssi(0),
have_ber(0),
have_bto(0),
have_link_qual(0),
have_ms_rx_qual(0),
have_ms_c_value(0),
have_ms_sign_var(0),
have_ms_i_level(0)
{}
#endif
};
#ifdef __cplusplus
void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr);
void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr);
void pcu_l1if_tx_agch(bitvec * block, int len);
void pcu_l1if_tx_pch(bitvec * block, int plen, char *imsi);
void pcu_l1if_tx_pch(bitvec * block, int plen, const char *imsi);
int pcu_l1if_open(void);
void pcu_l1if_close(void);
@ -50,15 +122,18 @@ int pcu_sock_send(struct msgb *msg);
#endif
#ifdef __cplusplus
extern "C"
extern "C" {
#endif
int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr);
#ifdef __cplusplus
extern "C"
#endif
int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data,
uint8_t len, uint32_t fn);
uint8_t len, uint32_t fn, struct pcu_l1_meas *meas);
void pcu_rx_block_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no);
void pcu_rx_ra_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no);
#ifdef __cplusplus
}
#endif
#endif // PCU_L1_IF_H

View File

@ -26,25 +26,27 @@
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <sched.h>
#include <bts.h>
extern "C" {
#include "pcu_vty.h"
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/gsmtap_util.h>
}
struct gprs_rlcmac_bts *gprs_rlcmac_bts;
extern struct gprs_nsvc *nsvc;
uint16_t spoof_mcc = 0, spoof_mnc = 0;
static int config_given = 0;
static const char *config_file = "osmo-pcu.cfg";
static char *config_file = strdup("osmo-pcu.cfg");
extern struct vty_app_info pcu_vty_info;
void *tall_pcu_ctx;
extern void *bv_tall_ctx;
static int quit = 0;
#ifdef DEBUG_DIAGRAM
extern struct timeval diagram_time;
#endif
static int rt_prio = -1;
static char *gsmtap_addr = "localhost"; // FIXME: use gengetopt's default value instead
static void print_help()
{
@ -56,6 +58,10 @@ static void print_help()
"provided by BTS\n"
" -n --mnc MNC use given MNC instead of value "
"provided by BTS\n"
" -V --version print version\n"
" -r --realtime PRIO Use SCHED_RR with the specified "
"priority\n"
" -i --gsmtap-ip The destination IP used for GSMTAP.\n"
);
}
@ -70,10 +76,13 @@ static void handle_options(int argc, char **argv)
{ "mcc", 1, 0, 'm' },
{ "mnc", 1, 0, 'n' },
{ "version", 0, 0, 'V' },
{ "realtime", 1, 0, 'r' },
{ "exit", 0, 0, 'e' },
{ "gsmtap-ip", 1, 0, 'i' },
{ 0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "hc:m:n:V",
c = getopt_long(argc, argv, "hc:m:n:Vr:e:i:",
long_options, &option_idx);
if (c == -1)
break;
@ -84,6 +93,7 @@ static void handle_options(int argc, char **argv)
exit(0);
break;
case 'c':
free(config_file);
config_file = strdup(optarg);
config_given = 1;
break;
@ -97,6 +107,15 @@ static void handle_options(int argc, char **argv)
print_version(1);
exit(0);
break;
case 'i':
gsmtap_addr = optarg;
break;
case 'r':
rt_prio = atoi(optarg);
break;
case 'e':
fprintf(stderr, "Warning: Option '-e' is deprecated!\n");
break;
default:
fprintf(stderr, "Unknown option '%c'\n", c);
exit(0);
@ -114,6 +133,7 @@ void sighandler(int sigset)
switch (sigset) {
case SIGINT:
case SIGTERM:
/* If another signal is received afterwards, the program
* is terminated without finishing shutdown process.
*/
@ -140,6 +160,7 @@ void sighandler(int sigset)
int main(int argc, char *argv[])
{
struct sched_param param;
struct gprs_rlcmac_bts *bts;
int rc;
@ -148,12 +169,10 @@ int main(int argc, char *argv[])
return -ENOMEM;
bv_tall_ctx = tall_pcu_ctx;
bts = gprs_rlcmac_bts = talloc_zero(tall_pcu_ctx,
struct gprs_rlcmac_bts);
if (!gprs_rlcmac_bts)
return -ENOMEM;
bts = bts_main_data();
bts->fc_interval = 1;
bts->initial_cs_dl = bts->initial_cs_ul = 1;
bts->initial_mcs_dl = bts->initial_mcs_ul = 1;
bts->cs1 = 1;
bts->t3142 = 20;
bts->t3169 = 5;
@ -163,11 +182,40 @@ int main(int argc, char *argv[])
bts->n3101 = 10;
bts->n3103 = 4;
bts->n3105 = 8;
bts->alpha = 10; /* a = 1.0 */
bts->alpha = 0; /* a = 0.0 */
bts->ms_idle_sec = 60; /* slightly above T3314 (default 44s, 24.008, 11.2.2) */
bts->cs_adj_enabled = 1;
bts->cs_adj_upper_limit = 33; /* Decrease CS if the error rate is above */
bts->cs_adj_lower_limit = 10; /* Increase CS if the error rate is below */
bts->max_cs_ul = 4;
bts->max_cs_dl = 4;
bts->max_mcs_ul = 4;
bts->max_mcs_dl = 4;
/* CS-1 to CS-4 */
bts->cs_lqual_ranges[0].low = -256;
bts->cs_lqual_ranges[0].high = 6;
bts->cs_lqual_ranges[1].low = 5;
bts->cs_lqual_ranges[1].high = 8;
bts->cs_lqual_ranges[2].low = 7;
bts->cs_lqual_ranges[2].high = 13;
bts->cs_lqual_ranges[3].low = 12;
bts->cs_lqual_ranges[3].high = 256;
bts->cs_downgrade_threshold = 200;
/* TODO: increase them when CRBB decoding is implemented */
bts->ws_base = 64;
bts->ws_pdch = 0;
bts->llc_codel_interval_msec = LLC_CODEL_USE_DEFAULT;
bts->dl_tbf_idle_msec = 2000;
bts->llc_idle_ack_csec = 10;
msgb_set_talloc_ctx(tall_pcu_ctx);
osmo_init_logging(&gprs_log_info);
osmo_stats_init(tall_pcu_ctx);
gprs_ns_set_log_ss(DNS);
bssgp_set_log_ss(DBSSGP);
vty_init(&pcu_vty_info);
pcu_vty_init(&gprs_log_info);
@ -179,6 +227,13 @@ int main(int argc, char *argv[])
exit(0);
}
bts->gsmtap = gsmtap_source_init(gsmtap_addr, GSMTAP_UDP_PORT, 1);
if (bts->gsmtap)
gsmtap_source_add_sink(bts->gsmtap);
else
fprintf(stderr, "Failed to initialize GSMTAP for %s\n", gsmtap_addr);
rc = vty_read_config_file(config_file, NULL);
if (rc < 0 && config_given) {
fprintf(stderr, "Failed to parse the config file: '%s'\n",
@ -196,7 +251,7 @@ int main(int argc, char *argv[])
}
if (!bts->alloc_algorithm)
bts->alloc_algorithm = alloc_algorithm_b;
bts->alloc_algorithm = alloc_algorithm_dynamic;
rc = pcu_l1if_open();
@ -211,23 +266,30 @@ int main(int argc, char *argv[])
signal(SIGUSR1, sighandler);
signal(SIGUSR2, sighandler);
/* enable realtime priority for us */
if (rt_prio != -1) {
memset(&param, 0, sizeof(param));
param.sched_priority = rt_prio;
rc = sched_setscheduler(getpid(), SCHED_RR, &param);
if (rc != 0) {
fprintf(stderr, "Setting SCHED_RR priority(%d) failed: %s\n",
param.sched_priority, strerror(errno));
exit(1);
}
}
while (!quit) {
osmo_gsm_timers_check();
osmo_gsm_timers_prepare();
osmo_gsm_timers_update();
osmo_select_main(0);
#ifdef DEBUG_DIAGRAM
gettimeofday(&diagram_time, NULL);
#endif
}
telnet_exit();
pcu_l1if_close();
talloc_free(gprs_rlcmac_bts);
talloc_report_full(tall_pcu_ctx, stderr);
talloc_free(tall_pcu_ctx);

42
src/pcu_utils.h Normal file
View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
inline int msecs_to_frames(int msecs) {
return (msecs * (1024 * 1000 / 4615)) / 1024;
}
inline void csecs_to_timeval(unsigned csecs, struct timeval *tv) {
tv->tv_sec = csecs / 100;
tv->tv_usec = (csecs % 100) * 10000;
}
template <typename T>
inline unsigned int pcu_bitcount(T x)
{
unsigned int count = 0;
for (count = 0; x; count += 1)
x &= x - 1;
return count;
}
template <typename T>
inline T pcu_lsb(T x)
{
return x & -x;
}

View File

@ -2,12 +2,20 @@
#include <stdint.h>
#include <stdlib.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/misc.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include "pcu_vty.h"
#include "gprs_rlcmac.h"
#include "bts.h"
#include "tbf.h"
enum node_type pcu_vty_go_parent(struct vty *vty)
#include "pcu_vty_functions.h"
int pcu_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
#if 0
@ -37,71 +45,124 @@ int pcu_vty_is_config_node(struct vty *vty, int node)
static struct cmd_node pcu_node = {
(enum node_type) PCU_NODE,
"%s(pcu)#",
"%s(config-pcu)# ",
1,
};
gDEFUN(ournode_exit, ournode_exit_cmd, "exit",
"Exit current node, go down to provious node")
{
switch (vty->node) {
#if 0
case TRXV_NODE:
vty->node = PCU_NODE;
{
struct gsm_bts_trx *trx = vty->index;
vty->index = trx->bts;
}
break;
#endif
default:
break;
}
return CMD_SUCCESS;
}
gDEFUN(ournode_end, ournode_end_cmd, "end",
"End current mode and change to enable mode")
{
switch (vty->node) {
default:
vty_config_unlock(vty);
vty->node = ENABLE_NODE;
vty->index = NULL;
vty->index_sub = NULL;
break;
}
return CMD_SUCCESS;
}
static int config_write_pcu(struct vty *vty)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
vty_out(vty, "pcu%s", VTY_NEWLINE);
if (bts->egprs_enabled)
vty_out(vty, " egprs only%s", VTY_NEWLINE);
vty_out(vty, " flow-control-interval %d%s", bts->fc_interval,
VTY_NEWLINE);
if (bts->force_cs)
if (bts->fc_bvc_bucket_size)
vty_out(vty, " flow-control force-bvc-bucket-size %d%s",
bts->fc_bvc_bucket_size, VTY_NEWLINE);
if (bts->fc_bvc_leak_rate)
vty_out(vty, " flow-control force-bvc-leak-rate %d%s",
bts->fc_bvc_leak_rate, VTY_NEWLINE);
if (bts->fc_ms_bucket_size)
vty_out(vty, " flow-control force-ms-bucket-size %d%s",
bts->fc_ms_bucket_size, VTY_NEWLINE);
if (bts->fc_ms_leak_rate)
vty_out(vty, " flow-control force-ms-leak-rate %d%s",
bts->fc_ms_leak_rate, VTY_NEWLINE);
if (bts->force_cs) {
if (bts->initial_cs_ul == bts->initial_cs_dl)
vty_out(vty, " cs %d%s", bts->initial_cs_dl,
VTY_NEWLINE);
else
vty_out(vty, " cs %d %d%s", bts->initial_cs_dl,
bts->initial_cs_ul, VTY_NEWLINE);
}
if (bts->max_cs_dl && bts->max_cs_ul) {
if (bts->max_cs_ul == bts->max_cs_dl)
vty_out(vty, " cs max %d%s", bts->max_cs_dl,
VTY_NEWLINE);
else
vty_out(vty, " cs max %d %d%s", bts->max_cs_dl,
bts->max_cs_ul, VTY_NEWLINE);
}
if (bts->cs_adj_enabled)
vty_out(vty, " cs threshold %d %d%s",
bts->cs_adj_lower_limit, bts->cs_adj_upper_limit,
VTY_NEWLINE);
else
vty_out(vty, " no cs threshold%s", VTY_NEWLINE);
if (bts->cs_downgrade_threshold)
vty_out(vty, " cs downgrade-threshold %d%s",
bts->cs_downgrade_threshold, VTY_NEWLINE);
else
vty_out(vty, " no cs downgrade-threshold%s", VTY_NEWLINE);
vty_out(vty, " cs link-quality-ranges cs1 %d cs2 %d %d cs3 %d %d cs4 %d%s",
bts->cs_lqual_ranges[0].high,
bts->cs_lqual_ranges[1].low,
bts->cs_lqual_ranges[1].high,
bts->cs_lqual_ranges[2].low,
bts->cs_lqual_ranges[2].high,
bts->cs_lqual_ranges[3].low,
VTY_NEWLINE);
if (bts->initial_mcs_dl != 1 && bts->initial_mcs_ul != 1) {
if (bts->initial_mcs_ul == bts->initial_mcs_dl)
vty_out(vty, " mcs %d%s", bts->initial_mcs_dl,
VTY_NEWLINE);
else
vty_out(vty, " mcs %d %d%s", bts->initial_mcs_dl,
bts->initial_mcs_ul, VTY_NEWLINE);
}
if (bts->max_mcs_dl && bts->max_mcs_ul) {
if (bts->max_mcs_ul == bts->max_mcs_dl)
vty_out(vty, " mcs max %d%s", bts->max_mcs_dl,
VTY_NEWLINE);
else
vty_out(vty, " mcs max %d %d%s", bts->max_mcs_dl,
bts->max_mcs_ul, VTY_NEWLINE);
}
vty_out(vty, " window-size %d %d%s", bts->ws_base, bts->ws_pdch,
VTY_NEWLINE);
if (bts->force_llc_lifetime == 0xffff)
vty_out(vty, " queue lifetime infinite%s", VTY_NEWLINE);
else if (bts->force_llc_lifetime)
vty_out(vty, " queue lifetime %d%s", bts->force_llc_lifetime,
VTY_NEWLINE);
if (bts->llc_discard_csec)
vty_out(vty, " queue hysteresis %d%s", bts->llc_discard_csec,
VTY_NEWLINE);
if (bts->llc_idle_ack_csec)
vty_out(vty, " queue idle-ack-delay %d%s", bts->llc_idle_ack_csec,
VTY_NEWLINE);
if (bts->llc_codel_interval_msec == LLC_CODEL_USE_DEFAULT)
vty_out(vty, " queue codel%s", VTY_NEWLINE);
else if (bts->llc_codel_interval_msec == LLC_CODEL_DISABLE)
vty_out(vty, " no queue codel%s", VTY_NEWLINE);
else
vty_out(vty, " queue codel interval %d%s",
bts->llc_codel_interval_msec/10, VTY_NEWLINE);
if (bts->alloc_algorithm == alloc_algorithm_a)
vty_out(vty, " alloc-algorithm a%s", VTY_NEWLINE);
if (bts->alloc_algorithm == alloc_algorithm_b)
vty_out(vty, " alloc-algorithm b%s", VTY_NEWLINE);
if (bts->alloc_algorithm == alloc_algorithm_dynamic)
vty_out(vty, " alloc-algorithm dynamic%s", VTY_NEWLINE);
if (bts->force_two_phase)
vty_out(vty, " two-phase-access%s", VTY_NEWLINE);
vty_out(vty, " alpha %d%s", bts->alpha, VTY_NEWLINE);
vty_out(vty, " gamma %d%s", bts->gamma * 2, VTY_NEWLINE);
if (bts->dl_tbf_idle_msec)
vty_out(vty, " dl-tbf-idle-time %d%s", bts->dl_tbf_idle_msec,
VTY_NEWLINE);
return pcu_vty_config_write_pcu_ext(vty);
}
/* per-BTS configuration */
@ -115,26 +176,185 @@ DEFUN(cfg_pcu,
return CMD_SUCCESS;
}
#define EGPRS_STR "EGPRS configuration\n"
DEFUN(cfg_pcu_egprs,
cfg_pcu_egprs_cmd,
"egprs only",
EGPRS_STR "Use EGPRS and disable plain GPRS\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->egprs_enabled = 1;
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;
}
DEFUN(cfg_pcu_no_egprs,
cfg_pcu_no_egprs_cmd,
"no egprs",
NO_STR EGPRS_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->egprs_enabled = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_fc_interval,
cfg_pcu_fc_interval_cmd,
"flow-control-interval <1-10>",
"Interval between sending subsequent Flow Control PDUs\n"
"Interval time in seconds\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_interval = atoi(argv[0]);
return CMD_SUCCESS;
}
#define FC_STR "BSSGP Flow Control configuration\n"
#define FC_BMAX_STR(who) "Force a fixed value for the " who " bucket size\n"
#define FC_LR_STR(who) "Force a fixed value for the " who " leak rate\n"
DEFUN(cfg_pcu_fc_bvc_bucket_size,
cfg_pcu_fc_bvc_bucket_size_cmd,
"flow-control force-bvc-bucket-size <1-6553500>",
FC_STR FC_BMAX_STR("BVC") "Bucket size in octets\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_bvc_bucket_size = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_fc_bvc_bucket_size,
cfg_pcu_no_fc_bvc_bucket_size_cmd,
"no flow-control force-bvc-bucket-size",
NO_STR FC_STR FC_BMAX_STR("BVC"))
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_bvc_bucket_size = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_fc_bvc_leak_rate,
cfg_pcu_fc_bvc_leak_rate_cmd,
"flow-control force-bvc-leak-rate <1-6553500>",
FC_STR FC_LR_STR("BVC") "Leak rate in bit/s\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_bvc_leak_rate = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_fc_bvc_leak_rate,
cfg_pcu_no_fc_bvc_leak_rate_cmd,
"no flow-control force-bvc-leak-rate",
NO_STR FC_STR FC_LR_STR("BVC"))
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_bvc_leak_rate = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_fc_ms_bucket_size,
cfg_pcu_fc_ms_bucket_size_cmd,
"flow-control force-ms-bucket-size <1-6553500>",
FC_STR FC_BMAX_STR("default MS") "Bucket size in octets\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_ms_bucket_size = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_fc_ms_bucket_size,
cfg_pcu_no_fc_ms_bucket_size_cmd,
"no flow-control force-ms-bucket-size",
NO_STR FC_STR FC_BMAX_STR("default MS"))
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_ms_bucket_size = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_fc_ms_leak_rate,
cfg_pcu_fc_ms_leak_rate_cmd,
"flow-control force-ms-leak-rate <1-6553500>",
FC_STR FC_LR_STR("default MS") "Leak rate in bit/s\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_ms_leak_rate = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_fc_ms_leak_rate,
cfg_pcu_no_fc_ms_leak_rate_cmd,
"no flow-control force-ms-leak-rate",
NO_STR FC_STR FC_LR_STR("default MS"))
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_ms_leak_rate = 0;
return CMD_SUCCESS;
}
#define FC_BTIME_STR "Set target downlink maximum queueing time (only affects the advertised bucket size)\n"
DEFUN(cfg_pcu_fc_bucket_time,
cfg_pcu_fc_bucket_time_cmd,
"flow-control bucket-time <1-65534>",
FC_STR FC_BTIME_STR "Time in centi-seconds\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_bucket_time = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_fc_bucket_time,
cfg_pcu_no_fc_bucket_time_cmd,
"no flow-control bucket-time",
NO_STR FC_STR FC_BTIME_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_bucket_time = 0;
return CMD_SUCCESS;
}
#define CS_STR "Coding Scheme configuration\n"
DEFUN(cfg_pcu_cs,
cfg_pcu_cs_cmd,
"cs <1-4> [<1-4>]",
"Set the Coding Scheme to be used, (overrides BTS config)\n"
"Initial CS used\nAlternative uplink CS")
CS_STR
"Initial CS value to be used (overrides BTS config)\n"
"Use a different initial CS value for the uplink")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t cs = atoi(argv[0]);
bts->force_cs = 1;
@ -150,15 +370,136 @@ DEFUN(cfg_pcu_cs,
DEFUN(cfg_pcu_no_cs,
cfg_pcu_no_cs_cmd,
"no cs",
NO_STR "Don't force given Coding Scheme, (use BTS config)\n")
NO_STR CS_STR)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_cs = 0;
return CMD_SUCCESS;
}
#define CS_MAX_STR "Set maximum values for adaptive CS selection (overrides BTS config)\n"
DEFUN(cfg_pcu_cs_max,
cfg_pcu_cs_max_cmd,
"cs max <1-4> [<1-4>]",
CS_STR
CS_MAX_STR
"Maximum CS value to be used\n"
"Use a different maximum CS value for the uplink")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t cs = atoi(argv[0]);
bts->max_cs_dl = cs;
if (argc > 1)
bts->max_cs_ul = atoi(argv[1]);
else
bts->max_cs_ul = cs;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_cs_max,
cfg_pcu_no_cs_max_cmd,
"no cs max",
NO_STR CS_STR CS_MAX_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->max_cs_dl = 0;
bts->max_cs_ul = 0;
return CMD_SUCCESS;
}
#define MCS_STR "Modulation and Coding Scheme configuration (EGPRS)\n"
DEFUN(cfg_pcu_mcs,
cfg_pcu_mcs_cmd,
"mcs <1-9> [<1-9>]",
MCS_STR
"Initial MCS value to be used (default 1)\n"
"Use a different initial MCS value for the uplink")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t cs = atoi(argv[0]);
bts->initial_mcs_dl = cs;
if (argc > 1)
bts->initial_mcs_ul = atoi(argv[1]);
else
bts->initial_mcs_ul = cs;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_mcs,
cfg_pcu_no_mcs_cmd,
"no mcs",
NO_STR MCS_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->initial_mcs_dl = 1;
bts->initial_mcs_ul = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_mcs_max,
cfg_pcu_mcs_max_cmd,
"mcs max <1-9> [<1-9>]",
MCS_STR
CS_MAX_STR
"Maximum MCS value to be used\n"
"Use a different maximum MCS value for the uplink")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t mcs = atoi(argv[0]);
bts->max_mcs_dl = mcs;
if (argc > 1)
bts->max_mcs_ul = atoi(argv[1]);
else
bts->max_mcs_ul = mcs;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_mcs_max,
cfg_pcu_no_mcs_max_cmd,
"no mcs max",
NO_STR MCS_STR CS_MAX_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->max_mcs_dl = 0;
bts->max_mcs_ul = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_window_size,
cfg_pcu_window_size_cmd,
"window-size <0-1024> [<0-256>]",
"Window size configuration (b + N_PDCH * f)\n"
"Base value (b)\n"
"Factor for number of PDCH (f)")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint16_t b = atoi(argv[0]);
bts->ws_base = b;
if (argc > 1) {
uint16_t f = atoi(argv[1]);
bts->ws_pdch = f;
}
return CMD_SUCCESS;
}
#define QUEUE_STR "Packet queue options\n"
#define LIFETIME_STR "Set lifetime limit of LLC frame in centi-seconds " \
"(overrides the value given by SGSN)\n"
@ -168,8 +509,8 @@ DEFUN(cfg_pcu_queue_lifetime,
"queue lifetime <1-65534>",
QUEUE_STR LIFETIME_STR "Lifetime in centi-seconds")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
uint8_t csec = atoi(argv[0]);
struct gprs_rlcmac_bts *bts = bts_main_data();
uint16_t csec = atoi(argv[0]);
bts->force_llc_lifetime = csec;
@ -181,7 +522,7 @@ DEFUN(cfg_pcu_queue_lifetime_inf,
"queue lifetime infinite",
QUEUE_STR LIFETIME_STR "Infinite lifetime")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_llc_lifetime = 0xffff;
@ -194,21 +535,119 @@ DEFUN(cfg_pcu_no_queue_lifetime,
NO_STR QUEUE_STR "Disable lifetime limit of LLC frame (use value given "
"by SGSN)\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_llc_lifetime = 0;
return CMD_SUCCESS;
}
#define QUEUE_HYSTERESIS_STR "Set lifetime hysteresis of LLC frame in centi-seconds " \
"(continue discarding until lifetime-hysteresis is reached)\n"
DEFUN(cfg_pcu_queue_hysteresis,
cfg_pcu_queue_hysteresis_cmd,
"queue hysteresis <1-65535>",
QUEUE_STR QUEUE_HYSTERESIS_STR "Hysteresis in centi-seconds")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint16_t csec = atoi(argv[0]);
bts->llc_discard_csec = csec;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_queue_hysteresis,
cfg_pcu_no_queue_hysteresis_cmd,
"no queue hysteresis",
NO_STR QUEUE_STR QUEUE_HYSTERESIS_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->llc_discard_csec = 0;
return CMD_SUCCESS;
}
#define QUEUE_CODEL_STR "Set CoDel queue management\n"
DEFUN(cfg_pcu_queue_codel,
cfg_pcu_queue_codel_cmd,
"queue codel",
QUEUE_STR QUEUE_CODEL_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->llc_codel_interval_msec = LLC_CODEL_USE_DEFAULT;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_queue_codel_interval,
cfg_pcu_queue_codel_interval_cmd,
"queue codel interval <1-1000>",
QUEUE_STR QUEUE_CODEL_STR "Specify interval\n" "Interval in centi-seconds")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint16_t csec = atoi(argv[0]);
bts->llc_codel_interval_msec = 10*csec;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_queue_codel,
cfg_pcu_no_queue_codel_cmd,
"no queue codel",
NO_STR QUEUE_STR QUEUE_CODEL_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->llc_codel_interval_msec = LLC_CODEL_DISABLE;
return CMD_SUCCESS;
}
#define QUEUE_IDLE_ACK_STR "Request an ACK after the last DL LLC frame in centi-seconds\n"
DEFUN(cfg_pcu_queue_idle_ack_delay,
cfg_pcu_queue_idle_ack_delay_cmd,
"queue idle-ack-delay <1-65535>",
QUEUE_STR QUEUE_IDLE_ACK_STR "Idle ACK delay in centi-seconds")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint16_t csec = atoi(argv[0]);
bts->llc_idle_ack_csec = csec;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_queue_idle_ack_delay,
cfg_pcu_no_queue_idle_ack_delay_cmd,
"no queue idle-ack-delay",
NO_STR QUEUE_STR QUEUE_IDLE_ACK_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->llc_idle_ack_csec = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_alloc,
cfg_pcu_alloc_cmd,
"alloc-algorithm (a|b)",
"alloc-algorithm (a|b|dynamic)",
"Select slot allocation algorithm to use when assigning timeslots on "
"PACCH\nSingle slot is assigned only\nMultiple slots are assigned for "
"semi-duplex operation")
"PACCH\n"
"Single slot is assigned only\n"
"Multiple slots are assigned for semi-duplex operation\n"
"Dynamically select the algorithm based on the system state\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
switch (argv[0][0]) {
case 'a':
@ -217,6 +656,9 @@ DEFUN(cfg_pcu_alloc,
case 'b':
bts->alloc_algorithm = alloc_algorithm_b;
break;
default:
bts->alloc_algorithm = alloc_algorithm_dynamic;
break;
}
return CMD_SUCCESS;
@ -227,7 +669,7 @@ DEFUN(cfg_pcu_two_phase,
"two-phase-access",
"Force two phase access when MS requests single phase access\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_two_phase = 1;
@ -239,7 +681,7 @@ DEFUN(cfg_pcu_no_two_phase,
"no two-phase-access",
NO_STR "Only use two phase access when requested my MS\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_two_phase = 0;
@ -253,7 +695,7 @@ DEFUN(cfg_pcu_alpha,
"NOTE: Be sure to set Alpha value at System information 13 too.\n"
"Alpha in units of 0.1\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->alpha = atoi(argv[0]);
@ -266,13 +708,214 @@ DEFUN(cfg_pcu_gamma,
"Gamma parameter for MS power control in units of dB (see TS 05.08)\n"
"Gamma in even unit of dBs\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->gamma = atoi(argv[0]) / 2;
return CMD_SUCCESS;
}
DEFUN(show_bts_stats,
show_bts_stats_cmd,
"show bts statistics",
SHOW_STR "BTS related functionality\nStatistics\n")
{
vty_out_rate_ctr_group(vty, "", bts_main_data_stats());
return CMD_SUCCESS;
}
#define IDLE_TIME_STR "keep an idle DL TBF alive for the time given\n"
DEFUN(cfg_pcu_dl_tbf_idle_time,
cfg_pcu_dl_tbf_idle_time_cmd,
"dl-tbf-idle-time <1-5000>",
IDLE_TIME_STR "idle time in msec")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->dl_tbf_idle_msec = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_dl_tbf_idle_time,
cfg_pcu_no_dl_tbf_idle_time_cmd,
"no dl-tbf-idle-time",
NO_STR IDLE_TIME_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->dl_tbf_idle_msec = 0;
return CMD_SUCCESS;
}
#define MS_IDLE_TIME_STR "keep an idle MS object alive for the time given\n"
DEFUN(cfg_pcu_ms_idle_time,
cfg_pcu_ms_idle_time_cmd,
"ms-idle-time <1-7200>",
MS_IDLE_TIME_STR "idle time in sec")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->ms_idle_sec = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_ms_idle_time,
cfg_pcu_no_ms_idle_time_cmd,
"no ms-idle-time",
NO_STR MS_IDLE_TIME_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->ms_idle_sec = 0;
return CMD_SUCCESS;
}
#define CS_ERR_LIMITS_STR "set thresholds for error rate based CS adjustment\n"
DEFUN(cfg_pcu_cs_err_limits,
cfg_pcu_cs_err_limits_cmd,
"cs threshold <0-100> <0-100>",
CS_STR CS_ERR_LIMITS_STR "lower limit in %\n" "upper limit in %\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t lower_limit = atoi(argv[0]);
uint8_t upper_limit = atoi(argv[1]);
if (lower_limit > upper_limit) {
vty_out(vty,
"The lower limit must be less than or equal to the "
"upper limit.%s", VTY_NEWLINE);
return CMD_WARNING;
}
bts->cs_adj_enabled = 1;
bts->cs_adj_upper_limit = upper_limit;
bts->cs_adj_lower_limit = lower_limit;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_cs_err_limits,
cfg_pcu_no_cs_err_limits_cmd,
"no cs threshold",
NO_STR CS_STR CS_ERR_LIMITS_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->cs_adj_enabled = 0;
bts->cs_adj_upper_limit = 100;
bts->cs_adj_lower_limit = 0;
return CMD_SUCCESS;
}
#define CS_DOWNGRADE_STR "set threshold for data size based CS downgrade\n"
DEFUN(cfg_pcu_cs_downgrade_thrsh,
cfg_pcu_cs_downgrade_thrsh_cmd,
"cs downgrade-threshold <1-10000>",
CS_STR CS_DOWNGRADE_STR "downgrade if less octets left\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->cs_downgrade_threshold = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_cs_downgrade_thrsh,
cfg_pcu_no_cs_downgrade_thrsh_cmd,
"no cs downgrade-threshold",
NO_STR CS_STR CS_DOWNGRADE_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->cs_downgrade_threshold = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_cs_lqual_ranges,
cfg_pcu_cs_lqual_ranges_cmd,
"cs link-quality-ranges cs1 <0-35> cs2 <0-35> <0-35> cs3 <0-35> <0-35> cs4 <0-35>",
CS_STR "Set link quality ranges\n"
"Set quality range for CS-1 (high value only)\n"
"CS-1 high (dB)\n"
"Set quality range for CS-2\n"
"CS-2 low (dB)\n"
"CS-2 high (dB)\n"
"Set quality range for CS-3\n"
"CS-3 low (dB)\n"
"CS-3 high (dB)\n"
"Set quality range for CS-4 (low value only)\n"
"CS-4 low (dB)\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t cs1_high = atoi(argv[0]);
uint8_t cs2_low = atoi(argv[1]);
uint8_t cs2_high = atoi(argv[2]);
uint8_t cs3_low = atoi(argv[3]);
uint8_t cs3_high = atoi(argv[4]);
uint8_t cs4_low = atoi(argv[5]);
bts->cs_lqual_ranges[0].high = cs1_high;
bts->cs_lqual_ranges[1].low = cs2_low;
bts->cs_lqual_ranges[1].high = cs2_high;
bts->cs_lqual_ranges[2].low = cs3_low;
bts->cs_lqual_ranges[2].high = cs3_high;
bts->cs_lqual_ranges[3].low = cs4_low;
return CMD_SUCCESS;
}
DEFUN(show_tbf,
show_tbf_cmd,
"show tbf all",
SHOW_STR "information about TBFs\n" "All TBFs\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
return pcu_vty_show_tbf_all(vty, bts);
}
DEFUN(show_ms_all,
show_ms_all_cmd,
"show ms all",
SHOW_STR "information about MSs\n" "All TBFs\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
return pcu_vty_show_ms_all(vty, bts);
}
DEFUN(show_ms_tlli,
show_ms_tlli_cmd,
"show ms tlli TLLI",
SHOW_STR "information about MSs\n" "Select MS by TLLI\n" "TLLI as hex\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
char *endp = NULL;
unsigned long long tlli = strtoll(argv[0], &endp, 16);
if ((endp != NULL && *endp != 0) || tlli > 0xffffffffULL) {
vty_out(vty, "Invalid TLLI.%s", VTY_NEWLINE);
return CMD_WARNING;
}
return pcu_vty_show_ms_by_tlli(vty, bts, (uint32_t)tlli);
}
DEFUN(show_ms_imsi,
show_ms_imsi_cmd,
"show ms imsi IMSI",
SHOW_STR "information about MSs\n" "Select MS by IMSI\n" "IMSI\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
return pcu_vty_show_ms_by_imsi(vty, bts, argv[0]);
}
static const char pcu_copyright[] =
"Copyright (C) 2012 by Ivan Kluchnikov <kluchnikovi@gmail.com> and \r\n"
" Andreas Eversberg <jolly@eversberg.eu>\r\n"
@ -293,22 +936,63 @@ int pcu_vty_init(const struct log_info *cat)
// install_element_ve(&show_pcu_cmd);
logging_vty_add_cmds(cat);
osmo_stats_vty_add_cmds(cat);
install_node(&pcu_node, config_write_pcu);
install_element(CONFIG_NODE, &cfg_pcu_cmd);
install_default(PCU_NODE);
vty_install_default(PCU_NODE);
install_element(PCU_NODE, &cfg_pcu_egprs_cmd);
install_element(PCU_NODE, &cfg_pcu_no_egprs_cmd);
install_element(PCU_NODE, &cfg_pcu_no_two_phase_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_cmd);
install_element(PCU_NODE, &cfg_pcu_no_cs_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_max_cmd);
install_element(PCU_NODE, &cfg_pcu_no_cs_max_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_err_limits_cmd);
install_element(PCU_NODE, &cfg_pcu_no_cs_err_limits_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_downgrade_thrsh_cmd);
install_element(PCU_NODE, &cfg_pcu_no_cs_downgrade_thrsh_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_lqual_ranges_cmd);
install_element(PCU_NODE, &cfg_pcu_mcs_cmd);
install_element(PCU_NODE, &cfg_pcu_no_mcs_cmd);
install_element(PCU_NODE, &cfg_pcu_mcs_max_cmd);
install_element(PCU_NODE, &cfg_pcu_no_mcs_max_cmd);
install_element(PCU_NODE, &cfg_pcu_window_size_cmd);
install_element(PCU_NODE, &cfg_pcu_queue_lifetime_cmd);
install_element(PCU_NODE, &cfg_pcu_queue_lifetime_inf_cmd);
install_element(PCU_NODE, &cfg_pcu_no_queue_lifetime_cmd);
install_element(PCU_NODE, &cfg_pcu_queue_hysteresis_cmd);
install_element(PCU_NODE, &cfg_pcu_no_queue_hysteresis_cmd);
install_element(PCU_NODE, &cfg_pcu_queue_codel_cmd);
install_element(PCU_NODE, &cfg_pcu_queue_codel_interval_cmd);
install_element(PCU_NODE, &cfg_pcu_no_queue_codel_cmd);
install_element(PCU_NODE, &cfg_pcu_queue_idle_ack_delay_cmd);
install_element(PCU_NODE, &cfg_pcu_no_queue_idle_ack_delay_cmd);
install_element(PCU_NODE, &cfg_pcu_alloc_cmd);
install_element(PCU_NODE, &cfg_pcu_two_phase_cmd);
install_element(PCU_NODE, &cfg_pcu_fc_interval_cmd);
install_element(PCU_NODE, &cfg_pcu_fc_bucket_time_cmd);
install_element(PCU_NODE, &cfg_pcu_no_fc_bucket_time_cmd);
install_element(PCU_NODE, &cfg_pcu_fc_bvc_bucket_size_cmd);
install_element(PCU_NODE, &cfg_pcu_no_fc_bvc_bucket_size_cmd);
install_element(PCU_NODE, &cfg_pcu_fc_bvc_leak_rate_cmd);
install_element(PCU_NODE, &cfg_pcu_no_fc_bvc_leak_rate_cmd);
install_element(PCU_NODE, &cfg_pcu_fc_ms_bucket_size_cmd);
install_element(PCU_NODE, &cfg_pcu_no_fc_ms_bucket_size_cmd);
install_element(PCU_NODE, &cfg_pcu_fc_ms_leak_rate_cmd);
install_element(PCU_NODE, &cfg_pcu_no_fc_ms_leak_rate_cmd);
install_element(PCU_NODE, &cfg_pcu_alpha_cmd);
install_element(PCU_NODE, &cfg_pcu_gamma_cmd);
install_element(PCU_NODE, &ournode_end_cmd);
install_element(PCU_NODE, &cfg_pcu_dl_tbf_idle_time_cmd);
install_element(PCU_NODE, &cfg_pcu_no_dl_tbf_idle_time_cmd);
install_element(PCU_NODE, &cfg_pcu_ms_idle_time_cmd);
install_element(PCU_NODE, &cfg_pcu_no_ms_idle_time_cmd);
install_element_ve(&show_bts_stats_cmd);
install_element_ve(&show_tbf_cmd);
install_element_ve(&show_ms_all_cmd);
install_element_ve(&show_ms_tlli_cmd);
install_element_ve(&show_ms_imsi_cmd);
return 0;
}

View File

@ -8,10 +8,7 @@ enum pcu_vty_node {
PCU_NODE = _LAST_OSMOVTY_NODE + 1,
};
extern struct cmd_element ournode_exit_cmd;
extern struct cmd_element ournode_end_cmd;
enum node_type pcu_vty_go_parent(struct vty *vty);
int pcu_vty_go_parent(struct vty *vty);
int pcu_vty_is_config_node(struct vty *vty, int node);
int pcu_vty_init(const struct log_info *cat);

218
src/pcu_vty_functions.cpp Normal file
View File

@ -0,0 +1,218 @@
/* pcu_vty_functions.cpp
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* OsmoBTS VTY interface */
#include <stdint.h>
#include <stdlib.h>
#include "pcu_vty_functions.h"
#include "bts.h"
#include "gprs_ms_storage.h"
#include "gprs_ms.h"
#include "cxx_linuxlist.h"
extern "C" {
# include <osmocom/vty/command.h>
# include <osmocom/vty/logging.h>
# include <osmocom/vty/misc.h>
}
int pcu_vty_config_write_pcu_ext(struct vty *vty)
{
return CMD_SUCCESS;
}
static void tbf_print_vty_info(struct vty *vty, gprs_rlcmac_tbf *tbf)
{
gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf);
gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf);
vty_out(vty, "TBF: TFI=%d TLLI=0x%08x (%s) DIR=%s IMSI=%s%s", tbf->tfi(),
tbf->tlli(), tbf->is_tlli_valid() ? "valid" : "invalid",
tbf->direction == GPRS_RLCMAC_UL_TBF ? "UL" : "DL",
tbf->imsi(), VTY_NEWLINE);
vty_out(vty, " created=%lu state=%08x 1st_TS=%d 1st_cTS=%d ctrl_TS=%d "
"MS_CLASS=%d/%d%s",
tbf->created_ts(), tbf->state_flags, tbf->first_ts,
tbf->first_common_ts, tbf->control_ts,
tbf->ms_class(),
tbf->ms() ? tbf->ms()->egprs_ms_class() : -1,
VTY_NEWLINE);
vty_out(vty, " TS_alloc=");
for (int i = 0; i < 8; i++) {
bool is_ctrl = tbf->is_control_ts(i);
if (tbf->pdch[i])
vty_out(vty, "%d%s ", i, is_ctrl ? "!" : "");
}
vty_out(vty, " CS=%s WS=%d",
tbf->current_cs().name(), tbf->window()->ws());
if (ul_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());
}
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%s", VTY_NEWLINE, VTY_NEWLINE);
}
int pcu_vty_show_tbf_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
{
BTS *bts = bts_data->bts;
LListHead<gprs_rlcmac_tbf> *ms_iter;
vty_out(vty, "UL TBFs%s", VTY_NEWLINE);
llist_for_each(ms_iter, &bts->ul_tbfs())
tbf_print_vty_info(vty, ms_iter->entry());
vty_out(vty, "%sDL TBFs%s", VTY_NEWLINE, VTY_NEWLINE);
llist_for_each(ms_iter, &bts->dl_tbfs())
tbf_print_vty_info(vty, ms_iter->entry());
return CMD_SUCCESS;
}
int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
{
BTS *bts = bts_data->bts;
LListHead<GprsMs> *ms_iter;
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, "
"IMSI=%s%s",
ms->tlli(),
ms->ta(), ms->current_cs_ul().name(),
ms->current_cs_dl().name(),
ms->llc_queue()->size(),
ms->imsi(),
VTY_NEWLINE);
}
return CMD_SUCCESS;
}
static int show_ms(struct vty *vty, GprsMs *ms)
{
unsigned i;
LListHead<gprs_rlcmac_tbf> *i_tbf;
uint8_t slots;
vty_out(vty, "MS TLLI=%08x, IMSI=%s%s", ms->tlli(), ms->imsi(), VTY_NEWLINE);
vty_out(vty, " Timing advance (TA): %d%s", ms->ta(), VTY_NEWLINE);
vty_out(vty, " Coding scheme uplink: %s%s", ms->current_cs_ul().name(),
VTY_NEWLINE);
vty_out(vty, " Coding scheme downlink: %s%s", ms->current_cs_dl().name(),
VTY_NEWLINE);
vty_out(vty, " Mode: %s%s",
GprsCodingScheme::modeName(ms->mode()), VTY_NEWLINE);
vty_out(vty, " MS class: %d%s", ms->ms_class(), VTY_NEWLINE);
vty_out(vty, " EGPRS MS class: %d%s", ms->egprs_ms_class(), VTY_NEWLINE);
vty_out(vty, " PACCH: ");
slots = ms->current_pacch_slots();
for (int i = 0; i < 8; i++)
if (slots & (1 << i))
vty_out(vty, "%d ", i);
vty_out(vty, "%s", VTY_NEWLINE);
vty_out(vty, " LLC queue length: %d%s", ms->llc_queue()->size(),
VTY_NEWLINE);
vty_out(vty, " LLC queue octets: %d%s", ms->llc_queue()->octets(),
VTY_NEWLINE);
if (ms->l1_meas()->have_rssi)
vty_out(vty, " RSSI: %d dBm%s",
ms->l1_meas()->rssi, VTY_NEWLINE);
if (ms->l1_meas()->have_ber)
vty_out(vty, " Bit error rate: %d %%%s",
ms->l1_meas()->ber, VTY_NEWLINE);
if (ms->l1_meas()->have_link_qual)
vty_out(vty, " Link quality: %d dB%s",
ms->l1_meas()->link_qual, VTY_NEWLINE);
if (ms->l1_meas()->have_bto)
vty_out(vty, " Burst timing offset: %d/4 bit%s",
ms->l1_meas()->bto, VTY_NEWLINE);
if (ms->l1_meas()->have_ms_rx_qual)
vty_out(vty, " Downlink NACK rate: %d %%%s",
ms->nack_rate_dl(), VTY_NEWLINE);
if (ms->l1_meas()->have_ms_rx_qual)
vty_out(vty, " MS RX quality: %d %%%s",
ms->l1_meas()->ms_rx_qual, VTY_NEWLINE);
if (ms->l1_meas()->have_ms_c_value)
vty_out(vty, " MS C value: %d dB%s",
ms->l1_meas()->ms_c_value, VTY_NEWLINE);
if (ms->l1_meas()->have_ms_sign_var)
vty_out(vty, " MS SIGN variance: %d dB%s",
ms->l1_meas()->ms_sign_var, VTY_NEWLINE);
for (i = 0; i < ARRAY_SIZE(ms->l1_meas()->ts); ++i) {
if (ms->l1_meas()->ts[i].have_ms_i_level)
vty_out(vty, " MS I level (slot %d): %d dB%s",
i, ms->l1_meas()->ts[i].ms_i_level, 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())
vty_out(vty, " Downlink TBF: TFI=%d, state=%s%s",
ms->dl_tbf()->tfi(),
ms->dl_tbf()->state_name(),
VTY_NEWLINE);
llist_for_each(i_tbf, &ms->old_tbfs())
vty_out(vty, " Old %-19s TFI=%d, state=%s%s",
i_tbf->entry()->direction == GPRS_RLCMAC_UL_TBF ?
"Uplink TBF:" : "Downlink TBF:",
i_tbf->entry()->tfi(),
i_tbf->entry()->state_name(),
VTY_NEWLINE);
return CMD_SUCCESS;
}
int pcu_vty_show_ms_by_tlli(struct vty *vty, struct gprs_rlcmac_bts *bts_data,
uint32_t tlli)
{
BTS *bts = bts_data->bts;
GprsMs *ms = bts->ms_store().get_ms(tlli);
if (!ms) {
vty_out(vty, "Unknown TLLI %08x.%s", tlli, VTY_NEWLINE);
return CMD_WARNING;
}
return show_ms(vty, ms);
}
int pcu_vty_show_ms_by_imsi(struct vty *vty, struct gprs_rlcmac_bts *bts_data,
const char *imsi)
{
BTS *bts = bts_data->bts;
GprsMs *ms = bts->ms_store().get_ms(0, 0, imsi);
if (!ms) {
vty_out(vty, "Unknown IMSI '%s'.%s", imsi, VTY_NEWLINE);
return CMD_WARNING;
}
return show_ms(vty, ms);
}

40
src/pcu_vty_functions.h Normal file
View File

@ -0,0 +1,40 @@
/* pcu_vty_functions.h
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
struct vty;
struct gprs_rlcmac_bts;
int pcu_vty_config_write_pcu_ext(struct vty *vty);
int pcu_vty_show_tbf_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data);
int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data);
int pcu_vty_show_ms_by_tlli(struct vty *vty, struct gprs_rlcmac_bts *bts_data,
uint32_t tlli);
int pcu_vty_show_ms_by_imsi(struct vty *vty, struct gprs_rlcmac_bts *bts_data,
const char *imsi);
#ifdef __cplusplus
}
#endif

View File

@ -1,7 +1,7 @@
#ifndef _PCUIF_PROTO_H
#define _PCUIF_PROTO_H
#define PCU_IF_VERSION 0x04
#define PCU_IF_VERSION 0x05
/* msg_type */
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
@ -49,6 +49,7 @@ struct gsm_pcu_if_data {
uint8_t trx_nr;
uint8_t ts_nr;
uint8_t block_nr;
int8_t rssi;
} __attribute__ ((packed));
struct gsm_pcu_if_rts_req {

65
src/poll_controller.cpp Normal file
View File

@ -0,0 +1,65 @@
/* poll_controller.h
*
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* 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 <poll_controller.h>
#include <bts.h>
#include <tbf.h>
PollController::PollController(BTS& bts)
: m_bts(bts)
{}
void PollController::expireTimedout(int frame_number, unsigned max_delay)
{
struct gprs_rlcmac_dl_tbf *dl_tbf;
struct gprs_rlcmac_ul_tbf *ul_tbf;
struct gprs_rlcmac_sba *sba, *sba2;
LListHead<gprs_rlcmac_tbf> *pos;
uint32_t elapsed;
llist_for_each(pos, &m_bts.ul_tbfs()) {
ul_tbf = as_ul_tbf(pos->entry());
if (ul_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
elapsed = (frame_number + 2715648 - ul_tbf->poll_fn)
% 2715648;
if (elapsed > max_delay && elapsed < 2715400)
ul_tbf->poll_timeout();
}
}
llist_for_each(pos, &m_bts.dl_tbfs()) {
dl_tbf = as_dl_tbf(pos->entry());
if (dl_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
elapsed = (frame_number + 2715648 - dl_tbf->poll_fn)
% 2715648;
if (elapsed > max_delay && elapsed < 2715400)
dl_tbf->poll_timeout();
}
}
llist_for_each_entry_safe(sba, sba2, &m_bts.sba()->m_sbas, list) {
elapsed = (frame_number + 2715648 - sba->fn) % 2715648;
if (elapsed > max_delay && elapsed < 2715400) {
/* sba will be freed here */
m_bts.sba()->timeout(sba);
}
}
}

47
src/poll_controller.h Normal file
View File

@ -0,0 +1,47 @@
/* poll_controller.h
*
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* 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/>.
*
*/
#pragma once
struct gprs_rlcmac_bts;
struct BTS;
/**
* I belong to a BTS and I am responsible for finding TBFs and
* SBAs that should have been polled and execute the timeout
* action on them.
*/
class PollController {
public:
PollController(BTS& bts);
/* check for poll timeout */
void expireTimedout(int frame_number, unsigned max_delay);
private:
BTS& m_bts;
private:
/* disable copying to avoid slicing */
PollController(const PollController&);
PollController& operator=(const PollController&);
};

387
src/rlc.cpp Normal file
View File

@ -0,0 +1,387 @@
/*
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "tbf.h"
#include "bts.h"
#include "gprs_debug.h"
#include <errno.h>
extern "C" {
#include <osmocom/core/utils.h>
}
uint8_t *gprs_rlc_data::prepare(size_t block_data_len)
{
/* todo.. only set it once if it turns out to be a bottleneck */
memset(block, 0x0, sizeof(block));
memset(block, 0x2b, block_data_len);
return block;
}
void gprs_rlc_data::put_data(const uint8_t *data, size_t data_len)
{
memcpy(block, data, data_len);
len = data_len;
}
void gprs_rlc_v_b::reset()
{
for (size_t i = 0; i < ARRAY_SIZE(m_v_b); ++i)
mark_invalid(i);
}
void gprs_rlc_dl_window::reset()
{
m_v_s = 0;
m_v_a = 0;
m_v_b.reset();
}
int gprs_rlc_dl_window::resend_needed()
{
for (uint16_t bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
if (m_v_b.is_nacked(bsn) || m_v_b.is_resend(bsn))
return bsn;
}
return -1;
}
int gprs_rlc_dl_window::mark_for_resend()
{
int resend = 0;
for (uint16_t bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
if (m_v_b.is_unacked(bsn)) {
/* mark to be re-send */
m_v_b.mark_resend(bsn);
resend += 1;
}
}
return resend;
}
int gprs_rlc_dl_window::count_unacked()
{
uint16_t unacked = 0;
uint16_t bsn;
for (bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
if (!m_v_b.is_acked(bsn))
unacked += 1;
}
return unacked;
}
static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn)
{
return (ssn - 1 - bitnum);
}
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 bsn;
/* first_bsn is in range V(A)..V(S) */
for (unsigned int bitpos = 0; bitpos < num_blocks; bitpos++) {
bool is_ack;
bsn = mod_sns(first_bsn + bitpos);
if (bsn == mod_sns(v_a() - 1))
break;
is_ack = bitvec_get_bit_pos(rbb, bitpos) == 1;
if (is_ack) {
LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn);
if (!m_v_b.is_acked(bsn))
*received += 1;
m_v_b.mark_acked(bsn);
} else {
LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
m_v_b.mark_nacked(bsn);
bts->rlc_nacked();
*lost += 1;
}
}
}
void gprs_rlc_dl_window::update(BTS *bts, char *show_rbb, uint16_t ssn,
uint16_t *lost, uint16_t *received)
{
/* SSN - 1 is in range V(A)..V(S)-1 */
for (int bitpos = 0; bitpos < ws(); bitpos++) {
uint16_t bsn = mod_sns(bitnum_to_bsn(bitpos, ssn));
if (bsn == mod_sns(v_a() - 1))
break;
if (show_rbb[ws() - 1 - bitpos] == 'R') {
LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn);
if (!m_v_b.is_acked(bsn))
*received += 1;
m_v_b.mark_acked(bsn);
} else {
LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
m_v_b.mark_nacked(bsn);
bts->rlc_nacked();
*lost += 1;
}
}
}
int gprs_rlc_dl_window::move_window()
{
int i;
uint16_t bsn;
int moved = 0;
for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = mod_sns(bsn + 1)) {
if (m_v_b.is_acked(bsn)) {
m_v_b.mark_invalid(bsn);
moved += 1;
} else
break;
}
return moved;
}
void gprs_rlc_dl_window::show_state(char *show_v_b)
{
int i;
uint16_t bsn;
for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = mod_sns(bsn + 1)) {
uint16_t index = bsn & mod_sns_half();
switch(m_v_b.get_state(index)) {
case GPRS_RLC_DL_BSN_INVALID:
show_v_b[i] = 'I';
break;
case GPRS_RLC_DL_BSN_ACKED:
show_v_b[i] = 'A';
break;
case GPRS_RLC_DL_BSN_RESEND:
show_v_b[i] = 'X';
break;
case GPRS_RLC_DL_BSN_NACKED:
show_v_b[i] = 'N';
break;
default:
show_v_b[i] = '?';
}
}
show_v_b[i] = '\0';
}
void gprs_rlc_v_n::reset()
{
for (size_t i = 0; i < ARRAY_SIZE(m_v_n); ++i)
m_v_n[i] = GPRS_RLC_UL_BSN_INVALID;
}
void gprs_rlc_window::set_sns(uint16_t sns)
{
OSMO_ASSERT(sns >= RLC_GPRS_SNS);
OSMO_ASSERT(sns <= RLC_MAX_SNS);
/* check for 2^n */
OSMO_ASSERT((sns & (-sns)) == sns);
m_sns = sns;
}
void gprs_rlc_window::set_ws(uint16_t ws)
{
OSMO_ASSERT(ws >= RLC_GPRS_SNS/2);
OSMO_ASSERT(ws <= RLC_MAX_SNS/2);
m_ws = ws;
}
/* Update the receive block bitmap */
void gprs_rlc_ul_window::update_rbb(char *rbb)
{
int i;
for (i=0; i < ws(); i++) {
if (m_v_n.is_received(ssn()-1-i))
rbb[ws()-1-i] = 'R';
else
rbb[ws()-1-i] = 'I';
}
}
/* Raise V(R) to highest received sequence number not received. */
void gprs_rlc_ul_window::raise_v_r(const uint16_t bsn)
{
uint16_t offset_v_r;
offset_v_r = mod_sns(bsn + 1 - v_r());
/* Positive offset, so raise. */
if (offset_v_r < (sns() >> 1)) {
while (offset_v_r--) {
if (offset_v_r) /* all except the received block */
m_v_n.mark_missing(v_r());
raise_v_r_to(1);
}
LOGP(DRLCMACUL, LOGL_DEBUG, "- Raising V(R) to %d\n", v_r());
}
}
/*
* Raise V(Q) if possible. This is looped until there is a gap
* (non received block) or the window is empty.
*/
uint16_t gprs_rlc_ul_window::raise_v_q()
{
uint16_t count = 0;
while (v_q() != v_r()) {
if (!m_v_n.is_received(v_q()))
break;
LOGP(DRLCMACUL, LOGL_DEBUG, "- Taking block %d out, raising "
"V(Q) to %d\n", v_q(), mod_sns(v_q() + 1));
raise_v_q(1);
count += 1;
}
return count;
}
void gprs_rlc_ul_window::receive_bsn(const uint16_t bsn)
{
m_v_n.mark_received(bsn);
raise_v_r(bsn);
}
bool gprs_rlc_ul_window::invalidate_bsn(const uint16_t bsn)
{
bool was_valid = m_v_n.is_received(bsn);
m_v_n.mark_missing(bsn);
return was_valid;
}
static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
GprsCodingScheme cs, bool with_padding, unsigned int header_bits)
{
unsigned int i;
unsigned int padding_bits = with_padding ? cs.optionalPaddingBits() : 0;
memset(rlc, 0, sizeof(*rlc));
rlc->cs = cs;
rlc->with_padding = with_padding;
rlc->num_data_blocks = cs.numDataBlocks();
OSMO_ASSERT(rlc->num_data_blocks <= ARRAY_SIZE(rlc->block_info));
for (i = 0; i < rlc->num_data_blocks; i++) {
gprs_rlc_data_block_info_init(&rlc->block_info[i], cs,
with_padding);
rlc->data_offs_bits[i] =
header_bits + padding_bits +
(i+1) * cs.numDataBlockHeaderBits() +
i * 8 * rlc->block_info[0].data_len;
}
}
void gprs_rlc_data_info_init_dl(struct gprs_rlc_data_info *rlc,
GprsCodingScheme cs, bool with_padding)
{
return gprs_rlc_data_header_init(rlc, cs, with_padding,
cs.numDataHeaderBitsDL());
}
void gprs_rlc_data_info_init_ul(struct gprs_rlc_data_info *rlc,
GprsCodingScheme cs, bool with_padding)
{
return gprs_rlc_data_header_init(rlc, cs, with_padding,
cs.numDataHeaderBitsUL());
}
void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
GprsCodingScheme cs, bool with_padding)
{
unsigned int data_len = cs.maxDataBlockBytes();
if (with_padding)
data_len -= cs.optionalPaddingBits() / 8;
rdbi->data_len = data_len;
rdbi->bsn = 0;
rdbi->ti = 0;
rdbi->e = 1;
rdbi->cv = 15;
rdbi->pi = 0;
rdbi->spb = 0;
}
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, int punct, int punct2,
int with_padding)
{
switch (GprsCodingScheme::Scheme(cs)) {
case GprsCodingScheme::MCS1: return 0b1011 + punct % 2;
case GprsCodingScheme::MCS2: return 0b1001 + punct % 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;
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;
default: ;
}
return -1;
}
void gprs_rlc_mcs_cps_decode(unsigned int cps,
GprsCodingScheme cs, int *punct, int *punct2, int *with_padding)
{
*punct2 = -1;
*with_padding = 0;
switch (GprsCodingScheme::Scheme(cs)) {
case GprsCodingScheme::MCS1:
cps -= 0b1011; *punct = cps % 2; break;
case GprsCodingScheme::MCS2:
cps -= 0b1001; *punct = cps % 2; break;
case GprsCodingScheme::MCS3:
cps -= 0b0011; *punct = cps % 3; *with_padding = cps >= 3; break;
case GprsCodingScheme::MCS4:
cps -= 0b0000; *punct = cps % 3; break;
case GprsCodingScheme::MCS5:
cps -= 0b100; *punct = cps % 2; break;
case GprsCodingScheme::MCS6:
cps -= 0b000; *punct = cps % 2; *with_padding = cps >= 2; break;
case GprsCodingScheme::MCS7:
cps -= 0b10100; *punct = cps / 3; *punct2 = cps % 3; break;
case GprsCodingScheme::MCS8:
cps -= 0b01011; *punct = cps / 3; *punct2 = cps % 3; break;
case GprsCodingScheme::MCS9:
cps -= 0b00000; *punct = cps / 4; *punct2 = cps % 3; break;
default: ;
}
}

577
src/rlc.h Normal file
View File

@ -0,0 +1,577 @@
/* rlc header descriptions
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
#include "gprs_coding_scheme.h"
#include <osmocom/core/endian.h>
#include <stdint.h>
#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 */
#define RLC_EGPRS_MAX_BSN_DELTA 512
#define RLC_MAX_SNS RLC_EGPRS_SNS
#define RLC_MAX_WS RLC_EGPRS_MAX_WS
#define RLC_MAX_LEN 74 /* MCS-9 data unit */
struct BTS;
struct gprs_rlc_v_n;
/* The state of a BSN in the send/receive window */
enum gprs_rlc_ul_bsn_state {
GPRS_RLC_UL_BSN_INVALID,
GPRS_RLC_UL_BSN_RECEIVED,
GPRS_RLC_UL_BSN_MISSING,
GPRS_RLC_UL_BSN_MAX,
};
enum gprs_rlc_dl_bsn_state {
GPRS_RLC_DL_BSN_INVALID,
GPRS_RLC_DL_BSN_NACKED,
GPRS_RLC_DL_BSN_ACKED,
GPRS_RLC_DL_BSN_UNACKED,
GPRS_RLC_DL_BSN_RESEND,
GPRS_RLC_DL_BSN_MAX,
};
static inline uint16_t mod_sns_half()
{
return (RLC_MAX_SNS / 2) - 1;
}
struct gprs_rlc_data_block_info {
unsigned int data_len; /* EGPRS: N2, GPRS: N2-2, N-2 */
unsigned int bsn;
unsigned int ti;
unsigned int e;
unsigned int cv; /* FBI == 1 <=> CV == 0 */
unsigned int pi;
unsigned int spb;
};
struct gprs_rlc_data_info {
GprsCodingScheme cs;
unsigned int r;
unsigned int si;
unsigned int tfi;
unsigned int cps;
unsigned int rsb;
unsigned int usf;
unsigned int es_p;
unsigned int rrbp;
unsigned int pr;
unsigned int num_data_blocks;
unsigned int with_padding;
unsigned int data_offs_bits[2];
struct gprs_rlc_data_block_info block_info[2];
};
struct gprs_rlc_data {
uint8_t *prepare(size_t block_data_length);
void put_data(const uint8_t *data, size_t len);
/* block history */
uint8_t block[RLC_MAX_LEN];
/* block len of history */
uint8_t len;
struct gprs_rlc_data_block_info block_info;
GprsCodingScheme cs;
};
void gprs_rlc_data_info_init_dl(struct gprs_rlc_data_info *rlc,
GprsCodingScheme cs, bool with_padding);
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);
void gprs_rlc_mcs_cps_decode(unsigned int cps, GprsCodingScheme cs,
int *punct, int *punct2, int *with_padding);
/*
* I hold the currently transferred blocks and will provide
* the routines to manipulate these arrays.
*/
struct gprs_rlc {
gprs_rlc_data *block(int bsn);
gprs_rlc_data m_blocks[RLC_MAX_SNS/2];
};
/**
* TODO: for GPRS/EDGE maybe make sns a template parameter
* so we create specialized versions...
*/
struct gprs_rlc_v_b {
/* Check for an individual frame */
bool is_unacked(int bsn) const;
bool is_nacked(int bsn) const;
bool is_acked(int bsn) const;
bool is_resend(int bsn) const;
bool is_invalid(int bsn) const;
gprs_rlc_dl_bsn_state get_state(int bsn) const;
/* Mark a RLC frame for something */
void mark_unacked(int bsn);
void mark_nacked(int bsn);
void mark_acked(int bsn);
void mark_resend(int bsn);
void mark_invalid(int bsn);
void reset();
private:
bool is_state(int bsn, const gprs_rlc_dl_bsn_state state) const;
void mark(int bsn, const gprs_rlc_dl_bsn_state state);
gprs_rlc_dl_bsn_state m_v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
};
/**
* TODO: The UL/DL code could/should share a base class.
*/
class gprs_rlc_window {
public:
gprs_rlc_window();
const uint16_t mod_sns() const;
const uint16_t mod_sns(uint16_t bsn) const;
const uint16_t sns() const;
const uint16_t ws() const;
void set_sns(uint16_t sns);
void set_ws(uint16_t ws);
protected:
uint16_t m_sns;
uint16_t m_ws;
};
struct gprs_rlc_dl_window: public gprs_rlc_window {
void reset();
bool window_stalled() const;
bool window_empty() const;
void increment_send();
void raise(int moves);
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;
/* Methods to manage reception */
int resend_needed();
int mark_for_resend();
void update(BTS *bts, char *show_rbb, uint16_t ssn,
uint16_t *lost, uint16_t *received);
void update(BTS *bts, const struct bitvec *rbb,
uint16_t first_bsn, uint16_t *lost,
uint16_t *received);
int move_window();
void show_state(char *show_rbb);
int count_unacked();
uint16_t m_v_s; /* send state */
uint16_t m_v_a; /* ack state */
gprs_rlc_v_b m_v_b;
gprs_rlc_dl_window();
};
struct gprs_rlc_v_n {
void reset();
void mark_received(int bsn);
void mark_missing(int bsn);
bool is_received(int bsn) const;
gprs_rlc_ul_bsn_state state(int bsn) const;
private:
bool is_state(int bsn, const gprs_rlc_ul_bsn_state state) const;
void mark(int bsn, const gprs_rlc_ul_bsn_state state);
gprs_rlc_ul_bsn_state m_v_n[RLC_MAX_SNS/2]; /* receive state array */
};
struct gprs_rlc_ul_window: public gprs_rlc_window {
const uint16_t v_r() const;
const uint16_t v_q() const;
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);
void raise_v_r_to(int moves);
void raise_v_r(const uint16_t bsn);
uint16_t raise_v_q();
void raise_v_q(int);
void receive_bsn(const uint16_t bsn);
bool invalidate_bsn(const uint16_t bsn);
uint16_t m_v_r; /* receive state */
uint16_t m_v_q; /* receive window state */
gprs_rlc_v_n m_v_n;
gprs_rlc_ul_window();
};
extern "C" {
/* TS 04.60 10.2.2 */
#if OSMO_IS_LITTLE_ENDIAN
struct rlc_ul_header {
uint8_t r:1,
si:1,
cv:4,
pt:2;
uint8_t ti:1,
tfi:5,
pi:1,
spare:1;
uint8_t e:1,
bsn:7;
} __attribute__ ((packed));
struct rlc_dl_header {
uint8_t usf:3,
s_p:1,
rrbp:2,
pt:2;
uint8_t fbi:1,
tfi:5,
pr:2;
uint8_t e:1,
bsn:7;
} __attribute__ ((packed));
struct rlc_li_field {
uint8_t e:1,
m:1,
li:6;
} __attribute__ ((packed));
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
}
inline bool gprs_rlc_v_b::is_state(int bsn, const gprs_rlc_dl_bsn_state type) const
{
return m_v_b[bsn & mod_sns_half()] == type;
}
inline void gprs_rlc_v_b::mark(int bsn, const gprs_rlc_dl_bsn_state type)
{
m_v_b[bsn & mod_sns_half()] = type;
}
inline bool gprs_rlc_v_b::is_nacked(int bsn) const
{
return is_state(bsn, GPRS_RLC_DL_BSN_NACKED);
}
inline bool gprs_rlc_v_b::is_acked(int bsn) const
{
return is_state(bsn, GPRS_RLC_DL_BSN_ACKED);
}
inline bool gprs_rlc_v_b::is_unacked(int bsn) const
{
return is_state(bsn, GPRS_RLC_DL_BSN_UNACKED);
}
inline bool gprs_rlc_v_b::is_resend(int bsn) const
{
return is_state(bsn, GPRS_RLC_DL_BSN_RESEND);
}
inline bool gprs_rlc_v_b::is_invalid(int bsn) const
{
return is_state(bsn, GPRS_RLC_DL_BSN_INVALID);
}
inline gprs_rlc_dl_bsn_state gprs_rlc_v_b::get_state(int bsn) const
{
return m_v_b[bsn & mod_sns_half()];
}
inline void gprs_rlc_v_b::mark_resend(int bsn)
{
return mark(bsn, GPRS_RLC_DL_BSN_RESEND);
}
inline void gprs_rlc_v_b::mark_unacked(int bsn)
{
return mark(bsn, GPRS_RLC_DL_BSN_UNACKED);
}
inline void gprs_rlc_v_b::mark_acked(int bsn)
{
return mark(bsn, GPRS_RLC_DL_BSN_ACKED);
}
inline void gprs_rlc_v_b::mark_nacked(int bsn)
{
return mark(bsn, GPRS_RLC_DL_BSN_NACKED);
}
inline void gprs_rlc_v_b::mark_invalid(int bsn)
{
return mark(bsn, GPRS_RLC_DL_BSN_INVALID);
}
inline gprs_rlc_window::gprs_rlc_window()
: m_sns(RLC_GPRS_SNS)
, m_ws(RLC_GPRS_WS)
{
}
inline const uint16_t gprs_rlc_window::sns() const
{
return m_sns;
}
inline const uint16_t gprs_rlc_window::ws() const
{
return m_ws;
}
inline const uint16_t gprs_rlc_window::mod_sns() const
{
return sns() - 1;
}
inline const uint16_t gprs_rlc_window::mod_sns(uint16_t bsn) const
{
return bsn & mod_sns();
}
inline gprs_rlc_dl_window::gprs_rlc_dl_window()
: m_v_s(0)
, m_v_a(0)
{
}
inline const uint16_t gprs_rlc_dl_window::v_s() const
{
return m_v_s;
}
inline const uint16_t gprs_rlc_dl_window::v_s_mod(int offset) const
{
return mod_sns(m_v_s + offset);
}
inline const uint16_t gprs_rlc_dl_window::v_a() const
{
return m_v_a;
}
inline bool gprs_rlc_dl_window::window_stalled() const
{
return (mod_sns(m_v_s - m_v_a)) == ws();
}
inline bool gprs_rlc_dl_window::window_empty() const
{
return m_v_s == m_v_a;
}
inline void gprs_rlc_dl_window::increment_send()
{
m_v_s = (m_v_s + 1) & mod_sns();
}
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
{
return (m_v_s - m_v_a) & mod_sns();
}
inline gprs_rlc_ul_window::gprs_rlc_ul_window()
: m_v_r(0)
, m_v_q(0)
{
}
inline bool gprs_rlc_ul_window::is_in_window(uint16_t bsn) const
{
uint16_t offset_v_q;
/* current block relative to lowest unreceived block */
offset_v_q = (bsn - m_v_q) & mod_sns();
/* If out of window (may happen if blocks below V(Q) are received
* again. */
return offset_v_q < ws();
}
inline bool gprs_rlc_ul_window::is_received(uint16_t bsn) const
{
uint16_t offset_v_r;
/* Offset to the end of the received window */
offset_v_r = (m_v_r - 1 - bsn) & mod_sns();
return is_in_window(bsn) && m_v_n.is_received(bsn) && offset_v_r < ws();
}
inline const uint16_t gprs_rlc_ul_window::v_r() const
{
return m_v_r;
}
inline const uint16_t gprs_rlc_ul_window::v_q() const
{
return m_v_q;
}
inline const uint16_t gprs_rlc_ul_window::ssn() const
{
return m_v_r;
}
inline void gprs_rlc_ul_window::raise_v_r_to(int moves)
{
m_v_r = mod_sns(m_v_r + moves);
}
inline void gprs_rlc_ul_window::raise_v_q(int incr)
{
m_v_q = mod_sns(m_v_q + incr);
}
inline void gprs_rlc_v_n::mark_received(int bsn)
{
return mark(bsn, GPRS_RLC_UL_BSN_RECEIVED);
}
inline void gprs_rlc_v_n::mark_missing(int bsn)
{
return mark(bsn, GPRS_RLC_UL_BSN_MISSING);
}
inline bool gprs_rlc_v_n::is_received(int bsn) const
{
return is_state(bsn, GPRS_RLC_UL_BSN_RECEIVED);
}
inline bool gprs_rlc_v_n::is_state(int bsn, gprs_rlc_ul_bsn_state type) const
{
return m_v_n[bsn & mod_sns_half()] == type;
}
inline void gprs_rlc_v_n::mark(int bsn, gprs_rlc_ul_bsn_state type)
{
m_v_n[bsn & mod_sns_half()] = type;
}
inline gprs_rlc_ul_bsn_state gprs_rlc_v_n::state(int bsn) const
{
return m_v_n[bsn & mod_sns_half()];
}
inline gprs_rlc_data *gprs_rlc::block(int bsn)
{
return &m_blocks[bsn & mod_sns_half()];
}

149
src/sba.cpp Normal file
View File

@ -0,0 +1,149 @@
/* sba.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <sba.h>
#include <gprs_rlcmac.h>
#include <gprs_debug.h>
#include <bts.h>
extern "C" {
#include <osmocom/core/talloc.h>
}
#include <errno.h>
extern void *tall_pcu_ctx;
/* starting time for assigning single slot
* This offset must be a multiple of 13. */
#define AGCH_START_OFFSET 52
SBAController::SBAController(BTS &bts)
: m_bts(bts)
{
INIT_LLIST_HEAD(&m_sbas);
}
int SBAController::alloc(
uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta)
{
struct gprs_rlcmac_pdch *pdch;
struct gprs_rlcmac_sba *sba;
int8_t trx, ts;
uint32_t fn;
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);
return -EINVAL;
}
fn = (pdch->last_rts_fn + AGCH_START_OFFSET) % 2715648;
sba->trx_no = trx;
sba->ts_no = ts;
sba->fn = fn;
sba->ta = ta;
llist_add(&sba->list, &m_sbas);
m_bts.sba_allocated();
*_trx = trx;
*_ts = ts;
*_fn = fn;
return 0;
}
gprs_rlcmac_sba *SBAController::find(uint8_t trx, uint8_t ts, uint32_t fn)
{
struct gprs_rlcmac_sba *sba;
llist_for_each_entry(sba, &m_sbas, list) {
if (sba->trx_no == trx && sba->ts_no == ts && sba->fn == fn)
return sba;
}
return NULL;
}
gprs_rlcmac_sba *SBAController::find(const gprs_rlcmac_pdch *pdch, uint32_t fn)
{
return find(pdch->trx_no(), pdch->ts_no, fn);
}
uint32_t SBAController::sched(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr)
{
uint32_t sba_fn;
struct gprs_rlcmac_sba *sba;
/* check special TBF for events */
sba_fn = fn + 4;
if ((block_nr % 3) == 2)
sba_fn ++;
sba_fn = sba_fn % 2715648;
sba = find(trx, ts, sba_fn);
if (sba)
return sba_fn;
return 0xffffffff;
}
int SBAController::timeout(struct gprs_rlcmac_sba *sba)
{
LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for SBA\n");
m_bts.sba_timedout();
free_sba(sba);
return 0;
}
void SBAController::free_sba(gprs_rlcmac_sba *sba)
{
m_bts.sba_freed();
llist_del(&sba->list);
talloc_free(sba);
}
void SBAController::free_resources(struct gprs_rlcmac_pdch *pdch)
{
struct gprs_rlcmac_sba *sba, *sba2;
const uint8_t trx_no = pdch->trx->trx_no;
const uint8_t ts_no = pdch->ts_no;
llist_for_each_entry_safe(sba, sba2, &m_sbas, list) {
if (sba->trx_no == trx_no && sba->ts_no == ts_no)
free_sba(sba);
}
}

69
src/sba.h Normal file
View File

@ -0,0 +1,69 @@
/*
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
#include <stdint.h>
extern "C" {
#include <osmocom/core/linuxlist.h>
}
struct BTS;
class PollController;
struct gprs_rlcmac_sba;
struct gprs_rlcmac_pdch;
/*
* single block allocation entry
*/
struct gprs_rlcmac_sba {
struct llist_head list;
uint8_t trx_no;
uint8_t ts_no;
uint32_t fn;
uint8_t ta;
};
/**
* I help to manage SingleBlockAssignment (SBA).
*
* TODO: Add a flush method..
*/
class SBAController {
friend class PollController;
public:
SBAController(BTS &bts);
int alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta);
gprs_rlcmac_sba *find(uint8_t trx, uint8_t ts, uint32_t fn);
gprs_rlcmac_sba *find(const gprs_rlcmac_pdch *pdch, uint32_t fn);
uint32_t sched(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr);
int timeout(struct gprs_rlcmac_sba *sba);
void free_resources(struct gprs_rlcmac_pdch *pdch);
void free_sba(gprs_rlcmac_sba *sba);
private:
BTS &m_bts;
llist_head m_sbas;
};

View File

@ -24,6 +24,7 @@
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

View File

@ -36,7 +36,10 @@ static int l1if_req_pdch(struct femtol1_hdl *fl1h, struct msgb *msg)
{
struct osmo_wqueue *wqueue = &fl1h->write_q[MQ_PDTCH_WRITE];
osmo_wqueue_enqueue(wqueue, msg);
if (osmo_wqueue_enqueue(wqueue, msg) != 0) {
LOGP(DL1IF, LOGL_ERROR, "PDTCH queue full. dropping message.\n");
msgb_free(msg);
}
return 0;
}
@ -163,22 +166,47 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1h,
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 femtol1_hdl *fl1h,
GsmL1_PhDataInd_t *data_ind, struct msgb *l1p_msg)
{
int rc = 0;
struct pcu_l1_meas meas = {0};
gsmtap_send(fl1h->gsmtap, data_ind->u16Arfcn | GSMTAP_ARFCN_F_UPLINK,
data_ind->u8Tn, GSMTAP_CHANNEL_PACCH, 0,
data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer,
data_ind->msgUnitParam.u8Size);
DEBUGP(DL1IF, "Rx PH-DATA.ind %s (hL2 %08x): %s",
DEBUGP(DL1IF, "Rx PH-DATA.ind %s (hL2 %08x): %s\n",
get_value_string(femtobts_l1sapi_names, data_ind->sapi),
data_ind->hLayer2,
osmo_hexdump(data_ind->msgUnitParam.u8Buffer,
data_ind->msgUnitParam.u8Size));
pcu_rx_block_time(data_ind->u16Arfcn, data_ind->u32Fn, data_ind->u8Tn);
/*
* TODO: Add proper bad frame handling here. This could be used
* to switch the used CS. Avoid a crash with the PCU right now
* feed "0 - 1" amount of data.
*/
if (data_ind->msgUnitParam.u8Size == 0)
return -1;
gsmtap_send(fl1h->gsmtap, data_ind->u16Arfcn | GSMTAP_ARFCN_F_UPLINK,
data_ind->u8Tn, GSMTAP_CHANNEL_PACCH, 0,
data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer+1,
data_ind->msgUnitParam.u8Size-1);
get_meas(&meas, &data_ind->measParam);
switch (data_ind->sapi) {
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
@ -190,7 +218,8 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
pcu_rx_data_ind_pdtch((long)fl1h->priv, data_ind->u8Tn,
data_ind->msgUnitParam.u8Buffer + 1,
data_ind->msgUnitParam.u8Size - 1,
data_ind->u32Fn);
data_ind->u32Fn,
&meas);
break;
case GsmL1_Sapi_Ptcch:
// FIXME
@ -210,6 +239,8 @@ 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;
@ -220,6 +251,12 @@ static int handle_ph_ra_ind(struct femtol1_hdl *fl1h, GsmL1_PhRaInd_t *ra_ind)
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",
@ -312,12 +349,15 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
/* transmit */
osmo_wqueue_enqueue(&fl1h->write_q[MQ_PDTCH_WRITE], msg);
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(void *priv, uint32_t hlayer1)
void *l1if_open_pdch(void *priv, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
{
struct femtol1_hdl *fl1h;
int rc;
@ -338,9 +378,7 @@ void *l1if_open_pdch(void *priv, uint32_t hlayer1)
return NULL;
}
fl1h->gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 1);
if (fl1h->gsmtap)
gsmtap_source_add_sink(fl1h->gsmtap);
fl1h->gsmtap = gsmtap;
return fl1h;
}

View File

@ -73,9 +73,19 @@ 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 femtol1_hdl *fl1h, struct msgb *msg);
int l1if_handle_l1prim(int wq, struct femtol1_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 femtol1_hdl *hdl);
int l1if_transport_close(int q, struct femtol1_hdl *hdl);
#endif /* _SYSMO_L1_IF_H */

1251
src/tbf.cpp Normal file

File diff suppressed because it is too large Load Diff

490
src/tbf.h Normal file
View File

@ -0,0 +1,490 @@
/*
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
#ifdef __cplusplus
#include "gprs_rlcmac.h"
#include "llc.h"
#include "rlc.h"
#include "cxx_linuxlist.h"
#include <gprs_debug.h>
#include <stdint.h>
struct bssgp_bvc_ctx;
struct rlc_ul_header;
struct msgb;
struct pcu_l1_meas;
class GprsMs;
/*
* TBF instance
*/
#define Tassign_agch 0,200000 /* waiting after IMM.ASS confirm */
#define Tassign_pacch 2,0 /* timeout for pacch assigment */
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_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 */
GPRS_RLCMAC_RELEASING, /* releasing, wait to free TBI/USF */
};
enum gprs_rlcmac_tbf_poll_state {
GPRS_RLCMAC_POLL_NONE = 0,
GPRS_RLCMAC_POLL_SCHED, /* a polling was scheduled */
};
enum gprs_rlcmac_tbf_dl_ass_state {
GPRS_RLCMAC_DL_ASS_NONE = 0,
GPRS_RLCMAC_DL_ASS_SEND_ASS, /* send downlink assignment on next RTS */
GPRS_RLCMAC_DL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
};
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_WAIT_ACK, /* wait for PACKET CONTROL ACK */
};
enum gprs_rlcmac_tbf_ul_ack_state {
GPRS_RLCMAC_UL_ACK_NONE = 0,
GPRS_RLCMAC_UL_ACK_SEND_ACK, /* send acknowledge on next RTS */
GPRS_RLCMAC_UL_ACK_WAIT_ACK, /* wait for PACKET CONTROL ACK */
};
enum gprs_rlcmac_tbf_direction {
GPRS_RLCMAC_DL_TBF,
GPRS_RLCMAC_UL_TBF
};
#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 */
#define GPRS_RLCMAC_FLAG_DL_ACK 3 /* downlink acknowledge received */
#define GPRS_RLCMAC_FLAG_TO_UL_ACK 4
#define GPRS_RLCMAC_FLAG_TO_DL_ACK 5
#define GPRS_RLCMAC_FLAG_TO_UL_ASS 6
#define GPRS_RLCMAC_FLAG_TO_DL_ASS 7
#define GPRS_RLCMAC_FLAG_TO_MASK 0xf0 /* timeout bits */
struct gprs_rlcmac_tbf {
gprs_rlcmac_tbf(BTS *bts_, gprs_rlcmac_tbf_direction dir);
static void free_all(struct gprs_rlcmac_trx *trx);
static void free_all(struct gprs_rlcmac_pdch *pdch);
bool state_is(enum gprs_rlcmac_tbf_state rhs) const;
bool state_is_not(enum gprs_rlcmac_tbf_state rhs) const;
void set_state(enum gprs_rlcmac_tbf_state new_state);
const char *state_name() const;
const char *name() const;
struct msgb *create_dl_ass(uint32_t fn, uint8_t ts);
struct msgb *create_ul_ass(uint32_t fn, uint8_t ts);
GprsMs *ms() const;
void set_ms(GprsMs *ms);
gprs_rlc_window *window();
uint8_t tsc() const;
int rlcmac_diag();
int update();
void handle_timeout();
void stop_timer();
void stop_t3191();
int establish_dl_tbf_on_pacch();
int check_polling(uint32_t fn, uint8_t ts,
uint32_t *poll_fn, unsigned int *rrbp);
void set_polling(uint32_t poll_fn, uint8_t ts);
void poll_timeout();
/** tlli handling */
uint32_t tlli() const;
bool is_tlli_valid() const;
/** MS updating */
void update_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction);
uint8_t tfi() const;
bool is_tfi_assigned() const;
const char *imsi() const;
void assign_imsi(const char *imsi);
uint8_t ta() const;
void set_ta(uint8_t);
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;
time_t created_ts() const;
uint8_t dl_slots() const;
uint8_t ul_slots() const;
bool is_control_ts(uint8_t ts) const;
/* EGPRS */
bool is_egprs_enabled() const;
void enable_egprs();
void disable_egprs();
/* attempt to make things a bit more fair */
void rotate_in_list();
LListHead<gprs_rlcmac_tbf>& ms_list() {return this->m_ms_list;}
const LListHead<gprs_rlcmac_tbf>& ms_list() const {return this->m_ms_list;}
LListHead<gprs_rlcmac_tbf>& list();
const LListHead<gprs_rlcmac_tbf>& list() const;
uint32_t state_flags;
enum gprs_rlcmac_tbf_direction direction;
struct gprs_rlcmac_trx *trx;
uint8_t first_ts; /* first TS used by TBF */
uint8_t first_common_ts; /* first TS that the phone can send and
reveive simultaniously */
uint8_t control_ts; /* timeslot control messages and polling */
struct gprs_rlcmac_pdch *pdch[8]; /* list of PDCHs allocated to TBF */
gprs_llc m_llc;
enum gprs_rlcmac_tbf_dl_ass_state dl_ass_state;
enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
enum gprs_rlcmac_tbf_poll_state poll_state;
uint32_t poll_fn; /* frame number to poll */
uint8_t poll_ts; /* TS to poll */
gprs_rlc m_rlc;
uint8_t n3105; /* N3105 counter */
struct osmo_timer_list timer;
unsigned int T; /* Txxxx number */
unsigned int num_T_exp; /* number of consecutive T expirations */
struct osmo_gsm_timer_list gsm_timer;
unsigned int fT; /* fTxxxx number */
unsigned int num_fT_exp; /* number of consecutive fT expirations */
struct Meas {
struct timeval rssi_tv; /* timestamp for rssi calculation */
int32_t rssi_sum; /* sum of rssi values */
int rssi_num; /* number of rssi values added since rssi_tv */
Meas();
} meas;
/* these should become protected but only after gprs_rlcmac_data.c
* stops to iterate over all tbf in its current form */
enum gprs_rlcmac_tbf_state state;
/* Remember if the tbf was in wait_release state when we want to
* schedule a new dl assignment */
uint8_t was_releasing;
/* Can/should we upgrade this tbf to use multiple slots? */
uint8_t upgrade_to_multislot;
/* store the BTS this TBF belongs to */
BTS *bts;
/*
* private fields. We can't make it private as it is breaking the
* llist macros.
*/
uint8_t m_tfi;
time_t m_created_ts;
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];
class GprsMs *m_ms;
/* Fields to take the TA/MS class values if no MS is associated */
uint8_t m_ta;
uint8_t m_ms_class;
private:
LListHead<gprs_rlcmac_tbf> m_list;
LListHead<gprs_rlcmac_tbf> m_ms_list;
bool m_egprs_enabled;
mutable char m_name_buf[60];
};
struct gprs_rlcmac_ul_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts,
int8_t use_trx, uint8_t ms_class, uint8_t egprs_ms_class,
uint32_t tlli, uint8_t ta, GprsMs *ms);
struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
GprsMs *ms, int8_t use_trx,
uint8_t ms_class, uint8_t egprs_ms_class, uint8_t single_slot);
struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
GprsMs *ms, int8_t use_trx,
uint8_t ms_class, uint8_t egprs_ms_class, uint8_t single_slot);
void tbf_free(struct gprs_rlcmac_tbf *tbf);
int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
unsigned int seconds, unsigned int microseconds);
inline bool gprs_rlcmac_tbf::state_is(enum gprs_rlcmac_tbf_state rhs) const
{
return state == rhs;
}
inline bool gprs_rlcmac_tbf::state_is_not(enum gprs_rlcmac_tbf_state rhs) const
{
return state != rhs;
}
const char *tbf_name(gprs_rlcmac_tbf *tbf);
inline const char *gprs_rlcmac_tbf::state_name() const
{
return tbf_state_name[state];
}
inline void gprs_rlcmac_tbf::set_state(enum gprs_rlcmac_tbf_state new_state)
{
LOGP(DRLCMAC, LOGL_DEBUG, "%s changes state from %s to %s\n",
tbf_name(this),
tbf_state_name[state], tbf_state_name[new_state]);
state = new_state;
}
inline LListHead<gprs_rlcmac_tbf>& gprs_rlcmac_tbf::list()
{
return this->m_list;
}
inline const LListHead<gprs_rlcmac_tbf>& gprs_rlcmac_tbf::list() const
{
return this->m_list;
}
inline GprsMs *gprs_rlcmac_tbf::ms() const
{
return m_ms;
}
inline bool gprs_rlcmac_tbf::is_tlli_valid() const
{
return tlli() != 0;
}
inline bool gprs_rlcmac_tbf::is_tfi_assigned() const
{
/* The TBF is established or has been assigned by a IMM.ASS for
* download */
return state > GPRS_RLCMAC_ASSIGN;
}
inline uint8_t gprs_rlcmac_tbf::tfi() const
{
return m_tfi;
}
inline time_t gprs_rlcmac_tbf::created_ts() const
{
return m_created_ts;
}
inline bool gprs_rlcmac_tbf::is_egprs_enabled() const
{
return m_egprs_enabled;
}
inline void gprs_rlcmac_tbf::enable_egprs()
{
m_egprs_enabled = true;
}
inline void gprs_rlcmac_tbf::disable_egprs()
{
m_egprs_enabled = false;
}
struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
gprs_rlcmac_dl_tbf(BTS *bts);
void cleanup();
/* dispatch Unitdata.DL messages */
static int handle(struct gprs_rlcmac_bts *bts,
const uint32_t tlli, const uint32_t old_tlli,
const char *imsi, const uint8_t ms_class,
const uint8_t egprs_ms_class, const uint16_t delay_csec,
const uint8_t *data, const uint16_t len);
int append_data(const uint8_t ms_class,
const uint16_t pdu_delay_csec,
const uint8_t *data, const uint16_t len);
int rcvd_dl_ack(uint8_t final, uint8_t ssn, uint8_t *rbb);
int rcvd_dl_ack(uint8_t final_ack, unsigned first_bsn, struct bitvec *rbb);
struct msgb *create_dl_acked_block(uint32_t fn, uint8_t ts);
void request_dl_ack();
bool need_control_ts() const;
bool have_data() const;
int frames_since_last_poll(unsigned fn) const;
int frames_since_last_drain(unsigned fn) const;
bool keep_open(unsigned fn) const;
int release();
int abort();
/* TODO: add the gettimeofday as parameter */
struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx);
/* Please note that all variables here will be reset when changing
* from WAIT RELEASE back to FLOW state (re-use of TBF).
* All states that need reset must be in this struct, so this is why
* variables are in both (dl and ul) structs and not outside union.
*/
gprs_rlc_dl_window m_window;
int32_t m_tx_counter; /* count all transmitted blocks */
uint8_t m_wait_confirm; /* wait for CCCH IMM.ASS cnf */
bool m_dl_ack_requested;
int32_t m_last_dl_poll_fn;
int32_t m_last_dl_drained_fn;
struct BandWidth {
struct timeval dl_bw_tv; /* timestamp for dl bw calculation */
uint32_t dl_bw_octets; /* number of octets since bw_tv */
struct timeval dl_loss_tv; /* timestamp for loss calculation */
uint16_t dl_loss_lost; /* sum of lost packets */
uint16_t dl_loss_received; /* sum of received packets */
BandWidth();
} m_bw;
protected:
struct ana_result {
unsigned received_packets;
unsigned lost_packets;
unsigned received_bytes;
unsigned lost_bytes;
};
int take_next_bsn(uint32_t fn, int previous_bsn,
bool *may_combine);
bool restart_bsn_cycle();
int create_new_bsn(const uint32_t fn, GprsCodingScheme cs);
struct msgb *create_dl_acked_block(const uint32_t fn, const uint8_t ts,
int index, int index2 = -1);
int update_window(const uint8_t ssn, const uint8_t *rbb);
int update_window(unsigned first_bsn, const struct bitvec *rbb);
int maybe_start_new_window();
bool dl_window_stalled() const;
void reuse_tbf();
void start_llc_timer();
int analyse_errors(char *show_rbb, uint8_t ssn, ana_result *res);
void schedule_next_frame();
struct osmo_timer_list m_llc_timer;
};
struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
gprs_rlcmac_ul_tbf(BTS *bts);
struct msgb *create_ul_ack(uint32_t fn, uint8_t ts);
/* blocks were acked */
int rcv_data_block_acknowledged(
const struct gprs_rlc_data_info *rlc,
uint8_t *data, struct pcu_l1_meas *meas);
/* TODO: extract LLC class? */
int assemble_forward_llc(const gprs_rlc_data *data);
int snd_ul_ud();
/* Please note that all variables here will be reset when changing
* from WAIT RELEASE back to FLOW state (re-use of TBF).
* All states that need reset must be in this struct, so this is why
* variables are in both (dl and ul) structs and not outside union.
*/
gprs_rlc_ul_window m_window;
int32_t m_rx_counter; /* count all received blocks */
uint8_t m_n3103; /* N3103 counter */
uint8_t m_usf[8]; /* list USFs per PDCH (timeslot) */
uint8_t m_contention_resolution_done; /* set after done */
uint8_t m_final_ack_sent; /* set if we sent final ack */
protected:
void maybe_schedule_uplink_acknack(const gprs_rlc_data_info *rlc);
};
inline enum gprs_rlcmac_tbf_direction reverse(enum gprs_rlcmac_tbf_direction dir)
{
return (enum gprs_rlcmac_tbf_direction)
((int)GPRS_RLCMAC_UL_TBF - (int)dir + (int)GPRS_RLCMAC_DL_TBF);
}
inline gprs_rlcmac_ul_tbf *as_ul_tbf(gprs_rlcmac_tbf *tbf)
{
if (tbf && tbf->direction == GPRS_RLCMAC_UL_TBF)
return static_cast<gprs_rlcmac_ul_tbf *>(tbf);
else
return NULL;
}
inline gprs_rlcmac_dl_tbf *as_dl_tbf(gprs_rlcmac_tbf *tbf)
{
if (tbf && tbf->direction == GPRS_RLCMAC_DL_TBF)
return static_cast<gprs_rlcmac_dl_tbf *>(tbf);
else
return NULL;
}
inline gprs_rlc_window *gprs_rlcmac_tbf::window()
{
switch (direction)
{
case GPRS_RLCMAC_UL_TBF: return &as_ul_tbf(this)->m_window;
case GPRS_RLCMAC_DL_TBF: return &as_dl_tbf(this)->m_window;
}
return NULL;
}
#endif

1127
src/tbf_dl.cpp Normal file

File diff suppressed because it is too large Load Diff

391
src/tbf_ul.cpp Normal file
View File

@ -0,0 +1,391 @@
/* Copied from tbf.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <bts.h>
#include <tbf.h>
#include <rlc.h>
#include <encoding.h>
#include <gprs_rlcmac.h>
#include <gprs_debug.h>
#include <gprs_bssgp_pcu.h>
#include <decoding.h>
#include <pcu_l1_if.h>
extern "C" {
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
}
#include <errno.h>
#include <string.h>
/* After receiving these frames, we send ack/nack. */
#define SEND_ACK_AFTER_FRAMES 20
extern void *tall_pcu_ctx;
/*
* Store received block data in LLC message(s) and forward to SGSN
* if complete.
*/
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;
Decoding::RlcData frames[16], *frame;
int i, num_frames = 0;
uint32_t dummy_tlli;
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),
&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);
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();
m_llc.reset();
}
}
return 0;
}
struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn, uint8_t ts)
{
int final = (state_is(GPRS_RLCMAC_FINISHED));
struct msgb *msg;
int rc;
unsigned int rrbp = 0;
uint32_t new_poll_fn = 0;
if (final) {
if (poll_state == GPRS_RLCMAC_POLL_SCHED &&
ul_ack_state == GPRS_RLCMAC_UL_ACK_WAIT_ACK) {
LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
"scheduled for %s, so we must wait for "
"the final uplink ack...\n", tbf_name(this));
return NULL;
}
rc = check_polling(fn, ts, &new_poll_fn, &rrbp);
if (rc < 0)
return NULL;
}
msg = msgb_alloc(23, "rlcmac_ul_ack");
if (!msg)
return NULL;
bitvec *ack_vec = bitvec_alloc(23);
if (!ack_vec) {
msgb_free(msg);
return NULL;
}
bitvec_unhex(ack_vec,
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
Encoding::write_packet_uplink_ack(bts_data(), ack_vec, this, final, rrbp);
bitvec_pack(ack_vec, msgb_put(msg, 23));
bitvec_free(ack_vec);
/* now we must set this flag, so we are allowed to assign downlink
* TBF on PACCH. it is only allowed when TLLI is acknowledged. */
m_contention_resolution_done = 1;
if (final) {
set_polling(new_poll_fn, ts);
/* waiting for final acknowledge */
ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK;
m_final_ack_sent = 1;
} else
ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
return msg;
}
int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
const struct gprs_rlc_data_info *rlc,
uint8_t *data, struct pcu_l1_meas *meas)
{
int8_t rssi = meas->have_rssi ? meas->rssi : 0;
const uint16_t ws = m_window.ws();
this->state_flags |= (1 << GPRS_RLCMAC_FLAG_UL_DATA);
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA TFI=%d received (V(Q)=%d .. "
"V(R)=%d)\n", rlc->tfi, this->m_window.v_q(),
this->m_window.v_r());
/* process RSSI */
gprs_rlcmac_rssi(this, rssi);
/* store measurement values */
if (ms())
ms()->update_l1_meas(meas);
uint32_t new_tlli = 0;
unsigned int block_idx;
/* restart T3169 */
tbf_timer_start(this, 3169, bts_data()->t3169, 0);
/* Increment RX-counter */
this->m_rx_counter++;
/* Loop over num_blocks */
for (block_idx = 0; block_idx < rlc->num_data_blocks; block_idx++) {
int num_chunks;
uint8_t *rlc_data;
const struct gprs_rlc_data_block_info *rdbi =
&rlc->block_info[block_idx];
bool need_rlc_data = false;
struct gprs_rlc_data *block;
LOGP(DRLCMACUL, LOGL_DEBUG,
"%s: Got %s RLC data block: "
"CV=%d, BSN=%d, SPB=%d, "
"PI=%d, E=%d, TI=%d, bitoffs=%d\n",
name(), rlc->cs.name(),
rdbi->cv, rdbi->bsn, rdbi->spb,
rdbi->pi, rdbi->e, rdbi->ti,
rlc->data_offs_bits[block_idx]);
/* Check whether the block needs to be decoded */
if (!m_window.is_in_window(rdbi->bsn)) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d out of window "
"%d..%d (it's normal)\n", rdbi->bsn,
m_window.v_q(),
m_window.mod_sns(m_window.v_q() + ws - 1));
} else if (m_window.is_received(rdbi->bsn)) {
LOGP(DRLCMACUL, LOGL_DEBUG,
"- BSN %d already received\n", rdbi->bsn);
} else {
need_rlc_data = true;
}
if (!is_tlli_valid()) {
if (!rdbi->ti) {
LOGP(DRLCMACUL, LOGL_NOTICE,
"%s: Missing TLLI within UL DATA.\n",
name());
continue;
}
need_rlc_data = true;
}
if (!need_rlc_data)
continue;
/* Store block and meta info to BSN buffer */
LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d storing in window (%d..%d)\n",
rdbi->bsn, m_window.v_q(),
m_window.mod_sns(m_window.v_q() + ws - 1));
block = m_rlc.block(rdbi->bsn);
block->block_info = *rdbi;
block->cs = rlc->cs;
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);
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(
rdbi, rlc->cs, rlc_data, NULL, 0, &new_tlli);
if (num_chunks < 0) {
bts->decode_error();
LOGP(DRLCMACUL, LOGL_NOTICE,
"Failed to decode TLLI of %s UL DATA "
"TFI=%d.\n", rlc->cs.name(), rlc->tfi);
m_window.invalidate_bsn(rdbi->bsn);
continue;
}
if (!this->is_tlli_valid()) {
if (!new_tlli) {
LOGP(DRLCMACUL, LOGL_NOTICE,
"%s: TLLI = 0 within UL DATA.\n",
name());
m_window.invalidate_bsn(rdbi->bsn);
continue;
}
LOGP(DRLCMACUL, LOGL_INFO,
"Decoded premier TLLI=0x%08x of "
"UL DATA TFI=%d.\n", tlli(), rlc->tfi);
set_tlli_from_ul(new_tlli);
} else if (new_tlli && new_tlli != tlli()) {
LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL "
"DATA TFI=%d. (Ignoring due to contention "
"resolution)\n", rlc->tfi);
m_window.invalidate_bsn(rdbi->bsn);
continue;
}
}
m_window.receive_bsn(rdbi->bsn);
}
/* Raise V(Q) if possible, and retrieve LLC frames from blocks.
* This is looped until there is a gap (non received block) or
* the window is empty.*/
const uint16_t v_q_beg = m_window.v_q();
const uint16_t count = m_window.raise_v_q();
/* Retrieve LLC frames from blocks that are ready */
for (uint16_t i = 0; i < count; ++i) {
uint16_t index = m_window.mod_sns(v_q_beg + i);
assemble_forward_llc(m_rlc.block(index));
}
/* Check CV of last frame in buffer */
if (this->state_is(GPRS_RLCMAC_FLOW) /* still in flow state */
&& this->m_window.v_q() == this->m_window.v_r()) { /* if complete */
struct gprs_rlc_data *block =
m_rlc.block(m_window.mod_sns(m_window.v_r() - 1));
const struct gprs_rlc_data_block_info *rdbi =
&block->block_info;
LOGP(DRLCMACUL, LOGL_DEBUG, "- No gaps in received block, "
"last block: BSN=%d CV=%d\n", rdbi->bsn,
rdbi->cv);
if (rdbi->cv == 0) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Finished with UL "
"TBF\n");
set_state(GPRS_RLCMAC_FINISHED);
/* Reset N3103 counter. */
this->m_n3103 = 0;
}
}
/* If TLLI is included or if we received half of the window, we send
* an ack/nack */
maybe_schedule_uplink_acknack(rlc);
return 0;
}
void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack(
const gprs_rlc_data_info *rlc)
{
bool have_ti = rlc->block_info[0].ti ||
(rlc->num_data_blocks > 1 && rlc->block_info[1].ti);
if (rlc->si || have_ti || state_is(GPRS_RLCMAC_FINISHED) ||
(m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0)
{
if (rlc->si) {
LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, "
"because MS is stalled.\n");
}
if (have_ti) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
"because TLLI is included.\n");
}
if (state_is(GPRS_RLCMAC_FINISHED)) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
"because last block has CV==0.\n");
}
if ((m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
"because %d frames received.\n",
SEND_ACK_AFTER_FRAMES);
}
if (ul_ack_state == GPRS_RLCMAC_UL_ACK_NONE) {
/* trigger sending at next RTS */
ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK;
} else {
/* already triggered */
LOGP(DRLCMACUL, LOGL_DEBUG, "- Sending Ack/Nack is "
"already triggered, don't schedule!\n");
}
}
}
/* Send Uplink unit-data to SGSN. */
int gprs_rlcmac_ul_tbf::snd_ul_ud()
{
uint8_t qos_profile[3];
struct msgb *llc_pdu;
unsigned msg_len = NS_HDR_LEN + BSSGP_HDR_LEN + m_llc.frame_length();
struct bssgp_bvc_ctx *bctx = gprs_bssgp_pcu_current_bctx();
LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] %s len=%d\n", tbf_name(this), m_llc.frame_length());
if (!bctx) {
LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
m_llc.reset_frame_space();
return -EIO;
}
llc_pdu = msgb_alloc_headroom(msg_len, msg_len,"llc_pdu");
uint8_t *buf = msgb_push(llc_pdu, TL16V_GROSS_LEN(sizeof(uint8_t)*m_llc.frame_length()));
tl16v_put(buf, BSSGP_IE_LLC_PDU, sizeof(uint8_t)*m_llc.frame_length(), m_llc.frame);
qos_profile[0] = QOS_PROFILE >> 16;
qos_profile[1] = QOS_PROFILE >> 8;
qos_profile[2] = QOS_PROFILE;
bssgp_tx_ul_ud(bctx, tlli(), qos_profile, llc_pdu);
m_llc.reset_frame_space();
return 0;
}

149
tests/Makefile.am Normal file
View File

@ -0,0 +1,149 @@
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
noinst_PROGRAMS = emu/pcu_emu
rlcmac_RLCMACTest_SOURCES = rlcmac/RLCMACTest.cpp
rlcmac_RLCMACTest_LDADD = \
$(top_builddir)/src/libgprs.la \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
alloc_AllocTest_SOURCES = alloc/AllocTest.cpp
alloc_AllocTest_LDADD = \
$(top_builddir)/src/libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
tbf_TbfTest_SOURCES = tbf/TbfTest.cpp
tbf_TbfTest_LDADD = \
$(top_builddir)/src/libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
edge_EdgeTest_SOURCES = edge/EdgeTest.cpp
edge_EdgeTest_LDADD = \
$(top_builddir)/src/libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
emu_pcu_emu_SOURCES = emu/pcu_emu.cpp emu/test_replay_gprs_attach.cpp \
emu/openbsc_clone.c emu/openbsc_clone.h emu/gprs_tests.h \
emu/test_pdp_activation.cpp
emu_pcu_emu_LDADD = \
$(top_builddir)/src/libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
types_TypesTest_SOURCES = types/TypesTest.cpp
types_TypesTest_LDADD = \
$(top_builddir)/src/libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
ms_MsTest_SOURCES = ms/MsTest.cpp
ms_MsTest_LDADD = \
$(top_builddir)/src/libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
ms_MsTest_LDFLAGS = \
-Wl,-u,bssgp_prim_cb
llc_LlcTest_SOURCES = llc/LlcTest.cpp
llc_LlcTest_LDADD = \
$(top_builddir)/src/libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
llc_LlcTest_LDFLAGS = \
-Wl,-u,bssgp_prim_cb
llist_LListTest_SOURCES = llist/LListTest.cpp
llist_LListTest_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
codel_codel_test_SOURCES = codel/codel_test.c
codel_codel_test_LDADD = \
$(top_builddir)/src/libgprs.la \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
:;{ \
echo '# Signature of the current package.' && \
echo 'm4_define([AT_PACKAGE_NAME],' && \
echo ' [$(PACKAGE_NAME)])' && \
echo 'm4_define([AT_PACKAGE_TARNAME],' && \
echo ' [$(PACKAGE_TARNAME)])' && \
echo 'm4_define([AT_PACKAGE_VERSION],' && \
echo ' [$(PACKAGE_VERSION)])' && \
echo 'm4_define([AT_PACKAGE_STRING],' && \
echo ' [$(PACKAGE_STRING)])' && \
echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \
echo ' [$(PACKAGE_BUGREPORT)])'; \
echo 'm4_define([AT_PACKAGE_URL],' && \
echo ' [$(PACKAGE_URL)])'; \
} >'$(srcdir)/package.m4'
EXTRA_DIST = \
testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
rlcmac/RLCMACTest.ok rlcmac/RLCMACTest.err \
alloc/AllocTest.ok alloc/AllocTest.err \
tbf/TbfTest.ok tbf/TbfTest.err \
types/TypesTest.ok types/TypesTest.err \
ms/MsTest.ok ms/MsTest.err \
llc/LlcTest.ok llc/LlcTest.err \
llist/LListTest.ok llist/LListTest.err \
codel/codel_test.ok \
edge/EdgeTest.ok
DISTCLEANFILES = atconfig
TESTSUITE = $(srcdir)/testsuite
# Python testing
if ENABLE_VTY_TESTS
python-tests: $(BUILT_SOURCES)
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
else
python-tests: $(BUILT_SOURCES)
@echo "Not running python-based tests (determined at configure-time)"
endif
check-local: $(BUILT_SOURCES) $(TESTSUITE)
$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
$(MAKE) $(AM_MAKEFLAGS) python-tests
installcheck-local: atconfig $(TESTSUITE)
$(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \
$(TESTSUITEFLAGS)
clean-local:
test ! -f '$(TESTSUITE)' || \
$(SHELL) '$(TESTSUITE)' --clean
AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te
AUTOTEST = $(AUTOM4TE) --language=autotest
$(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4
$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
mv $@.tmp $@

823
tests/alloc/AllocTest.cpp Normal file
View File

@ -0,0 +1,823 @@
/* AllocTest.cpp
*
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "gprs_rlcmac.h"
#include "gprs_debug.h"
#include "tbf.h"
#include "bts.h"
#include <string.h>
#include <stdio.h>
extern "C" {
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
}
/* globals used by the code */
void *tall_pcu_ctx;
int16_t spoof_mnc = 0, spoof_mcc = 0;
static gprs_rlcmac_tbf *tbf_alloc(struct gprs_rlcmac_bts *bts,
GprsMs *ms, gprs_rlcmac_tbf_direction dir,
uint8_t use_trx,
uint8_t ms_class, uint8_t egprs_ms_class, uint8_t single_slot)
{
if (dir == GPRS_RLCMAC_UL_TBF)
return tbf_alloc_ul_tbf(bts, ms, use_trx,
ms_class, egprs_ms_class, single_slot);
else
return tbf_alloc_dl_tbf(bts, ms, use_trx,
ms_class, egprs_ms_class, single_slot);
}
static void check_tfi_usage(BTS *the_bts)
{
int pdch_no;
struct gprs_rlcmac_tbf *tfi_usage[8][8][2][32] = {{{{NULL}}}};
LListHead<gprs_rlcmac_tbf> *tbf_lists[2] = {
&the_bts->ul_tbfs(),
&the_bts->dl_tbfs()
};
LListHead<gprs_rlcmac_tbf> *pos;
gprs_rlcmac_tbf *tbf;
unsigned list_idx;
struct gprs_rlcmac_tbf **tbf_var;
for (list_idx = 0; list_idx < ARRAY_SIZE(tbf_lists); list_idx += 1)
{
llist_for_each(pos, tbf_lists[list_idx]) {
tbf = pos->entry();
for (pdch_no = 0; pdch_no < 8; pdch_no += 1) {
struct gprs_rlcmac_pdch *pdch = tbf->pdch[pdch_no];
if (pdch == NULL)
continue;
tbf_var = &tfi_usage
[tbf->trx->trx_no]
[pdch_no]
[tbf->direction]
[tbf->tfi()];
OSMO_ASSERT(*tbf_var == NULL);
if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
OSMO_ASSERT(pdch->dl_tbf_by_tfi(
tbf->tfi()) == tbf);
OSMO_ASSERT(the_bts->dl_tbf_by_tfi(
tbf->tfi(),
tbf->trx->trx_no,
pdch_no) == tbf);
} else {
OSMO_ASSERT(pdch->ul_tbf_by_tfi(
tbf->tfi()) == tbf);
OSMO_ASSERT(the_bts->ul_tbf_by_tfi(
tbf->tfi(),
tbf->trx->trx_no,
pdch_no) == tbf);
}
*tbf_var = tbf;
OSMO_ASSERT(pdch->assigned_tfi(tbf->direction) &
(1 << tbf->tfi()));
}
}
}
}
static void test_alloc_a(gprs_rlcmac_tbf_direction dir,
uint8_t slots, const int count)
{
int tfi;
int i;
uint8_t used_trx, tmp_trx;
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_tbf *tbfs[32*8+1] = { 0, };
printf("Testing alloc_a direction(%d)\n", dir);
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_a;
struct gprs_rlcmac_trx *trx = &bts->trx[0];
for (i = 0; i < 8; i += 1)
if (slots & (1 << i))
trx->pdch[i].enable();
OSMO_ASSERT(count >= 0 && count <= (int)ARRAY_SIZE(tbfs));
/**
* Currently alloc_a will only allocate from the first
* PDCH and all possible usf's. We run out of usf's before
* we are out of tfi's. Observe this and make sure that at
* least this part is working okay.
*/
for (i = 0; i < (int)ARRAY_SIZE(tbfs); ++i) {
tbfs[i] = tbf_alloc(bts, NULL, dir, -1, 0, 0, 0);
if (tbfs[i] == NULL)
break;
used_trx = tbfs[i]->trx->trx_no;
tfi = the_bts.tfi_find_free(dir, &tmp_trx, used_trx);
OSMO_ASSERT(tbfs[i]->tfi() != tfi);
}
check_tfi_usage(&the_bts);
OSMO_ASSERT(i == count);
for (i = 0; i < count; ++i)
if (tbfs[i])
tbf_free(tbfs[i]);
tbfs[0] = tbf_alloc(bts, NULL, dir, -1, 0, 0, 0);
OSMO_ASSERT(tbfs[0]);
tbf_free(tbfs[0]);
}
static void test_alloc_a()
{
/* slots 2 - 3 */
test_alloc_a(GPRS_RLCMAC_DL_TBF, 0x0c, 32*2);
test_alloc_a(GPRS_RLCMAC_UL_TBF, 0x0c, 14);
/* slots 1 - 5 */
test_alloc_a(GPRS_RLCMAC_DL_TBF, 0x1e, 32*4);
test_alloc_a(GPRS_RLCMAC_UL_TBF, 0x1e, 28);
}
static void dump_assignment(struct gprs_rlcmac_tbf *tbf, const char *dir)
{
for (size_t i = 0; i < ARRAY_SIZE(tbf->pdch); ++i)
if (tbf->pdch[i])
printf("PDCH[%d] is used for %s\n", i, dir);
printf("PDCH[%d] is control_ts for %s\n", tbf->control_ts, dir);
printf("PDCH[%d] is first common for %s\n", tbf->first_common_ts, dir);
}
static void test_alloc_b(int ms_class)
{
printf("Going to test multislot assignment MS_CLASS=%d\n", ms_class);
/*
* PDCH is on TS 6,7,8 and we start with a UL allocation and
* then follow two DL allocations (once single, once normal).
*
* Uplink assigned and still available..
*/
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
uint8_t trx_no;
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
printf("Testing UL then DL assignment.\n");
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
trx->pdch[5].enable();
trx->pdch[6].enable();
trx->pdch[7].enable();
ul_tbf = tbf_alloc_ul_tbf(bts, NULL, -1, ms_class, 0, 1);
OSMO_ASSERT(ul_tbf);
OSMO_ASSERT(ul_tbf->ms());
OSMO_ASSERT(ul_tbf->ms()->current_trx());
trx_no = ul_tbf->ms()->current_trx()->trx_no;
dump_assignment(ul_tbf, "UL");
/* assume final ack has not been sent */
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), trx_no, ms_class, 0, 0);
OSMO_ASSERT(dl_tbf);
dump_assignment(dl_tbf, "DL");
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
check_tfi_usage(&the_bts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
/**
* Test with the other order.. first DL and then UL
*/
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
uint8_t trx_no;
gprs_rlcmac_ul_tbf *ul_tbf;
gprs_rlcmac_dl_tbf *dl_tbf;
printf("Testing DL then UL assignment followed by update\n");
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
trx->pdch[5].enable();
trx->pdch[6].enable();
trx->pdch[7].enable();
dl_tbf = tbf_alloc_dl_tbf(bts, NULL, -1, ms_class, 0, 1);
dl_tbf->update_ms(0x23, GPRS_RLCMAC_DL_TBF);
OSMO_ASSERT(dl_tbf);
OSMO_ASSERT(dl_tbf->ms());
OSMO_ASSERT(dl_tbf->ms()->current_trx());
trx_no = dl_tbf->ms()->current_trx()->trx_no;
dump_assignment(dl_tbf, "DL");
ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf->ms(), trx_no, ms_class, 0, 0);
ul_tbf->update_ms(0x23, GPRS_RLCMAC_UL_TBF);
ul_tbf->m_contention_resolution_done = 1;
OSMO_ASSERT(ul_tbf);
dump_assignment(ul_tbf, "UL");
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
/* now update the dl_tbf */
dl_tbf->update();
dump_assignment(dl_tbf, "DL");
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
check_tfi_usage(&the_bts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
/* Andreas osmocom-pcu example */
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
int tfi;
uint8_t trx_no;
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
printf("Testing jolly example\n");
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
trx->pdch[1].enable();
trx->pdch[2].enable();
trx->pdch[3].enable();
trx->pdch[4].enable();
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
OSMO_ASSERT(tfi >= 0);
ul_tbf = tbf_alloc_ul_tbf(bts, NULL, .1, ms_class, 0, 0);
OSMO_ASSERT(ul_tbf);
OSMO_ASSERT(ul_tbf->ms());
OSMO_ASSERT(ul_tbf->ms()->current_trx());
trx_no = ul_tbf->ms()->current_trx()->trx_no;
dump_assignment(ul_tbf, "UL");
/* assume final ack has not been sent */
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), trx_no, ms_class, 0, 0);
OSMO_ASSERT(dl_tbf);
dump_assignment(dl_tbf, "DL");
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
check_tfi_usage(&the_bts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
}
#define ENABLE_PDCH(ts_no, enable_flag, trx) \
if (enable_flag) \
trx->pdch[ts_no].enable();
static void test_alloc_b(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7, int ms_class)
{
/* we can test the allocation failures differently */
if (!ts0 && !ts1 && !ts2 && !ts3 && !ts4 && !ts5 && !ts6 && !ts7)
return;
printf("Mass test: TS0(%c%c%c%c%c%c%c%c)TS7 MS_Class=%d\n",
ts0 ? 'O' : 'x',
ts1 ? 'O' : 'x',
ts2 ? 'O' : 'x',
ts3 ? 'O' : 'x',
ts4 ? 'O' : 'x',
ts5 ? 'O' : 'x',
ts6 ? 'O' : 'x',
ts7 ? 'O' : 'x', ms_class);
fflush(stdout);
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
uint8_t trx_no;
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
ENABLE_PDCH(0, ts0, trx);
ENABLE_PDCH(1, ts1, trx);
ENABLE_PDCH(2, ts2, trx);
ENABLE_PDCH(3, ts3, trx);
ENABLE_PDCH(4, ts4, trx);
ENABLE_PDCH(5, ts5, trx);
ENABLE_PDCH(6, ts6, trx);
ENABLE_PDCH(7, ts7, trx);
ul_tbf = tbf_alloc_ul_tbf(bts, NULL, -1, ms_class, 0, 1);
OSMO_ASSERT(ul_tbf->ms());
OSMO_ASSERT(ul_tbf->ms()->current_trx());
trx_no = ul_tbf->ms()->current_trx()->trx_no;
OSMO_ASSERT(ul_tbf);
/* assume final ack has not been sent */
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), trx_no, ms_class, 0, 0);
OSMO_ASSERT(dl_tbf);
/* verify that both are on the same ts */
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
check_tfi_usage(&the_bts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
/**
* Test with the other order.. first DL and then UL
*/
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
uint8_t trx_no;
gprs_rlcmac_ul_tbf *ul_tbf;
gprs_rlcmac_dl_tbf *dl_tbf;
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
ENABLE_PDCH(0, ts0, trx);
ENABLE_PDCH(1, ts1, trx);
ENABLE_PDCH(2, ts2, trx);
ENABLE_PDCH(3, ts3, trx);
ENABLE_PDCH(4, ts4, trx);
ENABLE_PDCH(5, ts5, trx);
ENABLE_PDCH(6, ts6, trx);
ENABLE_PDCH(7, ts7, trx);
dl_tbf = tbf_alloc_dl_tbf(bts, NULL, -1, ms_class, 0, 1);
OSMO_ASSERT(dl_tbf);
OSMO_ASSERT(dl_tbf->ms());
OSMO_ASSERT(dl_tbf->ms()->current_trx());
trx_no = dl_tbf->ms()->current_trx()->trx_no;
dl_tbf->update_ms(0x23, GPRS_RLCMAC_DL_TBF);
ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf->ms(), trx_no, ms_class, 0, 0);
OSMO_ASSERT(ul_tbf);
ul_tbf->update_ms(0x23, GPRS_RLCMAC_UL_TBF);
ul_tbf->m_contention_resolution_done = 1;
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
/* now update the dl_tbf */
dl_tbf->update();
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
OSMO_ASSERT(ul_tbf->ms_class() == ms_class);
OSMO_ASSERT(dl_tbf->ms_class() == ms_class);
check_tfi_usage(&the_bts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
}
static void test_all_alloc_b()
{
/* it is a bit crazy... */
for (uint8_t ts0 = 0; ts0 < 2; ++ts0)
for (uint8_t ts1 = 0; ts1 < 2; ++ts1)
for (uint8_t ts2 = 0; ts2 < 2; ++ts2)
for (uint8_t ts3 = 0; ts3 < 2; ++ts3)
for (uint8_t ts4 = 0; ts4 < 2; ++ts4)
for (uint8_t ts5 = 0; ts5 < 2; ++ts5)
for (uint8_t ts6 = 0; ts6 < 2; ++ts6)
for (uint8_t ts7 = 0; ts7 < 2; ++ts7)
for (int ms_class = 0; ms_class < 30; ++ms_class)
test_alloc_b(ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7, ms_class);
}
static void test_alloc_b()
{
for (int i = 0; i < 30; ++i)
test_alloc_b(i);
test_all_alloc_b();
}
typedef int (*algo_t)(struct gprs_rlcmac_bts *bts,
struct GprsMs *ms,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
int use_trx);
static char get_dir_char(uint8_t mask, uint8_t tx, uint8_t rx, uint8_t busy)
{
int offs = busy ? 32 : 0;
return (mask & tx & rx) ? 'C' + offs :
(mask & tx) ? 'U' + offs :
(mask & rx) ? 'D' + offs :
'.';
}
enum test_mode {
TEST_MODE_UL_ONLY,
TEST_MODE_DL_ONLY,
TEST_MODE_UL_AND_DL,
TEST_MODE_DL_AND_UL,
TEST_MODE_DL_AFTER_UL,
TEST_MODE_UL_AFTER_DL,
};
static GprsMs *alloc_tbfs(BTS *the_bts, GprsMs *ms, unsigned ms_class,
enum test_mode mode)
{
struct gprs_rlcmac_bts *bts;
uint8_t trx_no = -1;
bts = the_bts->bts_data();
gprs_rlcmac_tbf *tbf = NULL;
if (ms && ms->current_trx())
trx_no = ms->current_trx()->trx_no;
GprsMs::Guard guard1(ms);
/* Allocate what is needed first */
switch (mode) {
case TEST_MODE_UL_ONLY:
case TEST_MODE_DL_AFTER_UL:
case TEST_MODE_UL_AND_DL:
if (ms && ms->ul_tbf())
tbf_free(ms->ul_tbf());
tbf = tbf_alloc_ul_tbf(bts, ms, trx_no, ms_class, 0, 0);
if (tbf == NULL)
return NULL;
break;
case TEST_MODE_DL_ONLY:
case TEST_MODE_UL_AFTER_DL:
case TEST_MODE_DL_AND_UL:
if (ms && ms->dl_tbf())
tbf_free(ms->dl_tbf());
tbf = tbf_alloc_dl_tbf(bts, ms, trx_no, ms_class, 0, 0);
if (tbf == NULL)
return NULL;
}
OSMO_ASSERT(tbf);
OSMO_ASSERT(tbf->ms());
OSMO_ASSERT(ms == NULL || ms == tbf->ms());
ms = tbf->ms();
GprsMs::Guard guard2(ms);
/* Continue with what is needed next */
switch (mode) {
case TEST_MODE_UL_ONLY:
case TEST_MODE_DL_ONLY:
/* We are done */
break;
case TEST_MODE_DL_AFTER_UL:
case TEST_MODE_UL_AND_DL:
ms = alloc_tbfs(the_bts, ms, ms_class, TEST_MODE_DL_ONLY);
break;
case TEST_MODE_UL_AFTER_DL:
case TEST_MODE_DL_AND_UL:
ms = alloc_tbfs(the_bts, ms, ms_class, TEST_MODE_UL_ONLY);
break;
}
/* Optionally delete the TBF */
switch (mode) {
case TEST_MODE_DL_AFTER_UL:
case TEST_MODE_UL_AFTER_DL:
tbf_free(tbf);
tbf = NULL;
break;
default:
break;
}
if (!ms && tbf)
tbf_free(tbf);
return guard2.is_idle() ? NULL : ms;
}
static unsigned alloc_many_tbfs(BTS *the_bts, unsigned min_class,
unsigned max_class, enum test_mode mode)
{
unsigned counter;
unsigned ms_class = min_class;
for (counter = 0; 1; counter += 1) {
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
uint8_t ul_slots = 0;
uint8_t dl_slots = 0;
uint8_t busy_slots = 0;
unsigned i;
int tfi = -1;
int tfi2;
uint8_t trx_no2;
struct gprs_rlcmac_trx *trx;
GprsMs *ms;
enum gprs_rlcmac_tbf_direction dir;
uint32_t tlli = counter + 0xc0000000;
ms = the_bts->ms_by_tlli(tlli);
ms = alloc_tbfs(the_bts, ms, ms_class, mode);
if (!ms)
break;
ms->set_tlli(tlli);
ul_tbf = ms->ul_tbf();
dl_tbf = ms->dl_tbf();
trx = ms->current_trx();
OSMO_ASSERT(ul_tbf || dl_tbf);
if (ul_tbf) {
ul_slots = 1 << ul_tbf->first_common_ts;
tfi = ul_tbf->tfi();
dir = GPRS_RLCMAC_UL_TBF;
} else {
ul_slots = 1 << dl_tbf->first_common_ts;
tfi = dl_tbf->tfi();
dir = GPRS_RLCMAC_DL_TBF;
}
for (i = 0; dl_tbf && i < ARRAY_SIZE(dl_tbf->pdch); i += 1)
if (dl_tbf->pdch[i])
dl_slots |= 1 << i;
for (i = 0; trx && i < ARRAY_SIZE(trx->pdch); i += 1) {
struct gprs_rlcmac_pdch *pdch = &trx->pdch[i];
if (ul_tbf && dl_tbf)
continue;
if (ul_tbf &&
pdch->assigned_tfi(GPRS_RLCMAC_DL_TBF) != 0xffffffff)
continue;
if (dl_tbf &&
pdch->assigned_tfi(GPRS_RLCMAC_UL_TBF) != 0xffffffff)
continue;
busy_slots |= 1 << i;
}
printf(" TBF[%d] class %d reserves %c%c%c%c%c%c%c%c\n",
tfi, ms_class,
get_dir_char(0x01, ul_slots, dl_slots, busy_slots),
get_dir_char(0x02, ul_slots, dl_slots, busy_slots),
get_dir_char(0x04, ul_slots, dl_slots, busy_slots),
get_dir_char(0x08, ul_slots, dl_slots, busy_slots),
get_dir_char(0x10, ul_slots, dl_slots, busy_slots),
get_dir_char(0x20, ul_slots, dl_slots, busy_slots),
get_dir_char(0x40, ul_slots, dl_slots, busy_slots),
get_dir_char(0x80, ul_slots, dl_slots, busy_slots));
if (tfi >= 0) {
OSMO_ASSERT(ms->current_trx());
tfi2 = the_bts->tfi_find_free(dir, &trx_no2,
ms->current_trx()->trx_no);
OSMO_ASSERT(tfi != tfi2);
OSMO_ASSERT(tfi2 < 0 ||
trx_no2 == ms->current_trx()->trx_no);
}
ms_class += 1;
if (ms_class > max_class)
ms_class = min_class;
}
return counter;
}
static void test_successive_allocation(algo_t algo, unsigned min_class,
unsigned max_class, enum test_mode mode,
unsigned expect_num, const char *text)
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
unsigned counter;
printf("Going to test assignment with many TBF, %s\n", text);
bts = the_bts.bts_data();
bts->alloc_algorithm = algo;
trx = &bts->trx[0];
trx->pdch[3].enable();
trx->pdch[4].enable();
trx->pdch[5].enable();
trx->pdch[6].enable();
trx->pdch[7].enable();
counter = alloc_many_tbfs(&the_bts, min_class, max_class, mode);
printf(" Successfully allocated %d UL TBFs\n", counter);
if (counter != expect_num)
fprintf(stderr, " Expected %d TBFs for %s\n", expect_num, text);
OSMO_ASSERT(counter == expect_num);
check_tfi_usage(&the_bts);
}
static void test_many_connections(algo_t algo, unsigned expect_num,
const char *text)
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
int counter1, counter2 = -1;
unsigned i;
enum test_mode mode_seq[] = {
TEST_MODE_DL_AFTER_UL,
TEST_MODE_UL_ONLY,
TEST_MODE_DL_AFTER_UL,
TEST_MODE_DL_ONLY,
};
printf("Going to test assignment with many connections, %s\n", text);
bts = the_bts.bts_data();
bts->alloc_algorithm = algo;
trx = &bts->trx[0];
trx->pdch[3].enable();
trx->pdch[4].enable();
trx->pdch[5].enable();
trx->pdch[6].enable();
trx->pdch[7].enable();
for (i = 0; i < ARRAY_SIZE(mode_seq); i += 1) {
counter1 = alloc_many_tbfs(&the_bts, 1, 29, mode_seq[i]);
fprintf(stderr, " Allocated %d TBFs (previously %d)\n",
counter1, counter2);
check_tfi_usage(&the_bts);
/* This will stop earlier due to USF shortage */
if (mode_seq[i] == TEST_MODE_UL_ONLY)
continue;
if (counter2 >= 0) {
if (counter1 < counter2)
fprintf(stderr, " Expected %d >= %d in %s\n",
counter1, counter2, text);
OSMO_ASSERT(counter1 >= counter2);
}
counter2 = counter1;
}
printf(" Successfully allocated %d TBFs\n", counter1);
if (counter1 != (int)expect_num)
fprintf(stderr, " Expected %d TBFs for %s\n", expect_num, text);
OSMO_ASSERT(expect_num == (unsigned)counter1);
}
static void test_successive_allocation()
{
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_UL_AND_DL,
35, "algorithm A (UL and DL)");
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_UL_AND_DL,
32, "algorithm B class 10 (UL and DL)");
test_successive_allocation(alloc_algorithm_b, 12, 12, TEST_MODE_UL_AND_DL,
32, "algorithm B class 12 (UL and DL)");
test_successive_allocation(alloc_algorithm_b, 1, 12, TEST_MODE_UL_AND_DL,
32, "algorithm B class 1-12 (UL and DL)");
test_successive_allocation(alloc_algorithm_b, 1, 29, TEST_MODE_UL_AND_DL,
32, "algorithm B class 1-29 (UL and DL)");
test_successive_allocation(alloc_algorithm_dynamic, 1, 29, TEST_MODE_UL_AND_DL,
35, "algorithm dynamic class 1-29 (UL and DL)");
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_DL_AND_UL,
35, "algorithm A (DL and UL)");
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_DL_AND_UL,
32, "algorithm B class 10 (DL and UL)");
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_DL_AND_UL,
32, "algorithm dynamic class 10 (DL and UL)");
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_DL_AFTER_UL,
160, "algorithm A (DL after UL)");
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_DL_AFTER_UL,
32, "algorithm B class 10 (DL after UL)");
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_DL_AFTER_UL,
95, "algorithm dynamic class 10 (DL after UL)");
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_UL_AFTER_DL,
35, "algorithm A (UL after DL)");
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_UL_AFTER_DL,
32, "algorithm B class 10 (UL after DL)");
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_UL_AFTER_DL,
35, "algorithm dynamic class 10 (UL after DL)");
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_UL_ONLY,
35, "algorithm A (UL only)");
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_UL_ONLY,
32, "algorithm B class 10 (UL only)");
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_UL_ONLY,
35, "algorithm dynamic class 10 (UL only)");
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_DL_ONLY,
160, "algorithm A (DL ONLY)");
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_DL_ONLY,
32, "algorithm B class 10 (DL ONLY)");
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_DL_ONLY,
101, "algorithm dynamic class 10 (DL ONLY)");
}
static void test_many_connections()
{
test_many_connections(alloc_algorithm_a, 160, "algorithm A");
test_many_connections(alloc_algorithm_b, 32, "algorithm B");
test_many_connections(alloc_algorithm_dynamic, 160, "algorithm dynamic");
}
int main(int argc, char **argv)
{
tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile AllocTest context");
if (!tall_pcu_ctx)
abort();
msgb_set_talloc_ctx(tall_pcu_ctx);
osmo_init_logging(&gprs_log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
if (getenv("LOGL_DEBUG"))
log_set_log_level(osmo_stderr_target, LOGL_DEBUG);
test_alloc_a();
test_alloc_b();
test_successive_allocation();
test_many_connections();
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(); }
}

894
tests/alloc/AllocTest.err Normal file
View File

@ -0,0 +1,894 @@
No TFI available.
No TFI available.
- Failed to find a usable TRX (TFI exhausted)
- Failed to allocate a TS, no USF available
No TFI available.
No TFI available.
No TFI available.
No TFI available.
- Failed to find a usable TRX (TFI exhausted)
- Failed to allocate a TS, no USF available
- Failed to allocate a TS, no USF available
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
No TFI available.
- Failed to allocate a TS, no USF available
- Failed to allocate a TS, no USF available
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
- Failed to allocate a TS, no USF available
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
- Failed to find a usable TRX (TFI exhausted)
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
- Failed to find a usable TRX (TFI exhausted)
- Failed to allocate a TS, no USF available
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
- Failed to allocate a TS, no USF available
- Failed to allocate a TS, no USF available
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
- Failed to allocate a TS, no USF available
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
- Failed to find a usable TRX (TFI exhausted)
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
- Failed to find a usable TRX (TFI exhausted)
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
- Failed to find a usable TRX (TFI exhausted)
Allocated 160 TBFs (previously -1)
- Failed to allocate a TS, no USF available
Allocated 35 TBFs (previously 160)
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
- Failed to find a usable TRX (TFI exhausted)
Allocated 160 TBFs (previously 160)
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
- Failed to find a usable TRX (TFI exhausted)
Allocated 160 TBFs (previously 160)
No TFI available.
No TFI available.
- Failed to allocate a TFI
Allocated 32 TBFs (previously -1)
No USF available
Allocated 24 TBFs (previously 32)
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
- Failed to allocate a TFI
Allocated 32 TBFs (previously 32)
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
- Failed to allocate a TFI
Allocated 32 TBFs (previously 32)
No TFI available.
No TFI available.
- Failed to allocate a TFI
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
- Failed to find a usable TRX (TFI exhausted)
Allocated 97 TBFs (previously -1)
- Failed to allocate a TS, no USF available
Allocated 24 TBFs (previously 97)
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
- Failed to find a usable TRX (TFI exhausted)
Allocated 160 TBFs (previously 97)
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
No TFI available.
- Failed to find a usable TRX (TFI exhausted)
Allocated 160 TBFs (previously 160)

10795
tests/alloc/AllocTest.ok Normal file

File diff suppressed because it is too large Load Diff

147
tests/codel/codel_test.c Normal file
View File

@ -0,0 +1,147 @@
/* Test routines for the CoDel implementation
*
* (C) 2015 by sysmocom s.f.m.c. GmbH
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*/
#undef _GNU_SOURCE
#define _GNU_SOURCE
#if 0
#include <osmocom/core/talloc.h>
#include <osmocom/core/prim.h>
#endif
#include <osmocom/core/application.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include "gprs_codel.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
static int do_codel_control(struct gprs_codel *state, const struct timeval *recv,
struct timeval *now, const struct timeval *delta_now, int count)
{
int drop;
drop = gprs_codel_control(state, recv, now, -1);
if (drop) {
printf("Dropping packet %d, "
"recv = %d.%03d, now = %d.%03d, "
"codel.count = %d\n",
count,
(int)recv->tv_sec, (int)recv->tv_usec/1000,
(int)now->tv_sec, (int)now->tv_usec/1000,
state->count);
} else {
timeradd(now, delta_now, now);
}
return drop == 0 ? 0 : 1;
}
static void test_codel(void)
{
struct gprs_codel codel;
struct timeval now;
struct timeval recv;
const struct timeval delta_now = {0, 10000};
const struct timeval init_delta_recv = {0, 5000};
struct timeval delta_recv;
unsigned count;
unsigned sum = 0;
unsigned dropped = 0;
int drop;
printf("----- %s START\n", __func__);
gprs_codel_init(&codel);
gprs_codel_set_interval(&codel, 100);
timerclear(&now);
timerclear(&recv);
delta_recv = init_delta_recv;
for (count = 0; count < 20; count++, sum++) {
drop = do_codel_control(&codel, &recv, &now, &delta_now, sum);
timeradd(&recv, &delta_recv, &recv);
dropped += drop;
}
printf("Dropped %d packets\n", dropped);
OSMO_ASSERT(dropped == 0);
OSMO_ASSERT(!codel.dropping);
for (count = 0; count < 20; count++, sum++) {
drop = do_codel_control(&codel, &recv, &now, &delta_now, sum);
timeradd(&recv, &delta_recv, &recv);
dropped += drop;
}
OSMO_ASSERT(dropped == 2);
OSMO_ASSERT(codel.dropping);
/* slow down recv rate */
delta_recv.tv_usec = delta_now.tv_usec;
for (count = 0; count < 75; count++, sum++) {
drop = do_codel_control(&codel, &recv, &now, &delta_now, sum);
timeradd(&recv, &delta_recv, &recv);
dropped += drop;
}
OSMO_ASSERT(dropped == 20);
OSMO_ASSERT(codel.dropping);
for (count = 0; count < 50; count++, sum++) {
drop = do_codel_control(&codel, &recv, &now, &delta_now, sum);
timeradd(&recv, &delta_recv, &recv);
dropped += drop;
}
OSMO_ASSERT(dropped == 20);
OSMO_ASSERT(!codel.dropping);
OSMO_ASSERT(codel.count >= 20);
/* go back to old data rate */
delta_recv = init_delta_recv;
for (count = 0; count < 20; count++, sum++) {
drop = do_codel_control(&codel, &recv, &now, &delta_now, sum);
timeradd(&recv, &delta_recv, &recv);
dropped += drop;
}
OSMO_ASSERT(dropped == 20);
OSMO_ASSERT(!codel.dropping);
for (count = 0; count < 20; count++, sum++) {
drop = do_codel_control(&codel, &recv, &now, &delta_now, sum);
timeradd(&recv, &delta_recv, &recv);
dropped += drop;
}
OSMO_ASSERT(dropped == 22);
OSMO_ASSERT(codel.count >= 2);
printf("Dropped %d packets\n", dropped);
printf("----- %s END\n", __func__);
}
static struct log_info info = {};
int main(int argc, char **argv)
{
osmo_init_logging(&info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
log_set_log_level(osmo_stderr_target, LOGL_INFO);
printf("===== CoDel test START\n");
test_codel();
printf("===== CoDel test END\n\n");
exit(EXIT_SUCCESS);
}

29
tests/codel/codel_test.ok Normal file
View File

@ -0,0 +1,29 @@
===== CoDel test START
----- test_codel START
Dropped 0 packets
Dropping packet 21, recv = 0.105, now = 0.210, codel.count = 1
Dropping packet 32, recv = 0.160, now = 0.310, codel.count = 2
Dropping packet 41, recv = 0.210, now = 0.390, codel.count = 3
Dropping packet 47, recv = 0.270, now = 0.440, codel.count = 4
Dropping packet 53, recv = 0.330, now = 0.490, codel.count = 5
Dropping packet 59, recv = 0.390, now = 0.540, codel.count = 6
Dropping packet 64, recv = 0.440, now = 0.580, codel.count = 7
Dropping packet 69, recv = 0.490, now = 0.620, codel.count = 8
Dropping packet 73, recv = 0.530, now = 0.650, codel.count = 9
Dropping packet 77, recv = 0.570, now = 0.680, codel.count = 10
Dropping packet 81, recv = 0.610, now = 0.710, codel.count = 11
Dropping packet 86, recv = 0.660, now = 0.750, codel.count = 12
Dropping packet 89, recv = 0.690, now = 0.770, codel.count = 13
Dropping packet 93, recv = 0.730, now = 0.800, codel.count = 14
Dropping packet 97, recv = 0.770, now = 0.830, codel.count = 15
Dropping packet 100, recv = 0.800, now = 0.850, codel.count = 16
Dropping packet 104, recv = 0.840, now = 0.880, codel.count = 17
Dropping packet 107, recv = 0.870, now = 0.900, codel.count = 18
Dropping packet 111, recv = 0.910, now = 0.930, codel.count = 19
Dropping packet 114, recv = 0.940, now = 0.950, codel.count = 20
Dropping packet 186, recv = 1.555, now = 1.660, codel.count = 1
Dropping packet 197, recv = 1.610, now = 1.760, codel.count = 2
Dropped 22 packets
----- test_codel END
===== CoDel test END

1139
tests/edge/EdgeTest.cpp Normal file

File diff suppressed because it is too large Load Diff

8
tests/edge/EdgeTest.ok Normal file
View File

@ -0,0 +1,8 @@
=== start test_coding_scheme ===
=== end test_coding_scheme ===
=== start test_rlc_info_init ===
=== end test_rlc_info_init ===
=== start test_rlc_unit_decoder ===
=== end test_rlc_unit_decoder ===
=== start test_rlc_unit_encoder ===
=== end test_rlc_unit_encoder ===

65
tests/emu/gprs_tests.h Normal file
View File

@ -0,0 +1,65 @@
/* (C) 2013 by Holger Hans Peter Freyther
*
* 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 tests_h
#define tests_h
#ifdef __cplusplus
extern "C" {
#endif
#include <osmocom/core/msgb.h>
#include <string.h>
struct gprs_bssgp_pcu;
struct tlv_parsed;
struct msgb;
struct gprs_test {
gprs_test(const char *name, const char *description,
void (*start)(struct gprs_bssgp_pcu *),
void (*data) (struct gprs_bssgp_pcu *, struct msgb *, struct tlv_parsed *parsed))
: name(name)
, description(description)
, start(start)
, data(data)
{}
const char *name;
const char *description;
void (*start)(struct gprs_bssgp_pcu *);
void (*data) (struct gprs_bssgp_pcu *, struct msgb *, struct tlv_parsed *);
};
void gprs_test_success(struct gprs_bssgp_pcu *);
static inline struct msgb *create_msg(const uint8_t *data, size_t len)
{
struct msgb *msg = msgb_alloc_headroom(4096, 128, "create msg");
msg->l3h = msgb_put(msg, len);
memcpy(msg->l3h, data, len);
return msg;
}
#ifdef __cplusplus
}
#endif
#endif

234
tests/emu/openbsc_clone.c Normal file
View File

@ -0,0 +1,234 @@
/* (C) 2009-2010 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 Affero 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 "openbsc_clone.h"
#include <gprs_debug.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/tlv.h>
#include <errno.h>
/* Section 6.4 Commands and Responses */
enum gprs_llc_u_cmd {
GPRS_LLC_U_DM_RESP = 0x01,
GPRS_LLC_U_DISC_CMD = 0x04,
GPRS_LLC_U_UA_RESP = 0x06,
GPRS_LLC_U_SABM_CMD = 0x07,
GPRS_LLC_U_FRMR_RESP = 0x08,
GPRS_LLC_U_XID = 0x0b,
GPRS_LLC_U_NULL_CMD = 0x00,
};
#define LLC_ALLOC_SIZE 16384
#define UI_HDR_LEN 3
#define N202 4
#define CRC24_LENGTH 3
static const struct value_string llc_cmd_strs[] = {
{ GPRS_LLC_NULL, "NULL" },
{ GPRS_LLC_RR, "RR" },
{ GPRS_LLC_ACK, "ACK" },
{ GPRS_LLC_RNR, "RNR" },
{ GPRS_LLC_SACK, "SACK" },
{ GPRS_LLC_DM, "DM" },
{ GPRS_LLC_DISC, "DISC" },
{ GPRS_LLC_UA, "UA" },
{ GPRS_LLC_SABM, "SABM" },
{ GPRS_LLC_FRMR, "FRMR" },
{ GPRS_LLC_XID, "XID" },
{ GPRS_LLC_UI, "UI" },
{ 0, NULL }
};
int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp,
const uint8_t *llc_hdr, int len)
{
const uint8_t *ctrl = llc_hdr+1;
if (len <= CRC24_LENGTH)
return -EIO;
ghp->crc_length = len - CRC24_LENGTH;
ghp->ack_req = 0;
/* Section 5.5: FCS */
ghp->fcs = *(llc_hdr + len - 3);
ghp->fcs |= *(llc_hdr + len - 2) << 8;
ghp->fcs |= *(llc_hdr + len - 1) << 16;
/* Section 6.2.1: invalid PD field */
if (llc_hdr[0] & 0x80)
return -EIO;
/* This only works for the MS->SGSN direction */
if (llc_hdr[0] & 0x40)
ghp->is_cmd = 0;
else
ghp->is_cmd = 1;
ghp->sapi = llc_hdr[0] & 0xf;
/* Section 6.2.3: check for reserved SAPI */
switch (ghp->sapi) {
case 0:
case 4:
case 6:
case 0xa:
case 0xc:
case 0xd:
case 0xf:
return -EINVAL;
}
if ((ctrl[0] & 0x80) == 0) {
/* I (Information transfer + Supervisory) format */
uint8_t k;
ghp->data = ctrl + 3;
if (ctrl[0] & 0x40)
ghp->ack_req = 1;
ghp->seq_tx = (ctrl[0] & 0x1f) << 4;
ghp->seq_tx |= (ctrl[1] >> 4);
ghp->seq_rx = (ctrl[1] & 0x7) << 6;
ghp->seq_rx |= (ctrl[2] >> 2);
switch (ctrl[2] & 0x03) {
case 0:
ghp->cmd = GPRS_LLC_RR;
break;
case 1:
ghp->cmd = GPRS_LLC_ACK;
break;
case 2:
ghp->cmd = GPRS_LLC_RNR;
break;
case 3:
ghp->cmd = GPRS_LLC_SACK;
k = ctrl[3] & 0x1f;
ghp->data += 1 + k;
break;
}
ghp->data_len = (llc_hdr + len - 3) - ghp->data;
} else if ((ctrl[0] & 0xc0) == 0x80) {
/* S (Supervisory) format */
ghp->data = NULL;
ghp->data_len = 0;
if (ctrl[0] & 0x20)
ghp->ack_req = 1;
ghp->seq_rx = (ctrl[0] & 0x7) << 6;
ghp->seq_rx |= (ctrl[1] >> 2);
switch (ctrl[1] & 0x03) {
case 0:
ghp->cmd = GPRS_LLC_RR;
break;
case 1:
ghp->cmd = GPRS_LLC_ACK;
break;
case 2:
ghp->cmd = GPRS_LLC_RNR;
break;
case 3:
ghp->cmd = GPRS_LLC_SACK;
break;
}
} else if ((ctrl[0] & 0xe0) == 0xc0) {
/* UI (Unconfirmed Inforamtion) format */
ghp->cmd = GPRS_LLC_UI;
ghp->data = ctrl + 2;
ghp->data_len = (llc_hdr + len - 3) - ghp->data;
ghp->seq_tx = (ctrl[0] & 0x7) << 6;
ghp->seq_tx |= (ctrl[1] >> 2);
if (ctrl[1] & 0x02) {
ghp->is_encrypted = 1;
/* FIXME: encryption */
}
if (ctrl[1] & 0x01) {
/* FCS over hdr + all inf fields */
} else {
/* FCS over hdr + N202 octets (4) */
if (ghp->crc_length > UI_HDR_LEN + N202)
ghp->crc_length = UI_HDR_LEN + N202;
}
} else {
/* U (Unnumbered) format: 1 1 1 P/F M4 M3 M2 M1 */
ghp->data = NULL;
ghp->data_len = 0;
switch (ctrl[0] & 0xf) {
case GPRS_LLC_U_NULL_CMD:
ghp->cmd = GPRS_LLC_NULL;
break;
case GPRS_LLC_U_DM_RESP:
ghp->cmd = GPRS_LLC_DM;
break;
case GPRS_LLC_U_DISC_CMD:
ghp->cmd = GPRS_LLC_DISC;
break;
case GPRS_LLC_U_UA_RESP:
ghp->cmd = GPRS_LLC_UA;
break;
case GPRS_LLC_U_SABM_CMD:
ghp->cmd = GPRS_LLC_SABM;
break;
case GPRS_LLC_U_FRMR_RESP:
ghp->cmd = GPRS_LLC_FRMR;
break;
case GPRS_LLC_U_XID:
ghp->cmd = GPRS_LLC_XID;
ghp->data = ctrl + 1;
ghp->data_len = (llc_hdr + len - 3) - ghp->data;
break;
default:
return -EIO;
}
}
/* FIXME: parse sack frame */
if (ghp->cmd == GPRS_LLC_SACK) {
LOGP(DPCU, LOGL_NOTICE, "Unsupported SACK frame\n");
return -EIO;
}
return 0;
}
const struct tlv_definition gsm48_gmm_att_tlvdef = {
.def = {
[GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_FIXED, 1 },
[GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 },
[GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 },
[GSM48_IE_GMM_AUTH_RAND] = { TLV_TYPE_FIXED, 16 },
[GSM48_IE_GMM_AUTH_SRES] = { TLV_TYPE_FIXED, 4 },
[GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 },
[GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 },
},
};

96
tests/emu/openbsc_clone.h Normal file
View File

@ -0,0 +1,96 @@
/* (C) 2009-2010 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 Affero 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 OPENBSC_CLONE_H
#define OPENBSC_CLONE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <stdint.h>
enum gprs_llc_cmd {
GPRS_LLC_NULL,
GPRS_LLC_RR,
GPRS_LLC_ACK,
GPRS_LLC_RNR,
GPRS_LLC_SACK,
GPRS_LLC_DM,
GPRS_LLC_DISC,
GPRS_LLC_UA,
GPRS_LLC_SABM,
GPRS_LLC_FRMR,
GPRS_LLC_XID,
GPRS_LLC_UI,
};
struct gprs_llc_hdr_parsed {
uint8_t sapi;
uint8_t is_cmd:1,
ack_req:1,
is_encrypted:1;
uint32_t seq_rx;
uint32_t seq_tx;
uint32_t fcs;
uint32_t fcs_calc;
const uint8_t *data;
uint16_t data_len;
uint16_t crc_length;
enum gprs_llc_cmd cmd;
};
int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp, const uint8_t *llc_hdr, int len);
/* Table 10.4 / 10.4a, GPRS Mobility Management (GMM) */
#define GSM48_MT_GMM_ATTACH_ACK 0x02
/* Chapter 9.4.2 / Table 9.4.2 */
struct gsm48_attach_ack {
uint8_t att_result:4, /* 10.5.5.7 */
force_stby:4; /* 10.5.5.1 */
uint8_t ra_upd_timer; /* 10.5.7.3 */
uint8_t radio_prio; /* 10.5.7.2 */
struct gsm48_ra_id ra_id; /* 10.5.5.15 */
uint8_t data[0];
} __attribute__((packed));
enum gsm48_gprs_ie_mm {
GSM48_IE_GMM_CIPH_CKSN = 0x08, /* 10.5.1.2 */
GSM48_IE_GMM_TIMER_READY = 0x17, /* 10.5.7.3 */
GSM48_IE_GMM_ALLOC_PTMSI = 0x18, /* 10.5.1.4 */
GSM48_IE_GMM_PTMSI_SIG = 0x19, /* 10.5.5.8 */
GSM48_IE_GMM_AUTH_RAND = 0x21, /* 10.5.3.1 */
GSM48_IE_GMM_AUTH_SRES = 0x22, /* 10.5.3.2 */
GSM48_IE_GMM_IMEISV = 0x23, /* 10.5.1.4 */
GSM48_IE_GMM_DRX_PARAM = 0x27, /* 10.5.5.6 */
GSM48_IE_GMM_MS_NET_CAPA = 0x31, /* 10.5.5.12 */
GSM48_IE_GMM_PDP_CTX_STATUS = 0x32, /* 10.5.7.1 */
GSM48_IE_GMM_PS_LCS_CAPA = 0x33, /* 10.5.5.22 */
GSM48_IE_GMM_GMM_MBMS_CTX_ST = 0x35, /* 10.5.7.6 */
};
extern const struct tlv_definition gsm48_gmm_att_tlvdef;
#ifdef __cplusplus
}
#endif
#endif

152
tests/emu/pcu_emu.cpp Normal file
View File

@ -0,0 +1,152 @@
/* Code for a software PCU to test a SGSN.. */
/* (C) 2013 by Holger Hans Peter Freyther
*
* 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/>.
*
*/
extern "C" {
#include <osmocom/core/talloc.h>
#include <pcu_vty.h>
}
#include "gprs_tests.h"
#include <gprs_bssgp_pcu.h>
#include <gprs_rlcmac.h>
#include <bts.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
static size_t current_test;
/* Extern data to please the underlying code */
void *tall_pcu_ctx;
int16_t spoof_mnc = 0, spoof_mcc = 0;
extern void test_replay_gprs_attach(struct gprs_bssgp_pcu *pcu);
extern void test_replay_gprs_data(struct gprs_bssgp_pcu *, struct msgb *, struct tlv_parsed *);
extern void test_pdp_activation_start(struct gprs_bssgp_pcu *pcu);
extern void test_pdp_activation_data(struct gprs_bssgp_pcu *, struct msgb *, struct tlv_parsed*);
struct gprs_test all_tests[] = {
gprs_test("gprs_attach_with_tmsi",
"A simple test that verifies that N(U) is "
"increasing across various messages. This makes "
"sure that no new LLE/LLME is created on the fly.",
test_replay_gprs_attach,
test_replay_gprs_data),
gprs_test("gprs_full_attach_pdp_activation",
"A simple test to do a GPRS attach and open a PDP "
"context. Then goes to sleep and waits for you to ping "
"the connection and hopefully re-produce a crash.",
test_pdp_activation_start,
test_pdp_activation_data),
};
static void init_main_bts()
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_interval = 100;
bts->initial_cs_dl = bts->initial_cs_ul = 1;
bts->cs1 = 1;
bts->t3142 = 20;
bts->t3169 = 5;
bts->t3191 = 5;
bts->t3193_msec = 100;
bts->t3195 = 5;
bts->n3101 = 10;
bts->n3103 = 4;
bts->n3105 = 8;
bts->alpha = 0; /* a = 0.0 */
if (!bts->alloc_algorithm)
bts->alloc_algorithm = alloc_algorithm_b;
}
static void bvci_unblocked(struct gprs_bssgp_pcu *pcu)
{
printf("BVCI unblocked. We can begin with test cases.\n");
all_tests[current_test].start(pcu);
}
static void bssgp_data(struct gprs_bssgp_pcu *pcu, struct msgb *msg, struct tlv_parsed *tp)
{
all_tests[current_test].data(pcu, msg, tp);
}
void create_and_connect_bssgp(struct gprs_rlcmac_bts *bts,
uint32_t sgsn_ip, uint16_t sgsn_port)
{
struct gprs_bssgp_pcu *pcu;
pcu = gprs_bssgp_create_and_connect(bts, 0, sgsn_ip, sgsn_port,
20, 20, 20, 0x901, 0x99, 1, 0, 0);
pcu->on_unblock_ack = bvci_unblocked;
pcu->on_dl_unit_data = bssgp_data;
}
int main(int argc, char **argv)
{
tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile Emu-PCU context");
if (!tall_pcu_ctx)
abort();
msgb_set_talloc_ctx(tall_pcu_ctx);
osmo_init_logging(&gprs_log_info);
vty_init(&pcu_vty_info);
pcu_vty_init(&gprs_log_info);
current_test = 0;
init_main_bts();
create_and_connect_bssgp(bts_main_data(), INADDR_LOOPBACK, 23000);
for (;;)
osmo_select_main(0);
return EXIT_SUCCESS;
}
/*
* Test handling..
*/
void gprs_test_success(struct gprs_bssgp_pcu *pcu)
{
current_test += 1;
if (current_test >= ARRAY_SIZE(all_tests)) {
printf("All tests executed.\n");
exit(EXIT_SUCCESS);
}
all_tests[current_test].start(pcu);
}
/*
* 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,169 @@
/* (C) 2013 by Holger Hans Peter Freyther
*
* 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/>.
*
*/
extern "C" {
#include <osmocom/core/msgb.h>
#include <osmocom/core/backtrace.h>
#include <osmocom/gsm/gsm_utils.h>
}
#include "openbsc_clone.h"
#include "gprs_tests.h"
#include <gprs_bssgp_pcu.h>
#include <stdint.h>
#include <string.h>
static const uint8_t attach[] = {
0x0e, 0x00, 0x26,
0x01, 0xc0, 0x01, 0x08, 0x01, 0x02, 0xe5, 0x80,
0x71, 0x0d, 0x01, 0x05, 0xf4, 0x02, 0x30, 0xef,
0x0e, 0x09, 0xf1, 0x07, 0x00, 0x01, 0x00, 0x0b,
0x34, 0xc7, 0x03, 0x2a, 0xa0, 0x42, 0x7c, 0xad,
0xe1, 0x18, 0x0b, 0xf8, 0xef, 0xfc
};
static const uint8_t id_resp_imei[] = {
0x0e, 0x00, 0x11,
0x01, 0xc0, 0x05, 0x08, 0x16, 0x08, 0x3a, 0x49,
0x50, 0x13, 0x28, 0x15, 0x80, 0x01, 0x21, 0x6c,
0x22
};
static const uint8_t id_resp_imsi[] = {
0x0e, 0x00, 0x11,
0x01, 0xc0, 0x09, 0x08, 0x16, 0x08, 0x99, 0x10,
0x07, 0x00, 0x00, 0x00, 0x03, 0x49, 0xc7, 0x5b,
0xb6
};
static const uint8_t attach_complete[] = {
0x0e, 0x00, 0x08,
0x01, 0xc0, 0x0d, 0x08, 0x03, 0x55, 0x1c, 0xea
};
static const uint8_t pdp_context[] = {
0x0e, 0x00, 0x5a,
0x01, 0xc0, 0x11, 0x0a, 0x41, 0x05, 0x03, 0x0c,
0x00, 0x00, 0x1f, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x21, 0x28,
0x12, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x65, 0x74, 0x05, 0x65, 0x70, 0x6c, 0x75, 0x73,
0x02, 0x64, 0x65, 0x27, 0x2a, 0x80, 0xc0, 0x23,
0x13, 0x01, 0x00, 0x00, 0x13, 0x05, 0x65, 0x70,
0x6c, 0x75, 0x73, 0x08, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x80, 0x21, 0x10, 0x01,
0x00, 0x00, 0x10, 0x81, 0x06, 0x00, 0x00, 0x00,
0x00, 0x83, 0x06, 0x00, 0x00, 0x00, 0x00, 0xcf,
0x90, 0xcc
};
static const uint8_t qos_profile[] = { 0x0, 0x0, 0x04 };
static uint32_t tlli = 0xadf11821;
enum state {
Test_Start,
Test_IdRespIMEI,
Test_IdRespIMSI,
Test_AttachCompl,
Test_PDPAct,
Test_Done,
};
static enum state current_state = Test_Start;
static void extract_tmsi_and_generate_tlli(struct msgb *msg, struct tlv_parsed *tp)
{
uint32_t tmsi;
struct gprs_llc_hdr_parsed hp;
struct tlv_parsed ack_tp;
gprs_llc_hdr_parse(&hp, TLVP_VAL(tp, BSSGP_IE_LLC_PDU),
TLVP_LEN(tp, BSSGP_IE_LLC_PDU));
msgb_gmmh(msg) = (unsigned char *) hp.data;
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
OSMO_ASSERT(gh->msg_type == GSM48_MT_GMM_ATTACH_ACK);
struct gsm48_attach_ack *ack = (struct gsm48_attach_ack *) gh->data;
tlv_parse(&ack_tp, &gsm48_gmm_att_tlvdef, ack->data,
(msg->data + msg->len) - ack->data, 0, 0);
OSMO_ASSERT(TLVP_PRESENT(&ack_tp, GSM48_IE_GMM_ALLOC_PTMSI));
memcpy(&tmsi, TLVP_VAL(&ack_tp, GSM48_IE_GMM_ALLOC_PTMSI) + 1, 4);
tmsi = ntohl(tmsi);
tlli = gprs_tmsi2tlli(tmsi, TLLI_LOCAL);
printf("New TLLI(0x%08x) based on tmsi(0x%x)\n", tlli, tmsi);
}
void test_pdp_activation_start(struct gprs_bssgp_pcu *pcu)
{
struct msgb *msg = create_msg(attach, ARRAY_SIZE(attach));
bssgp_tx_ul_ud(pcu->bctx, tlli, qos_profile, msg);
current_state = Test_IdRespIMEI;
}
void test_pdp_activation_data(struct gprs_bssgp_pcu *pcu, struct msgb *msg, struct tlv_parsed *tp)
{
const uint8_t *data;
size_t len;
switch (current_state) {
case Test_IdRespIMEI:
data = id_resp_imei;
len = ARRAY_SIZE(id_resp_imei);
current_state = Test_IdRespIMSI;
break;
case Test_IdRespIMSI:
data = id_resp_imsi;
len = ARRAY_SIZE(id_resp_imsi);
current_state = Test_AttachCompl;
break;
case Test_AttachCompl:
data = attach_complete;
len = ARRAY_SIZE(attach_complete);
extract_tmsi_and_generate_tlli(msg, tp);
current_state = Test_PDPAct;
break;
case Test_PDPAct:
printf("PDP context is active or not...\n");
return;
break;
case Test_Done:
case Test_Start: /* fall through */
return;
break;
default:
printf("Unknown state. %d\n", current_state);
return;
break;
};
struct msgb *out = create_msg(data, len);
bssgp_tx_ul_ud(pcu->bctx, tlli, qos_profile, out);
/* send it after the PDP... */
if (current_state == Test_PDPAct) {
out = create_msg(pdp_context, ARRAY_SIZE(pdp_context));
bssgp_tx_ul_ud(pcu->bctx, tlli, qos_profile, out);
}
}

View File

@ -0,0 +1,89 @@
/* (C) 2013 by Holger Hans Peter Freyther
*
* 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/>.
*
*/
extern "C" {
#include <osmocom/core/msgb.h>
#include <osmocom/core/backtrace.h>
#include <osmocom/gsm/gsm_utils.h>
}
#include "openbsc_clone.h"
#include "gprs_tests.h"
#include <gprs_bssgp_pcu.h>
#include <stdint.h>
#include <string.h>
/* GPRS attach with a foreign TLLI */
static const uint8_t gprs_attach_llc[] = {
/* LLC-PDU IE */
0x0e, 0x00, 0x2e,
0x01, 0xc0, 0x01, 0x08, 0x01, 0x02, 0xf5, 0x40,
0x71, 0x08, 0x00, 0x05, 0xf4, 0x2d, 0xf1, 0x18,
0x20, 0x62, 0xf2, 0x10, 0x09, 0x67, 0x00, 0x13,
0x16, 0x73, 0x43, 0x2a, 0x80, 0x42, 0x00, 0x42,
0x88, 0x0b, 0x04, 0x20, 0x04, 0x2e, 0x82, 0x30,
0x42, 0x00, 0x40, 0xaa, 0xf3, 0x18
};
static uint32_t next_wanted_nu;
void test_replay_gprs_attach(struct gprs_bssgp_pcu *pcu)
{
uint32_t tlli = 0xadf11820;
const uint8_t qos_profile[] = { 0x0, 0x0, 0x04 };
next_wanted_nu = 0;
struct msgb *msg = create_msg(gprs_attach_llc, ARRAY_SIZE(gprs_attach_llc));
bssgp_tx_ul_ud(pcu->bctx, tlli, qos_profile, msg);
}
void test_replay_gprs_data(struct gprs_bssgp_pcu *pcu, struct msgb *msg, struct tlv_parsed *tp)
{
struct bssgp_ud_hdr *budh;
struct gprs_llc_hdr_parsed ph;
uint32_t tlli;
if (!TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU))
return;
gprs_llc_hdr_parse(&ph, TLVP_VAL(tp, BSSGP_IE_LLC_PDU),
TLVP_LEN(tp, BSSGP_IE_LLC_PDU));
budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
tlli = ntohl(budh->tlli);
/* all messages we should get, should be for a foreign tlli */
OSMO_ASSERT(gprs_tlli_type(tlli) == TLLI_FOREIGN);
printf("TLLI(0x%08x) is foreign!\n", tlli);
OSMO_ASSERT(ph.cmd == GPRS_LLC_UI);
OSMO_ASSERT(ph.sapi == 1);
OSMO_ASSERT(ph.seq_tx == next_wanted_nu);
next_wanted_nu += 1;
/* this test just wants to see messages... no further data is sent */
if (next_wanted_nu == 6) {
printf("GPRS attach with increasing N(U) done.\n");
gprs_test_success(pcu);
}
}

266
tests/llc/LlcTest.cpp Normal file
View File

@ -0,0 +1,266 @@
/*
* LlcTest.cpp
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
*
* 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/>.
*
*/
extern "C" {
#include <osmocom/core/linuxlist.h>
}
#include "llc.h"
#include "gprs_debug.h"
extern "C" {
#include "pcu_vty.h"
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/vty/vty.h>
}
void *tall_pcu_ctx;
int16_t spoof_mnc = 0, spoof_mcc = 0;
static void enqueue_data(gprs_llc_queue *queue, const uint8_t *data, size_t len,
gprs_llc_queue::MetaInfo *info = 0)
{
struct timeval *tv;
uint8_t *msg_data;
struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv) * 2,
"llc_pdu_queue");
msg_data = (uint8_t *)msgb_put(llc_msg, len);
memcpy(msg_data, data, len);
queue->enqueue(llc_msg, info);
}
static void dequeue_and_check(gprs_llc_queue *queue, const uint8_t *exp_data,
size_t len, const gprs_llc_queue::MetaInfo *exp_info = 0)
{
struct msgb *llc_msg;
uint8_t *msg_data;
const gprs_llc_queue::MetaInfo *info_res;
llc_msg = queue->dequeue(&info_res);
OSMO_ASSERT(llc_msg != NULL);
fprintf(stderr, "dequeued msg, length %d (expected %d), data %s\n",
msgb_length(llc_msg), len, msgb_hexdump(llc_msg));
OSMO_ASSERT(msgb_length(llc_msg) == len);
msg_data = msgb_data(llc_msg);
OSMO_ASSERT(memcmp(msg_data, exp_data, len) == 0);
if (exp_info)
OSMO_ASSERT(memcmp(exp_info, info_res, sizeof(*exp_info)) == 0);
msgb_free(llc_msg);
}
static void enqueue_data(gprs_llc_queue *queue, const char *message,
gprs_llc_queue::MetaInfo *info = 0)
{
enqueue_data(queue, (uint8_t *)(message), strlen(message), info);
}
static void dequeue_and_check(gprs_llc_queue *queue, const char *exp_message,
const gprs_llc_queue::MetaInfo *exp_info = 0)
{
dequeue_and_check(queue,
(uint8_t *)(exp_message), strlen(exp_message), exp_info);
}
static void test_llc_queue()
{
gprs_llc_queue queue;
printf("=== start %s ===\n", __func__);
queue.init();
OSMO_ASSERT(queue.size() == 0);
OSMO_ASSERT(queue.octets() == 0);
enqueue_data(&queue, "LLC message");
OSMO_ASSERT(queue.size() == 1);
OSMO_ASSERT(queue.octets() == 11);
enqueue_data(&queue, "other LLC message");
OSMO_ASSERT(queue.size() == 2);
OSMO_ASSERT(queue.octets() == 28);
dequeue_and_check(&queue, "LLC message");
OSMO_ASSERT(queue.size() == 1);
OSMO_ASSERT(queue.octets() == 17);
dequeue_and_check(&queue, "other LLC message");
OSMO_ASSERT(queue.size() == 0);
OSMO_ASSERT(queue.octets() == 0);
enqueue_data(&queue, "LLC");
OSMO_ASSERT(queue.size() == 1);
OSMO_ASSERT(queue.octets() == 3);
queue.clear(NULL);
OSMO_ASSERT(queue.size() == 0);
OSMO_ASSERT(queue.octets() == 0);
printf("=== end %s ===\n", __func__);
}
static void test_llc_meta()
{
gprs_llc_queue queue;
gprs_llc_queue::MetaInfo info1;
gprs_llc_queue::MetaInfo info2;
info1.recv_time.tv_sec = 123456777;
info1.recv_time.tv_usec = 123456;
info1.expire_time.tv_sec = 123456789;
info1.expire_time.tv_usec = 987654;
info2.recv_time.tv_sec = 987654321;
info2.recv_time.tv_usec = 547352;
info2.expire_time.tv_sec = 987654327;
info2.expire_time.tv_usec = 867252;
printf("=== start %s ===\n", __func__);
queue.init();
OSMO_ASSERT(queue.size() == 0);
OSMO_ASSERT(queue.octets() == 0);
enqueue_data(&queue, "LLC message 1", &info1);
enqueue_data(&queue, "LLC message 2", &info2);
dequeue_and_check(&queue, "LLC message 1", &info1);
dequeue_and_check(&queue, "LLC message 2", &info2);
queue.clear(NULL);
OSMO_ASSERT(queue.size() == 0);
OSMO_ASSERT(queue.octets() == 0);
printf("=== end %s ===\n", __func__);
}
static void test_llc_merge()
{
gprs_llc_queue queue1;
gprs_llc_queue queue2;
gprs_llc_queue::MetaInfo info = {0};
printf("=== start %s ===\n", __func__);
queue1.init();
queue2.init();
info.recv_time.tv_sec += 1;
enqueue_data(&queue1, "*A*", &info);
info.recv_time.tv_sec += 1;
enqueue_data(&queue1, "*B*", &info);
info.recv_time.tv_sec += 1;
enqueue_data(&queue2, "*C*", &info);
info.recv_time.tv_sec += 1;
enqueue_data(&queue1, "*D*", &info);
info.recv_time.tv_sec += 1;
enqueue_data(&queue2, "*E*", &info);
OSMO_ASSERT(queue1.size() == 3);
OSMO_ASSERT(queue1.octets() == 9);
OSMO_ASSERT(queue2.size() == 2);
OSMO_ASSERT(queue2.octets() == 6);
queue2.move_and_merge(&queue1);
OSMO_ASSERT(queue1.size() == 0);
OSMO_ASSERT(queue1.octets() == 0);
OSMO_ASSERT(queue2.size() == 5);
OSMO_ASSERT(queue2.octets() == 15);
dequeue_and_check(&queue2, "*A*");
dequeue_and_check(&queue2, "*B*");
dequeue_and_check(&queue2, "*C*");
dequeue_and_check(&queue2, "*D*");
dequeue_and_check(&queue2, "*E*");
OSMO_ASSERT(queue2.size() == 0);
OSMO_ASSERT(queue2.octets() == 0);
printf("=== end %s ===\n", __func__);
}
static const struct log_info_cat default_categories[] = {
{"DPCU", "", "GPRS Packet Control Unit (PCU)", LOGL_INFO, 1},
};
static int filter_fn(const struct log_context *ctx,
struct log_target *tar)
{
return 1;
}
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)
{
struct vty_app_info pcu_vty_info = {0};
tall_pcu_ctx = talloc_named_const(NULL, 1, "LlcTest context");
if (!tall_pcu_ctx)
abort();
msgb_set_talloc_ctx(tall_pcu_ctx);
osmo_init_logging(&debug_log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
log_set_log_level(osmo_stderr_target, LOGL_INFO);
vty_init(&pcu_vty_info);
pcu_vty_init(&debug_log_info);
test_llc_queue();
test_llc_meta();
test_llc_merge();
if (getenv("TALLOC_REPORT_FULL"))
talloc_report_full(tall_pcu_ctx, stderr);
return EXIT_SUCCESS;
}
extern "C" {
void l1if_pdch_req() { abort(); }
void l1if_connect_pdch() { abort(); }
void l1if_close_pdch() { abort(); }
void l1if_open_pdch() { abort(); }
}

9
tests/llc/LlcTest.err Normal file
View File

@ -0,0 +1,9 @@
dequeued msg, length 11 (expected 11), data 4c 4c 43 20 6d 65 73 73 61 67 65
dequeued msg, length 17 (expected 17), data 6f 74 68 65 72 20 4c 4c 43 20 6d 65 73 73 61 67 65
dequeued msg, length 13 (expected 13), data 4c 4c 43 20 6d 65 73 73 61 67 65 20 31
dequeued msg, length 13 (expected 13), data 4c 4c 43 20 6d 65 73 73 61 67 65 20 32
dequeued msg, length 3 (expected 3), data 2a 41 2a
dequeued msg, length 3 (expected 3), data 2a 42 2a
dequeued msg, length 3 (expected 3), data 2a 43 2a
dequeued msg, length 3 (expected 3), data 2a 44 2a
dequeued msg, length 3 (expected 3), data 2a 45 2a

6
tests/llc/LlcTest.ok Normal file
View File

@ -0,0 +1,6 @@
=== start test_llc_queue ===
=== end test_llc_queue ===
=== start test_llc_meta ===
=== end test_llc_meta ===
=== start test_llc_merge ===
=== end test_llc_merge ===

91
tests/llist/LListTest.cpp Normal file
View File

@ -0,0 +1,91 @@
/*
* LListTest.cpp
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
*
* 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 "cxx_linuxlist.h"
extern "C" {
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
}
#include <errno.h>
struct TestElem {
const char *str;
LListHead<TestElem> list;
TestElem(const char *s) : str(s), list(this) {};
};
static void test_linux_list()
{
LListHead<TestElem> elems, *pos, *tmp;
TestElem elem1("number one");
TestElem elem2("number two");
TestElem elem3("number three");
int count = 0;
printf("=== start %s ===\n", __func__);
OSMO_ASSERT(llist_empty(&elems));
llist_add_tail(&elem1.list, &elems);
llist_add_tail(&elem2.list, &elems);
llist_add_tail(&elem3.list, &elems);
OSMO_ASSERT(!llist_empty(&elems));
llist_for_each(pos, &elems) {
count += 1;
printf(" %i -> %s\n", count, pos->entry()->str);
}
OSMO_ASSERT(count == 3);
count = 0;
llist_for_each_safe(pos, tmp, &elems) {
count += 1;
if (count == 2)
llist_del(pos);
printf(" %i -> %s\n", count, pos->entry()->str);
}
OSMO_ASSERT(count == 3);
count = 0;
llist_for_each(pos, &elems) {
count += 1;
OSMO_ASSERT(pos != &elem2.list);
printf(" %i -> %s\n", count, pos->entry()->str);
}
OSMO_ASSERT(count == 2);
printf("=== end %s ===\n", __func__);
}
int main(int argc, char **argv)
{
test_linux_list();
return EXIT_SUCCESS;
}

View File

10
tests/llist/LListTest.ok Normal file
View File

@ -0,0 +1,10 @@
=== start test_linux_list ===
1 -> number one
2 -> number two
3 -> number three
1 -> number one
2 -> number two
3 -> number three
1 -> number one
2 -> number three
=== end test_linux_list ===

569
tests/ms/MsTest.cpp Normal file
View File

@ -0,0 +1,569 @@
/*
* MsTest.cpp
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
*
* 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 "tbf.h"
#include "gprs_debug.h"
#include "gprs_ms.h"
#include "gprs_ms_storage.h"
#include "bts.h"
extern "C" {
#include "pcu_vty.h"
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/vty/vty.h>
}
#include <errno.h>
#include <unistd.h>
void *tall_pcu_ctx;
int16_t spoof_mnc = 0, spoof_mcc = 0;
static void test_ms_state()
{
uint32_t tlli = 0xffeeddbb;
gprs_rlcmac_dl_tbf *dl_tbf;
gprs_rlcmac_ul_tbf *ul_tbf;
GprsMs *ms;
printf("=== start %s ===\n", __func__);
ms = new GprsMs(NULL, tlli);
OSMO_ASSERT(ms->is_idle());
dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
new (dl_tbf) gprs_rlcmac_dl_tbf(NULL);
ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
new (ul_tbf) gprs_rlcmac_ul_tbf(NULL);
ms->attach_tbf(ul_tbf);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(ms->ul_tbf() == ul_tbf);
OSMO_ASSERT(ms->dl_tbf() == NULL);
ms->attach_tbf(dl_tbf);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(ms->ul_tbf() == ul_tbf);
OSMO_ASSERT(ms->dl_tbf() == dl_tbf);
OSMO_ASSERT(ms->tbf(GPRS_RLCMAC_UL_TBF) == ul_tbf);
OSMO_ASSERT(ms->tbf(GPRS_RLCMAC_DL_TBF) == dl_tbf);
ms->detach_tbf(ul_tbf);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(ms->ul_tbf() == NULL);
OSMO_ASSERT(ms->dl_tbf() == dl_tbf);
ms->detach_tbf(dl_tbf);
/* The ms object is freed now */
ms = NULL;
talloc_free(dl_tbf);
talloc_free(ul_tbf);
printf("=== end %s ===\n", __func__);
}
static void test_ms_callback()
{
uint32_t tlli = 0xffeeddbb;
gprs_rlcmac_dl_tbf *dl_tbf;
gprs_rlcmac_ul_tbf *ul_tbf;
GprsMs *ms;
static enum {UNKNOWN, IS_IDLE, IS_ACTIVE} last_cb = UNKNOWN;
struct MyCallback: public GprsMs::Callback {
virtual void ms_idle(class GprsMs *ms) {
OSMO_ASSERT(ms->is_idle());
printf(" ms_idle() was called\n");
last_cb = IS_IDLE;
}
virtual void ms_active(class GprsMs *ms) {
OSMO_ASSERT(!ms->is_idle());
printf(" ms_active() was called\n");
last_cb = IS_ACTIVE;
}
} cb;
printf("=== start %s ===\n", __func__);
ms = new GprsMs(NULL, tlli);
ms->set_callback(&cb);
OSMO_ASSERT(ms->is_idle());
dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
new (dl_tbf) gprs_rlcmac_dl_tbf(NULL);
ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
new (ul_tbf) gprs_rlcmac_ul_tbf(NULL);
OSMO_ASSERT(last_cb == UNKNOWN);
ms->attach_tbf(ul_tbf);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(ms->ul_tbf() == ul_tbf);
OSMO_ASSERT(ms->dl_tbf() == NULL);
OSMO_ASSERT(last_cb == IS_ACTIVE);
last_cb = UNKNOWN;
ms->attach_tbf(dl_tbf);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(ms->ul_tbf() == ul_tbf);
OSMO_ASSERT(ms->dl_tbf() == dl_tbf);
OSMO_ASSERT(last_cb == UNKNOWN);
ms->detach_tbf(ul_tbf);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(ms->ul_tbf() == NULL);
OSMO_ASSERT(ms->dl_tbf() == dl_tbf);
OSMO_ASSERT(last_cb == UNKNOWN);
ms->detach_tbf(dl_tbf);
OSMO_ASSERT(ms->is_idle());
OSMO_ASSERT(ms->ul_tbf() == NULL);
OSMO_ASSERT(ms->dl_tbf() == NULL);
OSMO_ASSERT(last_cb == IS_IDLE);
last_cb = UNKNOWN;
delete ms;
talloc_free(dl_tbf);
talloc_free(ul_tbf);
printf("=== end %s ===\n", __func__);
}
static void test_ms_replace_tbf()
{
uint32_t tlli = 0xffeeddbb;
gprs_rlcmac_dl_tbf *dl_tbf[2];
gprs_rlcmac_ul_tbf *ul_tbf;
GprsMs *ms;
static bool was_idle;
struct MyCallback: public GprsMs::Callback {
virtual void ms_idle(class GprsMs *ms) {
OSMO_ASSERT(ms->is_idle());
printf(" ms_idle() was called\n");
was_idle = true;
}
virtual void ms_active(class GprsMs *ms) {
OSMO_ASSERT(!ms->is_idle());
printf(" ms_active() was called\n");
}
} cb;
printf("=== start %s ===\n", __func__);
ms = new GprsMs(NULL, tlli);
ms->set_callback(&cb);
OSMO_ASSERT(ms->is_idle());
was_idle = false;
dl_tbf[0] = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
new (dl_tbf[0]) gprs_rlcmac_dl_tbf(NULL);
dl_tbf[1] = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
new (dl_tbf[1]) gprs_rlcmac_dl_tbf(NULL);
ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
new (ul_tbf) gprs_rlcmac_ul_tbf(NULL);
ms->attach_tbf(dl_tbf[0]);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(ms->ul_tbf() == NULL);
OSMO_ASSERT(ms->dl_tbf() == dl_tbf[0]);
OSMO_ASSERT(llist_empty(&ms->old_tbfs()));
OSMO_ASSERT(!was_idle);
ms->attach_tbf(dl_tbf[1]);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(ms->ul_tbf() == NULL);
OSMO_ASSERT(ms->dl_tbf() == dl_tbf[1]);
OSMO_ASSERT(!llist_empty(&ms->old_tbfs()));
OSMO_ASSERT(!was_idle);
ms->attach_tbf(ul_tbf);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(ms->ul_tbf() == ul_tbf);
OSMO_ASSERT(ms->dl_tbf() == dl_tbf[1]);
OSMO_ASSERT(!llist_empty(&ms->old_tbfs()));
OSMO_ASSERT(!was_idle);
ms->detach_tbf(ul_tbf);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(ms->ul_tbf() == NULL);
OSMO_ASSERT(ms->dl_tbf() == dl_tbf[1]);
OSMO_ASSERT(!llist_empty(&ms->old_tbfs()));
OSMO_ASSERT(!was_idle);
ms->detach_tbf(dl_tbf[0]);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(ms->ul_tbf() == NULL);
OSMO_ASSERT(ms->dl_tbf() == dl_tbf[1]);
OSMO_ASSERT(llist_empty(&ms->old_tbfs()));
OSMO_ASSERT(!was_idle);
ms->detach_tbf(dl_tbf[1]);
OSMO_ASSERT(ms->is_idle());
OSMO_ASSERT(ms->ul_tbf() == NULL);
OSMO_ASSERT(ms->dl_tbf() == NULL);
OSMO_ASSERT(llist_empty(&ms->old_tbfs()));
OSMO_ASSERT(was_idle);
delete ms;
talloc_free(dl_tbf[0]);
talloc_free(dl_tbf[1]);
talloc_free(ul_tbf);
printf("=== end %s ===\n", __func__);
}
static void test_ms_change_tlli()
{
uint32_t start_tlli = 0xaa000000;
uint32_t new_ms_tlli = 0xff001111;
uint32_t other_sgsn_tlli = 0xff00eeee;
GprsMs *ms;
printf("=== start %s ===\n", __func__);
ms = new GprsMs(NULL, start_tlli);
OSMO_ASSERT(ms->is_idle());
/* MS announces TLLI, SGSN uses it immediately */
ms->set_tlli(new_ms_tlli);
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
OSMO_ASSERT(ms->check_tlli(start_tlli));
ms->confirm_tlli(new_ms_tlli);
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
OSMO_ASSERT(!ms->check_tlli(start_tlli));
/* MS announces TLLI, SGSN uses it later */
ms->set_tlli(start_tlli);
ms->confirm_tlli(start_tlli);
ms->set_tlli(new_ms_tlli);
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
OSMO_ASSERT(ms->check_tlli(start_tlli));
ms->confirm_tlli(start_tlli);
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
OSMO_ASSERT(ms->check_tlli(start_tlli));
ms->set_tlli(new_ms_tlli);
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
OSMO_ASSERT(ms->check_tlli(start_tlli));
ms->confirm_tlli(new_ms_tlli);
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
OSMO_ASSERT(!ms->check_tlli(start_tlli));
/* MS announces TLLI, SGSN uses it later after another new TLLI */
ms->set_tlli(start_tlli);
ms->confirm_tlli(start_tlli);
ms->set_tlli(new_ms_tlli);
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
OSMO_ASSERT(ms->check_tlli(start_tlli));
ms->confirm_tlli(other_sgsn_tlli);
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
OSMO_ASSERT(ms->check_tlli(other_sgsn_tlli));
ms->set_tlli(new_ms_tlli);
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
OSMO_ASSERT(ms->check_tlli(other_sgsn_tlli));
ms->confirm_tlli(new_ms_tlli);
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
OSMO_ASSERT(!ms->check_tlli(start_tlli));
OSMO_ASSERT(!ms->check_tlli(other_sgsn_tlli));
/* SGSN uses the new TLLI before it is announced by the MS (shouldn't
* happen in normal use) */
ms->set_tlli(start_tlli);
ms->confirm_tlli(start_tlli);
ms->confirm_tlli(new_ms_tlli);
OSMO_ASSERT(ms->tlli() == start_tlli);
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
OSMO_ASSERT(ms->check_tlli(start_tlli));
ms->set_tlli(new_ms_tlli);
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
OSMO_ASSERT(!ms->check_tlli(start_tlli));
delete ms;
printf("=== end %s ===\n", __func__);
}
static void test_ms_storage()
{
uint32_t tlli = 0xffeeddbb;
const char *imsi1 = "001001987654321";
const char *imsi2 = "001001987654322";
gprs_rlcmac_ul_tbf *ul_tbf;
GprsMs *ms, *ms_tmp;
GprsMsStorage store(NULL);
printf("=== start %s ===\n", __func__);
ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
new (ul_tbf) gprs_rlcmac_ul_tbf(NULL);
ms = store.get_ms(tlli + 0);
OSMO_ASSERT(ms == NULL);
ms = store.create_ms(tlli + 0, GPRS_RLCMAC_UL_TBF);
OSMO_ASSERT(ms != NULL);
OSMO_ASSERT(ms->tlli() == tlli + 0);
ms->set_imsi(imsi1);
OSMO_ASSERT(strcmp(ms->imsi(), imsi1) == 0);
ms_tmp = store.get_ms(tlli + 0);
OSMO_ASSERT(ms == ms_tmp);
OSMO_ASSERT(ms->tlli() == tlli + 0);
ms_tmp = store.get_ms(0, 0, imsi1);
OSMO_ASSERT(ms == ms_tmp);
OSMO_ASSERT(strcmp(ms->imsi(), imsi1) == 0);
ms_tmp = store.get_ms(0, 0, imsi2);
OSMO_ASSERT(ms_tmp == NULL);
ms = store.create_ms(tlli + 1, GPRS_RLCMAC_UL_TBF);
OSMO_ASSERT(ms != NULL);
OSMO_ASSERT(ms->tlli() == tlli + 1);
ms->set_imsi(imsi2);
OSMO_ASSERT(strcmp(ms->imsi(), imsi2) == 0);
ms_tmp = store.get_ms(tlli + 1);
OSMO_ASSERT(ms == ms_tmp);
OSMO_ASSERT(ms->tlli() == tlli + 1);
ms_tmp = store.get_ms(0, 0, imsi1);
OSMO_ASSERT(ms_tmp != NULL);
OSMO_ASSERT(ms_tmp != ms);
ms_tmp = store.get_ms(0, 0, imsi2);
OSMO_ASSERT(ms == ms_tmp);
OSMO_ASSERT(strcmp(ms->imsi(), imsi2) == 0);
/* delete ms */
ms = store.get_ms(tlli + 0);
OSMO_ASSERT(ms != NULL);
ms->attach_tbf(ul_tbf);
ms->detach_tbf(ul_tbf);
ms = store.get_ms(tlli + 0);
OSMO_ASSERT(ms == NULL);
ms = store.get_ms(tlli + 1);
OSMO_ASSERT(ms != NULL);
/* delete ms */
ms = store.get_ms(tlli + 1);
OSMO_ASSERT(ms != NULL);
ms->attach_tbf(ul_tbf);
ms->detach_tbf(ul_tbf);
ms = store.get_ms(tlli + 1);
OSMO_ASSERT(ms == NULL);
talloc_free(ul_tbf);
printf("=== end %s ===\n", __func__);
}
static void test_ms_timeout()
{
uint32_t tlli = 0xffeeddbb;
gprs_rlcmac_dl_tbf *dl_tbf;
gprs_rlcmac_ul_tbf *ul_tbf;
GprsMs *ms;
static enum {UNKNOWN, IS_IDLE, IS_ACTIVE} last_cb = UNKNOWN;
struct MyCallback: public GprsMs::Callback {
virtual void ms_idle(class GprsMs *ms) {
OSMO_ASSERT(ms->is_idle());
printf(" ms_idle() was called\n");
last_cb = IS_IDLE;
}
virtual void ms_active(class GprsMs *ms) {
OSMO_ASSERT(!ms->is_idle());
printf(" ms_active() was called\n");
last_cb = IS_ACTIVE;
}
} cb;
printf("=== start %s ===\n", __func__);
ms = new GprsMs(NULL, tlli);
ms->set_callback(&cb);
ms->set_timeout(1);
OSMO_ASSERT(ms->is_idle());
dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
new (dl_tbf) gprs_rlcmac_dl_tbf(NULL);
ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
new (ul_tbf) gprs_rlcmac_ul_tbf(NULL);
OSMO_ASSERT(last_cb == UNKNOWN);
ms->attach_tbf(ul_tbf);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(last_cb == IS_ACTIVE);
last_cb = UNKNOWN;
ms->attach_tbf(dl_tbf);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(last_cb == UNKNOWN);
ms->detach_tbf(ul_tbf);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(last_cb == UNKNOWN);
ms->detach_tbf(dl_tbf);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(last_cb == UNKNOWN);
usleep(1100000);
osmo_timers_update();
OSMO_ASSERT(ms->is_idle());
OSMO_ASSERT(last_cb == IS_IDLE);
last_cb = UNKNOWN;
delete ms;
talloc_free(dl_tbf);
talloc_free(ul_tbf);
printf("=== end %s ===\n", __func__);
}
static void test_ms_cs_selection()
{
BTS the_bts;
gprs_rlcmac_bts *bts = the_bts.bts_data();
uint32_t tlli = 0xffeeddbb;
gprs_rlcmac_dl_tbf *dl_tbf;
GprsMs *ms;
printf("=== start %s ===\n", __func__);
bts->initial_cs_dl = 4;
bts->initial_cs_ul = 1;
bts->cs_downgrade_threshold = 0;
ms = new GprsMs(&the_bts, tlli);
OSMO_ASSERT(ms->is_idle());
dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
new (dl_tbf) gprs_rlcmac_dl_tbf(NULL);
dl_tbf->set_ms(ms);
OSMO_ASSERT(!ms->is_idle());
OSMO_ASSERT(ms->current_cs_dl().to_num() == 4);
bts->cs_downgrade_threshold = 200;
OSMO_ASSERT(ms->current_cs_dl().to_num() == 3);
talloc_free(dl_tbf);
printf("=== end %s ===\n", __func__);
}
static const struct log_info_cat default_categories[] = {
{"DPCU", "", "GPRS Packet Control Unit (PCU)", LOGL_INFO, 1},
};
static int filter_fn(const struct log_context *ctx,
struct log_target *tar)
{
return 1;
}
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)
{
struct vty_app_info pcu_vty_info = {0};
tall_pcu_ctx = talloc_named_const(NULL, 1, "MsTest context");
if (!tall_pcu_ctx)
abort();
msgb_set_talloc_ctx(tall_pcu_ctx);
osmo_init_logging(&debug_log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
log_set_log_level(osmo_stderr_target, LOGL_INFO);
vty_init(&pcu_vty_info);
pcu_vty_init(&debug_log_info);
test_ms_state();
test_ms_callback();
test_ms_replace_tbf();
test_ms_change_tlli();
test_ms_storage();
test_ms_timeout();
test_ms_cs_selection();
if (getenv("TALLOC_REPORT_FULL"))
talloc_report_full(tall_pcu_ctx, stderr);
return EXIT_SUCCESS;
}
extern "C" {
void l1if_pdch_req() { abort(); }
void l1if_connect_pdch() { abort(); }
void l1if_close_pdch() { abort(); }
void l1if_open_pdch() { abort(); }
}

58
tests/ms/MsTest.err Normal file
View File

@ -0,0 +1,58 @@
Creating MS object, TLLI = 0xffeeddbb
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
Destroying MS object, TLLI = 0xffeeddbb
Creating MS object, TLLI = 0xffeeddbb
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
Destroying MS object, TLLI = 0xffeeddbb
Creating MS object, TLLI = 0xffeeddbb
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
Destroying MS object, TLLI = 0xffeeddbb
Creating MS object, TLLI = 0xaa000000
Modifying MS object, UL TLLI: 0xaa000000 -> 0xff001111, not yet confirmed
Modifying MS object, TLLI: 0xff001111 confirmed
Modifying MS object, UL TLLI: 0xff001111 -> 0xaa000000, not yet confirmed
Modifying MS object, TLLI: 0xaa000000 confirmed
Modifying MS object, UL TLLI: 0xaa000000 -> 0xff001111, not yet confirmed
Modifying MS object, TLLI: 0xff001111 confirmed
Modifying MS object, UL TLLI: 0xff001111 -> 0xaa000000, not yet confirmed
Modifying MS object, TLLI: 0xaa000000 confirmed
Modifying MS object, UL TLLI: 0xaa000000 -> 0xff001111, not yet confirmed
The MS object cannot fully confirm an unexpected TLLI: 0xff00eeee, partly confirmed
Modifying MS object, TLLI: 0xff001111 confirmed
Modifying MS object, UL TLLI: 0xff001111 -> 0xaa000000, not yet confirmed
Modifying MS object, TLLI: 0xaa000000 confirmed
The MS object cannot fully confirm an unexpected TLLI: 0xff001111, partly confirmed
Modifying MS object, TLLI: 0xaa000000 -> 0xff001111, already confirmed partly
Destroying MS object, TLLI = 0xff001111
Creating MS object, TLLI = 0x00000000
Modifying MS object, UL TLLI: 0x00000000 -> 0xffeeddbb, not yet confirmed
Modifying MS object, TLLI = 0xffeeddbb, IMSI '' -> '001001987654321'
Creating MS object, TLLI = 0x00000000
Modifying MS object, UL TLLI: 0x00000000 -> 0xffeeddbc, not yet confirmed
Modifying MS object, TLLI = 0xffeeddbc, IMSI '' -> '001001987654322'
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
Destroying MS object, TLLI = 0xffeeddbb
Attaching TBF to MS object, TLLI = 0xffeeddbc, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
Detaching TBF from MS object, TLLI = 0xffeeddbc, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
Destroying MS object, TLLI = 0xffeeddbc
Creating MS object, TLLI = 0xffeeddbb
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
Timeout for MS object, TLLI = 0xffeeddbb
Destroying MS object, TLLI = 0xffeeddbb
Creating MS object, TLLI = 0xffeeddbb
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0xffeeddbb DIR=DL STATE=NULL)

20
tests/ms/MsTest.ok Normal file
View File

@ -0,0 +1,20 @@
=== start test_ms_state ===
=== end test_ms_state ===
=== start test_ms_callback ===
ms_active() was called
ms_idle() was called
=== end test_ms_callback ===
=== start test_ms_replace_tbf ===
ms_active() was called
ms_idle() was called
=== end test_ms_replace_tbf ===
=== start test_ms_change_tlli ===
=== end test_ms_change_tlli ===
=== start test_ms_storage ===
=== end test_ms_storage ===
=== start test_ms_timeout ===
ms_active() was called
ms_idle() was called
=== end test_ms_timeout ===
=== start test_ms_cs_selection ===
=== end test_ms_cs_selection ===

View File

@ -54,8 +54,8 @@ void printSizeofRLCMAC()
cout << "sizeof RlcMacDownlink_t " << sizeof(RlcMacDownlink_t) << endl;
cout << "sizeof Packet_Access_Reject_t " << sizeof(Packet_Access_Reject_t) << endl;
cout << "sizeof Packet_Cell_Change_Order_t " << sizeof(Packet_Cell_Change_Order_t) << endl;
cout << "sizeof Packet_Downlink_Assignment_t " << sizeof(Packet_Downlink_Assignment_t) << endl;
cout << "sizeof Packet_Cell_Change_Order_t " << sizeof(Packet_Cell_Change_Order_t) << endl;
cout << "sizeof Packet_Downlink_Assignment_t " << sizeof(Packet_Downlink_Assignment_t) << endl;
cout << "sizeof Packet_Measurement_Order_Reduced_t " << sizeof(Packet_Measurement_Order_Reduced_t) << endl;
cout << "sizeof Packet_Neighbour_Cell_Data_t " << sizeof(Packet_Neighbour_Cell_Data_t) << endl;
cout << "sizeof Packet_Serving_Cell_Data_t " << sizeof(Packet_Serving_Cell_Data_t) << endl;

View File

View File

@ -0,0 +1,58 @@
DOWNLINK
vector1 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
vector2 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
vector1 == vector2 : TRUE
vector1 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
vector2 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
vector1 == vector2 : TRUE
vector1 = 4724c040000000079eb2ac9402b2b2b2b2b2b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 4724c040000000079eb2ac9402b2b2b2b2b2b
vector2 = 4724c040000000079eb2ac9402b2b2b2b2b2b
vector1 == vector2 : TRUE
vector1 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
vector2 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
vector1 == vector2 : TRUE
UPLINK
vector1 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
vector2 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
vector1 == vector2 : TRUE
vector1 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
vector2 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
vector1 == vector2 : TRUE
vector1 = 4016713dc09427ca2ae57ef90906aafc001f80222b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 4016713dc09427ca2ae57ef90906aafc001f80222b
vector2 = 4016713dc09427ca2ae57ef90906aafc001f80222b
vector1 == vector2 : TRUE

1373
tests/tbf/TbfTest.cpp Normal file

File diff suppressed because it is too large Load Diff

5115
tests/tbf/TbfTest.err Normal file

File diff suppressed because it is too large Load Diff

45
tests/tbf/TbfTest.ok Normal file
View File

@ -0,0 +1,45 @@
=== start test_tbf_base ===
=== end test_tbf_base ===
=== start test_tbf_tlli_update ===
=== end test_tbf_tlli_update ===
=== start test_tbf_final_ack ===
=== end test_tbf_final_ack ===
=== start test_tbf_final_ack ===
=== end test_tbf_final_ack ===
=== start test_tbf_delayed_release ===
=== end test_tbf_delayed_release ===
=== start test_tbf_imsi ===
=== end test_tbf_imsi ===
=== start test_tbf_exhaustion ===
=== end test_tbf_exhaustion ===
=== start test_tbf_dl_llc_loss ===
=== end test_tbf_dl_llc_loss ===
=== start test_tbf_single_phase ===
=== end test_tbf_single_phase ===
=== start test_tbf_two_phase ===
=== end test_tbf_two_phase ===
=== start test_tbf_ra_update_rach ===
=== end test_tbf_ra_update_rach ===
=== start test_tbf_dl_flow_and_rach_two_phase ===
=== end test_tbf_dl_flow_and_rach_two_phase ===
=== start test_tbf_dl_flow_and_rach_single_phase ===
=== end test_tbf_dl_flow_and_rach_single_phase ===
=== start test_tbf_dl_reuse ===
=== end test_tbf_dl_reuse ===
=== start test_tbf_gprs_egprs ===
=== end test_tbf_gprs_egprs ===
=== start test_tbf_ws ===
=== end test_tbf_ws ===
=== start test_tbf_egprs_two_phase ===
=== end test_tbf_egprs_two_phase ===
=== start test_tbf_egprs_dl ===
Testing MCS-1
Testing MCS-2
Testing MCS-3
Testing MCS-4
Testing MCS-5
Testing MCS-6
Testing MCS-7
Testing MCS-8
Testing MCS-9
=== end test_tbf_egprs_dl ===

65
tests/testsuite.at Normal file
View File

@ -0,0 +1,65 @@
AT_INIT
AT_BANNER([Regression tests])
AT_SETUP([rlcmac])
AT_KEYWORDS([rlcmac])
cat $abs_srcdir/rlcmac/RLCMACTest.ok > expout
cat $abs_srcdir/rlcmac/RLCMACTest.err > experr
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/rlcmac/RLCMACTest], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([ts_alloc])
AT_KEYWORDS([ts_alloc])
cat $abs_srcdir/alloc/AllocTest.ok > expout
cat $abs_srcdir/alloc/AllocTest.err > experr
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/alloc/AllocTest], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([tbf])
AT_KEYWORDS([tbf])
cat $abs_srcdir/tbf/TbfTest.ok > expout
cat $abs_srcdir/tbf/TbfTest.err > experr
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/tbf/TbfTest], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([edge])
AT_KEYWORDS([edge])
cat $abs_srcdir/edge/EdgeTest.ok > expout
cat $abs_srcdir/edge/EdgeTest.err > experr
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/edge/EdgeTest], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([types])
AT_KEYWORDS([types])
cat $abs_srcdir/types/TypesTest.ok > expout
cat $abs_srcdir/types/TypesTest.err > experr
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/types/TypesTest], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([ms])
AT_KEYWORDS([ms])
cat $abs_srcdir/ms/MsTest.ok > expout
cat $abs_srcdir/ms/MsTest.err > experr
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/ms/MsTest], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([llc])
AT_KEYWORDS([llc])
cat $abs_srcdir/llc/LlcTest.ok > expout
cat $abs_srcdir/llc/LlcTest.err > experr
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/llc/LlcTest], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([llist])
AT_KEYWORDS([llist])
cat $abs_srcdir/llist/LListTest.ok > expout
cat $abs_srcdir/llist/LListTest.err > experr
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/llist/LListTest], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([codel])
AT_KEYWORDS([codel])
cat $abs_srcdir/codel/codel_test.ok > expout
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/codel/codel_test], [0], [expout], [ignore])
AT_CLEANUP

436
tests/types/TypesTest.cpp Normal file
View File

@ -0,0 +1,436 @@
/*
* TypesTest.cpp Test the primitive data types
*
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "bts.h"
#include "tbf.h"
#include "gprs_debug.h"
#include "encoding.h"
#include "decoding.h"
extern "C" {
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
}
#define OSMO_ASSERT_STR_EQ(a, b) \
do { \
if (strcmp(a, b)) { \
printf("String mismatch:\nGot:\t%s\nWant:\t%s\n", a, b); \
OSMO_ASSERT(false); \
} \
} while (0)
void *tall_pcu_ctx;
int16_t spoof_mnc = 0, spoof_mcc = 0;
static void test_llc(void)
{
{
uint8_t data[LLC_MAX_LEN] = {1, 2, 3, 4, };
uint8_t out;
gprs_llc llc;
llc.init();
OSMO_ASSERT(llc.chunk_size() == 0);
OSMO_ASSERT(llc.remaining_space() == LLC_MAX_LEN);
OSMO_ASSERT(llc.frame_length() == 0);
llc.put_frame(data, 2);
OSMO_ASSERT(llc.remaining_space() == LLC_MAX_LEN - 2);
OSMO_ASSERT(llc.frame_length() == 2);
OSMO_ASSERT(llc.chunk_size() == 2);
OSMO_ASSERT(llc.frame[0] == 1);
OSMO_ASSERT(llc.frame[1] == 2);
llc.append_frame(&data[3], 1);
OSMO_ASSERT(llc.remaining_space() == LLC_MAX_LEN - 3);
OSMO_ASSERT(llc.frame_length() == 3);
OSMO_ASSERT(llc.chunk_size() == 3);
/* consume two bytes */
llc.consume(&out, 1);
OSMO_ASSERT(llc.remaining_space() == LLC_MAX_LEN - 3);
OSMO_ASSERT(llc.frame_length() == 3);
OSMO_ASSERT(llc.chunk_size() == 2);
/* check that the bytes are as we expected */
OSMO_ASSERT(llc.frame[0] == 1);
OSMO_ASSERT(llc.frame[1] == 2);
OSMO_ASSERT(llc.frame[2] == 4);
/* now fill the frame */
llc.append_frame(data, llc.remaining_space() - 1);
OSMO_ASSERT(llc.fits_in_current_frame(1));
OSMO_ASSERT(!llc.fits_in_current_frame(2));
}
}
static void test_rlc()
{
{
struct gprs_rlc_data rlc = { 0, };
memset(rlc.block, 0x23, RLC_MAX_LEN);
uint8_t *p = rlc.prepare(20);
OSMO_ASSERT(p == rlc.block);
for (int i = 0; i < 20; ++i)
OSMO_ASSERT(p[i] == 0x2B);
for (int i = 20; i < RLC_MAX_LEN; ++i)
OSMO_ASSERT(p[i] == 0x0);
}
}
static void test_rlc_v_b()
{
{
gprs_rlc_v_b vb;
vb.reset();
for (size_t i = 0; i < RLC_MAX_SNS; ++i)
OSMO_ASSERT(vb.is_invalid(i));
vb.mark_unacked(23);
OSMO_ASSERT(vb.is_unacked(23));
vb.mark_nacked(23);
OSMO_ASSERT(vb.is_nacked(23));
vb.mark_acked(23);
OSMO_ASSERT(vb.is_acked(23));
vb.mark_resend(23);
OSMO_ASSERT(vb.is_resend(23));
vb.mark_invalid(23);
OSMO_ASSERT(vb.is_invalid(23));
}
}
static void test_rlc_v_n()
{
{
gprs_rlc_v_n vn;
vn.reset();
OSMO_ASSERT(!vn.is_received(0x23));
OSMO_ASSERT(vn.state(0x23) == GPRS_RLC_UL_BSN_INVALID);
vn.mark_received(0x23);
OSMO_ASSERT(vn.is_received(0x23));
OSMO_ASSERT(vn.state(0x23) == GPRS_RLC_UL_BSN_RECEIVED);
vn.mark_missing(0x23);
OSMO_ASSERT(!vn.is_received(0x23));
OSMO_ASSERT(vn.state(0x23) == GPRS_RLC_UL_BSN_MISSING);
}
}
static void test_rlc_dl_ul_basic()
{
{
gprs_rlc_dl_window dl_win;
OSMO_ASSERT(dl_win.window_empty());
OSMO_ASSERT(!dl_win.window_stalled());
OSMO_ASSERT(dl_win.distance() == 0);
dl_win.increment_send();
OSMO_ASSERT(!dl_win.window_empty());
OSMO_ASSERT(!dl_win.window_stalled());
OSMO_ASSERT(dl_win.distance() == 1);
for (int i = 1; i < 64; ++i) {
dl_win.increment_send();
OSMO_ASSERT(!dl_win.window_empty());
OSMO_ASSERT(dl_win.distance() == i + 1);
}
OSMO_ASSERT(dl_win.distance() == 64);
OSMO_ASSERT(dl_win.window_stalled());
dl_win.raise(1);
OSMO_ASSERT(dl_win.distance() == 63);
OSMO_ASSERT(!dl_win.window_stalled());
for (int i = 62; i >= 0; --i) {
dl_win.raise(1);
OSMO_ASSERT(dl_win.distance() == i);
}
OSMO_ASSERT(dl_win.distance() == 0);
OSMO_ASSERT(dl_win.window_empty());
dl_win.increment_send();
dl_win.increment_send();
dl_win.increment_send();
dl_win.increment_send();
OSMO_ASSERT(dl_win.distance() == 4);
for (int i = 0; i < 128; ++i) {
dl_win.increment_send();
dl_win.increment_send();
dl_win.raise(2);
OSMO_ASSERT(dl_win.distance() == 4);
}
}
{
gprs_rlc_ul_window ul_win;
int count;
const char *rbb;
char win_rbb[65];
uint8_t bin_rbb[8];
win_rbb[64] = '\0';
ul_win.m_v_n.reset();
OSMO_ASSERT(ul_win.is_in_window(0));
OSMO_ASSERT(ul_win.is_in_window(63));
OSMO_ASSERT(!ul_win.is_in_window(64));
OSMO_ASSERT(!ul_win.m_v_n.is_received(0));
rbb = "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII";
OSMO_ASSERT(ul_win.ssn() == 0);
ul_win.update_rbb(win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
Encoding::encode_rbb(win_rbb, bin_rbb);
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
Decoding::extract_rbb(bin_rbb, win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
/* simulate to have received 0, 1 and 5 */
OSMO_ASSERT(ul_win.is_in_window(0));
ul_win.receive_bsn(0);
count = ul_win.raise_v_q();
OSMO_ASSERT(ul_win.m_v_n.is_received(0));
OSMO_ASSERT(ul_win.v_q() == 1);
OSMO_ASSERT(ul_win.v_r() == 1);
OSMO_ASSERT(count == 1);
rbb = "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIR";
OSMO_ASSERT(ul_win.ssn() == 1);
ul_win.update_rbb(win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
Encoding::encode_rbb(win_rbb, bin_rbb);
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
Decoding::extract_rbb(bin_rbb, win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
OSMO_ASSERT(ul_win.is_in_window(1));
ul_win.receive_bsn(1);
count = ul_win.raise_v_q();
OSMO_ASSERT(ul_win.m_v_n.is_received(0));
OSMO_ASSERT(ul_win.v_q() == 2);
OSMO_ASSERT(ul_win.v_r() == 2);
OSMO_ASSERT(count == 1);
rbb = "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIRR";
OSMO_ASSERT(ul_win.ssn() == 2);
ul_win.update_rbb(win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
Encoding::encode_rbb(win_rbb, bin_rbb);
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
Decoding::extract_rbb(bin_rbb, win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
OSMO_ASSERT(ul_win.is_in_window(5));
ul_win.receive_bsn(5);
count = ul_win.raise_v_q();
OSMO_ASSERT(ul_win.m_v_n.is_received(0));
OSMO_ASSERT(ul_win.v_q() == 2);
OSMO_ASSERT(ul_win.v_r() == 6);
OSMO_ASSERT(count == 0);
rbb = "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIRRIIIR";
OSMO_ASSERT(ul_win.ssn() == 6);
ul_win.update_rbb(win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
Encoding::encode_rbb(win_rbb, bin_rbb);
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
Decoding::extract_rbb(bin_rbb, win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
OSMO_ASSERT(ul_win.is_in_window(65));
OSMO_ASSERT(ul_win.is_in_window(2));
OSMO_ASSERT(ul_win.m_v_n.is_received(5));
ul_win.receive_bsn(65);
count = ul_win.raise_v_q();
OSMO_ASSERT(count == 0);
OSMO_ASSERT(ul_win.m_v_n.is_received(5));
OSMO_ASSERT(ul_win.v_q() == 2);
OSMO_ASSERT(ul_win.v_r() == 66);
rbb = "IIIRIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIR";
OSMO_ASSERT(ul_win.ssn() == 66);
ul_win.update_rbb(win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
Encoding::encode_rbb(win_rbb, bin_rbb);
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
Decoding::extract_rbb(bin_rbb, win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
OSMO_ASSERT(ul_win.is_in_window(2));
OSMO_ASSERT(!ul_win.is_in_window(66));
ul_win.receive_bsn(2);
count = ul_win.raise_v_q();
OSMO_ASSERT(count == 1);
OSMO_ASSERT(ul_win.v_q() == 3);
OSMO_ASSERT(ul_win.v_r() == 66);
OSMO_ASSERT(ul_win.is_in_window(66));
ul_win.receive_bsn(66);
count = ul_win.raise_v_q();
OSMO_ASSERT(count == 0);
OSMO_ASSERT(ul_win.v_q() == 3);
OSMO_ASSERT(ul_win.v_r() == 67);
for (int i = 3; i <= 67; ++i) {
ul_win.receive_bsn(i);
ul_win.raise_v_q();
}
OSMO_ASSERT(ul_win.v_q() == 68);
OSMO_ASSERT(ul_win.v_r() == 68);
ul_win.receive_bsn(68);
count = ul_win.raise_v_q();
OSMO_ASSERT(ul_win.v_q() == 69);
OSMO_ASSERT(ul_win.v_r() == 69);
OSMO_ASSERT(count == 1);
/* now test the wrapping */
OSMO_ASSERT(ul_win.is_in_window(4));
OSMO_ASSERT(!ul_win.is_in_window(5));
ul_win.receive_bsn(4);
count = ul_win.raise_v_q();
OSMO_ASSERT(count == 0);
}
{
uint16_t lost = 0, recv = 0;
char show_rbb[65];
uint8_t bits_data[8];
BTS dummy_bts;
gprs_rlc_dl_window dl_win;
bitvec bits;
int bsn_begin, bsn_end, num_blocks;
Ack_Nack_Description_t desc;
dl_win.m_v_b.reset();
OSMO_ASSERT(dl_win.window_empty());
OSMO_ASSERT(!dl_win.window_stalled());
OSMO_ASSERT(dl_win.distance() == 0);
dl_win.increment_send();
OSMO_ASSERT(!dl_win.window_empty());
OSMO_ASSERT(!dl_win.window_stalled());
OSMO_ASSERT(dl_win.distance() == 1);
for (int i = 0; i < 35; ++i) {
dl_win.increment_send();
OSMO_ASSERT(!dl_win.window_empty());
OSMO_ASSERT(dl_win.distance() == i + 2);
}
uint8_t rbb_cmp[8] = { 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff };
bits.data = bits_data;
bits.data_len = sizeof(bits_data);
bits.cur_bit = 0;
memcpy(desc.RECEIVED_BLOCK_BITMAP, rbb_cmp,
sizeof(desc.RECEIVED_BLOCK_BITMAP));
desc.FINAL_ACK_INDICATION = 0;
desc.STARTING_SEQUENCE_NUMBER = 35;
num_blocks = Decoding::decode_gprs_acknack_bits(
&desc, &bits,
&bsn_begin, &bsn_end, &dl_win);
Decoding::extract_rbb(&bits, show_rbb);
printf("show_rbb: %s\n", show_rbb);
dl_win.update(&dummy_bts, &bits, 0, &lost, &recv);
OSMO_ASSERT(lost == 0);
OSMO_ASSERT(recv == 35);
OSMO_ASSERT(bsn_begin == 0);
OSMO_ASSERT(bsn_end == 35);
OSMO_ASSERT(num_blocks == 35);
dl_win.raise(dl_win.move_window());
for (int i = 0; i < 8; ++i) {
dl_win.increment_send();
OSMO_ASSERT(!dl_win.window_empty());
OSMO_ASSERT(dl_win.distance() == 2 + i);
}
uint8_t rbb_cmp2[8] = { 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0x31 };
bits.data = bits_data;
bits.data_len = sizeof(bits_data);
bits.cur_bit = 0;
memcpy(desc.RECEIVED_BLOCK_BITMAP, rbb_cmp2,
sizeof(desc.RECEIVED_BLOCK_BITMAP));
desc.FINAL_ACK_INDICATION = 0;
desc.STARTING_SEQUENCE_NUMBER = 35 + 8;
num_blocks = Decoding::decode_gprs_acknack_bits(
&desc, &bits,
&bsn_begin, &bsn_end, &dl_win);
Decoding::extract_rbb(&bits, show_rbb);
printf("show_rbb: %s\n", show_rbb);
lost = recv = 0;
dl_win.update(&dummy_bts, &bits, 0, &lost, &recv);
OSMO_ASSERT(lost == 5);
OSMO_ASSERT(recv == 3);
OSMO_ASSERT(bitvec_get_bit_pos(&bits, 0) == 0);
OSMO_ASSERT(bitvec_get_bit_pos(&bits, 7) == 1);
OSMO_ASSERT(bsn_begin == 35);
OSMO_ASSERT(bsn_end == 43);
OSMO_ASSERT(num_blocks == 8);
}
}
int main(int argc, char **argv)
{
osmo_init_logging(&gprs_log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
printf("Making some basic type testing.\n");
test_llc();
test_rlc();
test_rlc_v_b();
test_rlc_v_n();
test_rlc_dl_ul_basic();
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

8
tests/types/TypesTest.ok Normal file
View File

@ -0,0 +1,8 @@
Making some basic type testing.
rbb: 00 00 00 00 00 00 00 00
rbb: 00 00 00 00 00 00 00 01
rbb: 00 00 00 00 00 00 00 03
rbb: 00 00 00 00 00 00 00 31
rbb: 10 00 00 00 00 00 00 01
show_rbb: RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
show_rbb: IIRRIIIR