Compare commits

...

869 Commits
0.5.1 ... 1.0.0

Author SHA1 Message Date
Pau Espin Pedrol
11f72dfbcb Bump version: 0.9.0.280-56b7c-dirty → 1.0.0
Change-Id: I38b083755e71eac5158e68ef958e210eeced9038
2021-11-16 16:47:29 +01:00
Pau Espin Pedrol
56b7c64298 Move T3172 T_defs_bts->T_defs_pcu to have it configurable in VTY
The timers in T_defs_bts are actually set by BTS over PCUIF. That's not
the case for T3172, hence let's move it to T_defs_pcu so it can be
configured over VTY.

Change-Id: If24191b2305007aa5be5b551c913738a97597c77
2021-11-16 11:35:23 +01:00
Pau Espin Pedrol
683ce64039 T_defs_pcu: Set default val for X2000 to 0 ms
That timer is really only useful to free the tbf asynchronously after
generating the Pkt Access Reject message, since we have nothing to do
with it after the message is sent, and the dummt TBF doesn't really hold
any reserved resource such as USF or TFI.
The timer is useful to still do the freeing asyncrhonously, since the
scheduler is interacting with the TBF during the code path, but there's
no real need to keep the object alive for 2 ms afterwards. Having a
default value of 0 ms is enough, since it fullfills the requirement
of freeing asnchronously.

The value of 2 ms was set initially when the reject support was added
here (e9a138e111), with no specific
explanation on the 2 ms value. It was just probably picked as a
convinience one, but 0 is actually more convinient.

Change-Id: I60e34e643f5c9d9afaf85530c54ab3232dc8f0be
2021-11-15 17:10:55 +00:00
Pau Espin Pedrol
e30153ea10 tbf_dl_ass_fsm: Drop unsued X2000 timer callback
That timer is only relevant for transmission of Packet Access Reject,
which happens only for Uplink assignment, and hence is only set in the
timer of tbf_ul_ass_fsm, never in tbf_dl_ass_fsm. This is probably a
copy-paste artifact when implementing both FSMs.

Change-Id: I95900e211eddb280c72fb712ba1da4d2230cb77b
2021-11-15 17:10:55 +00:00
Pau Espin Pedrol
0dcbc07682 bts: Add counter availablePDCHAllocatedTime
We basically want to probe whether it's possible to allocate TBFs, or
whether we know it will fail due to all main resources being already in
use (TFI, USF).

Having bts_all_pdch_allocated() return false doesn't mean though that an
MS will be able to allocate a TBF for sure. That's because further
restrictions are applied based on MS: whether it was already attached to
a specific TRX, whether the ms_class allows for a certain multislot
combination, etc. However, it should provide a general idea on whether
for sure the PCU is unable to provide more allocations. More fine
grained state about failures can still be followed by looking at
tbf:alloc:failed:* rate counters.

Related: SYS#4878
Depends: Iabb17a08e6e1a86f168cdb008fba05ecd4776bdd (libosmocore)
Change-Id: Ie0f0c451558817bddc3fe1a0f0df531f14c9f1d3
2021-11-15 11:40:07 +01:00
Pau Espin Pedrol
5deac1404d Fix MS ending up with assigned imsi 000
The whole paging path and data structre is cleaned up.
New MS helpers ms_imsi_is_valid() and ms_paging_group() are introduced
to help in the process and keep implementation details inside GprsMs
class.

Related: OS#5303
Change-Id: I4c0838b26ede58e4b711410eee2a8e4f71e9414b
2021-11-12 18:38:43 +01:00
Pau Espin Pedrol
13866f02a2 vty: Introduce command 'gsmtap-remote-host' and 'gsmtap-category enable-all'
Related: OS#5306
Change-Id: Ibc6f78c46831b3c90ee3e97300fc13dc441df4c8
2021-11-12 17:01:28 +01:00
Pau Espin Pedrol
afd9393a2d pcu_main: Mark -r cmdline param as deprecated
We have VTY support for it enabled in osmo-pcu since a while ago. Let's
mark it as depcreated now so that we can drop this cmdline arg in the
future.

Change-Id: Ic39c94984cb311aac0464d69f564af6d5447bd0f
2021-11-12 17:00:34 +01:00
Pau Espin Pedrol
17bfbedbc7 tbf_{dl,ul}_ass_fsm.c: use proper macro to log tbf
TBF can be either UL or DL in that FSM.

Change-Id: Ief0cd5298e062f11b0f39716162a67b87c9ff35f
2021-11-12 13:14:19 +01:00
Pau Espin Pedrol
bd1b90f141 tbf_dl_ass_fsm: Fix missing transition to NONE if DL TBF is nonexistent
If by the time the PktDlAss is to be scheduled by the scheduler the DL
TBF is gone, the FSM will abort the assignment and go back to state
NONE.
However, the transition was missing, ending up in the scheduler trying
to schedule the message unsuccessfuly lots of times per second, clogging
the logs and disrupting normal operation.

Related: OS#5293
Change-Id: I6f421e5ddc9894fee72de1102df35a76cf2f2647
2021-11-12 12:03:00 +00:00
Pau Espin Pedrol
84f2b51a37 tbf_dl_ass_fsm: Log both TBFs if old TBF is handling assignment for new one
Change-Id: I4ec4eb2ca3f4fa576a4f9ada8c2462f59ca078b9
2021-11-12 12:03:00 +00:00
Pau Espin Pedrol
dbdf84eaff cosmetic: gprs_pcu.h: Fix typo in comment
Change-Id: I7140decb50e822bf412fa380dc8b6ef6486a5681
2021-11-11 14:11:40 +01:00
Pau Espin Pedrol
7eb9e69506 tbf_ul_ass_fsm: Log both TBFs if old TBF is handling assignment for new one
Change-Id: If475560aab16b0a89743139189ff7720389132a5
2021-11-10 17:54:38 +01:00
Pau Espin Pedrol
715aeb4ebc pdch: Increase log level of line informing about TS control change
This way it's the same log level as the one used in all other paths
using tbf_assign_control_ts() to assign tbf->control_ts =
tbf->first_common_ts, and this specific event is not lost from logs.

Change-Id: Ia32d835ee4c14d7d48391452b5e3d05ed88e0483
2021-11-10 17:21:43 +01:00
Pau Espin Pedrol
ace3b1bdc1 pdch: Drop previous UL TBF from MS who sent PktResReq through SBA
If the MS has a pending UL TBF but we just received a PktResReq on an
allocated SBA from it (same TLLI, hence same MS), then it means it
allocated the SBA through RACH req and hence it was on CCCH. That means
it was not active on any PDCH, hence for sure the previous UL TBF can be
dropped.

Related: OS#5293

Change-Id: I1f20dba56f46ea15cbb9b03bdc5b79d923491a3c
2021-11-10 16:11:19 +01:00
Pau Espin Pedrol
54a126f6d4 pdch: Update ms_reserved_slots in GprsMS when TS becomes disabled
Otherwise, after the TS is disabled, a new TBF created for that MS may
end up in alloc_algorithm assigning the disabled TS, since it will be in
the mask of reserved PDCH TS for that MS.

Related: OS#5265
Change-Id: Ifc59ac37fa6b0ad9ecc8f76326928611e748b11c
2021-11-10 11:36:55 +01:00
Pau Espin Pedrol
853cdf85eb ts_alloc: rename variable to clarify meaning
The variable counts reserved slots, not available one. It can easily be
seen by checking function count_slots() documentation "Number of
reserved TS".

The previous naming was used probably to indicate "available to reserve"
TS, but the naming is misleading.

Change-Id: Ib58e87d5a067d20d0b331fe32dff61b95ecc3e3f
2021-11-10 11:36:55 +01:00
Pau Espin Pedrol
978071bce9 ts_alloc: rename function to clraify what it does
The function updates nothing, so it's misleading. It simply counts
slots, so let's call it like that.

Change-Id: I55954321d6f2b5e755177a8829512da371e934aa
2021-11-10 11:36:55 +01:00
Pau Espin Pedrol
d9066272ec ts_alloc: Simplify tfi_find_free logic
Avoid passing an extra pram which is accessible by GprsMs object already
being passed. Once, the "trx = ms_current_trx(ms)" is moved inside the
tfi_find_free function, it becomes clear that all the logic can be
further simplified.

Change-Id: I733d9bee3fa1dfc647da9f150b30014fbdab7442
2021-11-10 11:36:14 +01:00
Pau Espin Pedrol
fb904fbbd9 pdch: Log DL TBF originating the new UL TBF
Change-Id: Ie4257a2468d589464d0d56882e4408e4bcfe340f
2021-11-09 14:01:40 +01:00
Pau Espin Pedrol
304b10a8b5 pdch: Log TS enable/disable transitions
Change-Id: I6780634de4791382ccd25bf7b74f5286420e2c52
2021-11-09 12:22:48 +01:00
Pau Espin Pedrol
20dfa54508 pdch: Log line detaching TBF at start of the function
Log before the action is done ("Detaching", not "Detached"). This way,
if something crashes we see that last line.

Change-Id: I6811d3772e2ac850741d3db5ec32be5c0812e81a
2021-11-09 12:15:23 +01:00
Pau Espin Pedrol
ef8a730f6d bts_pch_timer: Avoid resend Paging Request over PCUIF if T3113 is armed
Let's avoid flooding the BTS and taking CCCH resources for no good
reason. If user configures everything correctly, the SGSN should not
attempt a retry after similar timer >= T3113.

Related: OS#5297
Change-Id: I2a77714648d16ccff2a340ce775e83dcc5ffe707
2021-11-08 18:54:12 +00:00
Pau Espin Pedrol
19b3392166 tests/alloc: Extend test_bts_pch_timer() to validate MI type TMSI
Change-Id: I3673d387fa735b54fbc137ffaa18af581fa1b85a
2021-11-08 18:54:12 +00:00
Pau Espin Pedrol
13961c9b7a bts_pch_timer: Fix timer working only for MI type IMSI
This commit actually addresses 2 errors:

1- gprs_bssgp_pcu_rx_paging_ps() called gprs_rlcmac_paging_request()
with MI which can be either TMSI or IMSI, and the later always called
bts_pch_timer_start() passing mi->imsi regardless of the MI type. Hence,
trash was being accessed & stored into bts_pch_timer structures if MI
type used for paging was TMSI.

2- When the MS received the PS paging on CCCH and requests an UL TBF, it
will send some data. If one phase access is used for whatever reason,
the IMSI may not be yet available in the GprsMs object since we never
received it (and we'd only have it by means of PktResourceReq). Hence,
let's better first try to match the paging by TLLI/TMSI if set in both
places, and otherwise use the IMSI.

Related: OS#5297
Change-Id: Iedffb7c6978a3faf0fc26ce2181dde9791a8b6f4
2021-11-08 18:54:12 +00:00
Pau Espin Pedrol
f8a93cb882 doc: Update counters_generated.adoc using osmo_vty_interact.py
osmo_interact_vty.py -c 'enable;show asciidoc counters' -p 4240 -H 127.0.0.1 -O doc/manuals/chapters/counters_generated.adoc

Change-Id: I88e8e5548876fd6515e6bfcccec47bc48ba0ceb4
2021-11-08 18:33:04 +00:00
Pau Espin Pedrol
ba5683194a Add counter for successful contention resolution procedures
This counter is related to succPDTCHSeizures,
(3GPP TS 52.402 B.2.1.51 Successful PDTCH seizures).

The relevant event when the first RLC block on the PDCH from the MS is
received is the fact that contention resolution is considered as done in
the network side. Hence, name the counter that way to ease
interpretation.

Related: SYS#4878
Change-Id: I3d67e3e68907921b43f2ca4398ad9578c0b2618c
2021-11-08 18:33:04 +00:00
Pau Espin Pedrol
cc77eed9d9 tbf_ul: Improve documentation of tbf_alloc_ul_pacch()
Change-Id: I59493788b4a54610a70f3eb4c31fd05f6e39e63d
2021-11-08 18:33:04 +00:00
Pau Espin Pedrol
10b29153b7 pdch::rcv_resource_request(): Use local var to store bts pointer
Change-Id: Ica727c4b70cecc0ddb5d2a235bfc416735754b61
2021-11-08 18:33:04 +00:00
Pau Espin Pedrol
f61acd75c4 cosmetic: Add parenthesis around expression to clarify it
Change-Id: I621ef02868aff2bd23d82c8bc70e5cdbc391fbc2
2021-11-08 16:34:17 +01:00
Pau Espin Pedrol
dbd3b78a9b tbf_ul: Update FSM names for dummy reject TBFs
This allows easily identifying dummt TBFs created to send assignment
rejects.

Change-Id: I73a197795a9c8e9cd8dc06bf46ddb8f275d2c289
2021-11-08 16:33:51 +01:00
Pau Espin Pedrol
dff399fa42 bts: Add counters for successful 1,2 phase pkt access
These counters relate to succPDTCHAssProcsPerCause
(B.2.1.50 Successful Packet Channel Assignment Procedures, per cause).

Related: SYS#4878
Change-Id: I494afab337f2557ffa38e4c7ff2c15a1647a1e04
2021-11-08 15:26:49 +00:00
Pau Espin Pedrol
a02f945479 tbf: Set tfi to initial special value
This allows distinguishing when a TBF didn't set the TFI. Useful to
identify dummy reject TBFs, etc, and make sure a non-dummy TBF set its
TFI properly.

Change-Id: Iecf54a24041bd14f4ef5b86e57c3732e1b69d463
2021-11-08 13:24:20 +01:00
Pau Espin Pedrol
d3d46de278 tbf: Mark initial first_(common_)ts with special value
This way it's easier to distinguish when this value was not properly
filled when debugging or looking at logs.

Change-Id: I0c9c9fdcfca9eb15125ea49efcbb76711850052e
2021-11-08 13:24:20 +01:00
Pau Espin Pedrol
de0eeafd2e tbf: Set m_created_ts in constructor
This way the timestamp is also set for dummy reject TBFs. For other
TBFs, setup() is called immediatelly after calling the constructor, so
we are fine too.

Change-Id: I2966ec7f3f9161d528a173d94797b72d1398c747
2021-11-08 13:24:20 +01:00
Pau Espin Pedrol
063296883f tbf_ul: Set first_(common_)ts in handle_tbf_reject
Let's set them to match expectancies for this type of dummy TBFs, in
order to avoid acidental use/access of other timeslots to the one where
the reject was associated to.

Also use tbf_assign_control_ts() to log the TS used for the TBF, similar
to what's used in other places where control_ts is assigned.

Related: OS#5293
Change-Id: I32dcb29ad24519082b8665921efcce0b5a16d12e
2021-11-08 13:24:20 +01:00
Pau Espin Pedrol
92cbe4aee0 pdch: Improve log line and increase log level
Change-Id: Ie593331a69f6b8ec3b21e2b274a1aa060b2dc439
2021-11-08 13:24:19 +01:00
Pau Espin Pedrol
b6babc39dc tbf: Increase log level of line about unable to allocate poll for TBF
Change-Id: I0bd972d3b68017f12a0816a27162e3a409b1893a
2021-11-08 13:24:19 +01:00
Pau Espin Pedrol
48df600bfa bts: Count RACH Request with unexpected content
Change-Id: I86420b08a9a634ca2e1f5a1c7e66ec3d3c08ce0b
2021-11-08 12:23:31 +00:00
Pau Espin Pedrol
812a7d3fa3 bts: Improve logging to clarify RACH req is for 2 phase access
Change-Id: I047b688197a07e3592f19888f0ca71b9c3d2b3fd
2021-11-08 12:23:31 +00:00
Pau Espin Pedrol
769e28114f bts: Introduce new RACH req counters for one/two phase access
These new counters allow the user to find out which kind of access are
MS requesting.

Related: SYS#4878
Change-Id: Id87c3a53d3acee92499987c843130e358f54742c
2021-11-08 12:23:31 +00:00
Pau Espin Pedrol
bf129c1437 vty: show tbf: Drop unneeded check for non-null ms
Since a while a go, a TBF is guaranteed to always have a MS assigned.
Hence, there's no point in checking it.

Change-Id: I89e062432ac671c73731ce68c889aeb5e24277f5
2021-11-05 20:46:08 +01:00
Pau Espin Pedrol
14015124ed vty: Log tbf_state when showing a TBF
That's one of the most important information bits about a TBF when
debugging, and it's not shown currently, only when "show ms" is used.

Change-Id: I98e3c9cac4ca6fc29695768ecc6e0444e618b945
2021-11-05 20:44:49 +01:00
Pau Espin Pedrol
43fc4a8690 vty: Avoid crash in tbf_print_vty_info with null ptr ctrg
Previous code did use a ctrg based on MS being EGPRS capable or not.
However, an MS being EGPRS capable doesn't mean necessarily that all its
TBFs are EGPRS, since we may known about the capability after we already
created some previous TBF, so it was not ugpraded. Hence, we were
sometimes accessing the wrong NULL ctrg.
Let's simply check for non NULL ctrg when deciding what to print.

"""
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7561ea6 in vty_out_rate_ctr_group (vty=vty@entry=0x897850, prefix=prefix@entry=0x4482cd " ", ctrg=0x0) at utils.c:82
82    utils.c: No such file or directory.
(gdb) bt
 #0  0x00007ffff7561ea6 in vty_out_rate_ctr_group (vty=vty@entry=0x897850, prefix=prefix@entry=0x4482cd " ", ctrg=0x0) at utils.c:82
 #1  0x000000000041437b in tbf_print_vty_info (vty=vty@entry=0x897850, tbf=0x3fb61f0) at pcu_vty_functions.cpp:98
 #2  0x0000000000414acc in pcu_vty_show_tbf_all (vty=vty@entry=0x897850, bts=bts@entry=0x7be650, flags=4294967295) at pcu_vty_functions.cpp:127
 #3  0x000000000041206f in show_tbf (self=<optimized out>, vty=0x897850, argc=<optimized out>, argv=0x7fffffffe040) at pcu_vty.c:1150
 #4  0x00007ffff755d167 in cmd_execute_command_real (vline=vline@entry=0x7bc300, vty=vty@entry=0x897850, cmd=<optimized out>) at command.c:2604
"""

Related: SYS#5689
Change-Id: I3979bfc12dd3b9a53b34b284537f271c356a3024
2021-11-05 20:44:39 +01:00
Pau Espin Pedrol
7ce56d7c64 bts: Rename 11bit RACH request counter
This way it fits better the structure where the general one counts all
rachs, and 11bit only the 11 bits. More per-type splitting will be done
in follow-up commits where new types are added.

Change-Id: Ibdfb10dcc65d71e98e2fe8b05001cafea786f071
2021-11-02 16:47:08 +01:00
Pau Espin Pedrol
858f038c1c tbf_ul: Document context where tbf_alloc_ul_ccch() is used
It can be seen that this function properly passes single=true to
tbf_alloc_ul_tbf().

Change-Id: Id83bfd78c88fa9e4fa98268cc726298c276e6f20
2021-11-02 16:45:42 +01:00
Pau Espin Pedrol
7e8d5ab4c4 bts: Fix misleading log line in bts_rcv_rach()
If it's not single block packets access, then it's one phase packet
access. TS 44.018 Table 9.1.8.1:
"""
One phase packet access with request for single timeslot uplink transmission;
one PDCH is needed.
"""

Change-Id: Ic6beb6dcfebb77fd264b179b028f99a29c644fb1
2021-11-02 14:08:15 +01:00
Daniel Willmann
dd28f82747 gprs_bssgp_pcu: Fix crash when configuring an existing ns bind
ns_configure_nse() only sets bind when it doesn't exist yet. If it
already exists bind[i] stays NULL and causes a segfault in
gprs_ns2_is_ip_bind() later on.
This patch ensures bind[i] is either created of set to the existing
bind.

Change-Id: I103e82e6c64324c087a4ff325a83eeab0e5a4ee9
Related: SYS#4971
2021-10-29 17:34:24 +02:00
Pau Espin Pedrol
9ecdc11eb6 csn1_dec.c: Fix stored bit in CSN_NEXT_EXIST_LH
Fixup for previous patch, which forgot to update the pui using the new
variable, as done already in the same patch for M_NEXT_EXIST.

Change-Id: I92a04c708bcc6c15348324321e8890361bbc5c31
Fixes: 72cdb30ee2b1c0d71ff6d9583d51f46b2e5fdcea
2021-10-20 17:21:34 +02:00
Pau Espin Pedrol
1859ec38cc csn1: Avoid storing existence bit as true if content was actually NULL
If we decode Exist bit as "1" but we are at the end of the message, and
all the Next items we'd read are expected to be possibly NULL, then swap
the Exist bit in the decoded structure as "0" in order to tell the
decoder user that the related information structure is actually unset,
as if "0" was received.

Related: SYS#5552
Related: OS#4955
Related: OS#5020
Change-Id: I38602e4b680ed87297c7e440691a494c07cad446
2021-10-20 15:36:01 +02:00
Pau Espin Pedrol
ebdc0d8c17 csn1: Avoid failing if optional DownlinkDualCarrierCapability_r7 is missing
All additional release fields are considered optional, and the
CSN_DESCR for Content_t already marks almost all as such, except
DownlinkDualCarrierCapability_r7.

It has been found that some MS transmits a MS RA Capability with a Length=61 bits
where the last bit in the buffer is setting the Exist bit for
DownlinkDualCarrierCapability_r7 as 1. Hence, the CSN1 decoder failed to
decode the whole message because it expected to keep reading there
despite there's no more bytes to read.

While this is could actually be considered an MS bug, let's relax our
expectancies and simply consider the case { 1 <end> } as it was { 0 },
and mark skip decoding DownlinkDualCarrierCapability_r7. That waht
wireshark (packet-gsm_a_gsm.c) or pycrate do for instance.

This patch itself doesn't fix the problem where actually the Exist bit
is stored as 1 in the output decoded structure, but simply allows keep
ongoing with decoding until the end. This issue will be fixed in a
follow-up patch.

Related: SYS#5552
Related: OS#4955
Related: OS#5020
Change-Id: I9a2541bd3544802a646890f32725201836abb0da
2021-10-20 15:36:01 +02:00
Pau Espin Pedrol
089d734cd1 csn1: Add unit test showing RadioAccess Capability decoding failure
This RA Cap creaes a decoding error on our CSN1 decoder, but seems to be
handled properly by wireshark's own decoder as well as pycrate.

The ending bit of last byte in "MS RA capability 1" has a "1" which
according to spec should flag the existance of
DownlinkDualCarrierCapability_r7, but nothing else comes after it. This
matches the expectancies as per Length field of the first RA Cap.

Related: SYS#5552
Related: OS#4955
Related: OS#5020
Change-Id: I51235e8575f4b992b44078713ec67bbccfd13293
2021-10-20 15:36:01 +02:00
Pau Espin Pedrol
c90e6f8de1 Split csn1.c into common, enc and dec files
The CSN1 encoder/decoder code is already lengthy and complex enough,
there's no need to keep it in the same file, specially because when
debugging, only is interested in one of the 2 functions, and they both
look really similar (long spaghetti switches).

Change-Id: I7d1b1f7e6d7f89b052b3fd73a960419bb2673020
2021-10-20 13:35:44 +00:00
Pau Espin Pedrol
fef3da24ae pcuif: Submit data_req with len=0 as idle frames
This way PCU always answers DATA.ind and the BTS can still clearly
identify idle frames. It also simplifies testing and verification of
correct behavior.

Related: SYS#4919
Change-Id: Ife718eeed2af011479c03099ea109518f04567bc
2021-10-20 13:35:10 +00:00
Pau Espin Pedrol
9f43c65c99 cosmetic: Fix typo in comment
Change-Id: I9e6d0963533e15e73fb51bed11af563a62b92ecb
2021-10-19 14:20:11 +02:00
Pau Espin Pedrol
affd6a7f7a tbf: Drop unneeded braces in one line condition
Change-Id: Ief5e0ea0b7146d3330a17d5f0d171755b576fca3
2021-10-18 16:44:19 +02:00
Pau Espin Pedrol
e50b5f1e3f tbf: update(): return negative val on error
Let's follow usual convention where errors are returned as negative
values.

Change-Id: Ib4f4dc37ae82ba8efdc212ed85af7934e16a8a8d
2021-10-18 16:13:51 +02:00
Pau Espin Pedrol
8a9eec345e tbf: Assert if update() is called on UL TBF
This function is expected to be used only on DL TBF so far, so let's
really assert to avoid going through if something is wrong and ending up
later with other issues.

Change-Id: If398ee48364fce5b5e38830b2b278b3bad9a48a2
2021-10-18 16:11:34 +02:00
Pau Espin Pedrol
89a995a439 tbf_fsm: Add assert verifying X2002 only triggers for DL TBF
Code above setting the timer in same tbf_fsm already has this kind of
assert, but it helps understanding the code having this assert here.

Change-Id: I7588deef5073694eb5fecdb516c241a04594e2b0
2021-10-18 15:36:58 +02:00
Pau Espin Pedrol
a2ef802dba tbf: Update FSM names when TFI change during tbf_update()
In that function, previous PDCHs are unlinked and then alloc_algorithm
is expected to assign new TFIs.

Change-Id: I7bcbb223ca32400bede7ab638695ba3c015c9946
2021-10-18 15:36:58 +02:00
Pau Espin Pedrol
77e2ff32d9 ts_alloc: Rename s/tbf_/tbf/
Off the top of my head: The tbf_ was kept during a previous refactoring
a while ago to avoid changing lots of more lines in the same patch.

Change-Id: I8ae689a272b7c4d244576ff157f6019a87041abc
2021-10-18 15:36:45 +02:00
Pau Espin Pedrol
9c84c88259 Get rid of tbf tsc field
TSC is not really a property of a TBF, so let's drop it in order to avoid
confusing and possible misuse of that accessor.

Change-Id: I105eb65d507e45631faddb23420c42bc9560e580
2021-10-18 15:35:51 +02:00
Pau Espin Pedrol
f5cb4acb14 bts_rcv_rach(): Split code paths for Ass and Ass Rej
The function becomes a bit more long but it's a lot easier to follow.

Change-Id: I80e554315d36a515a7edc9ae51057ce31eb9110d
2021-10-18 13:33:15 +02:00
Pau Espin Pedrol
9b9f5efb09 bts_rcv_rach(): Gather pointers to data objects early and use them later
Change-Id: I476814d0f7be4b53f66211bb472700fee4f2caa9
2021-10-18 13:20:32 +02:00
Pau Espin Pedrol
196f36b75a pdch: Log reason of expected POLL when receiving unexpected UL data
Change-Id: I914e38154029f57cbf38120495220cd860877c45
2021-10-18 12:37:51 +02:00
Pau Espin Pedrol
592239630b pdch: Simplify code path allocating UL TBF
There's no real need to pass a tlli per separate, the information is
already contained in the MS. Furthermore, when doing so, it becomes
clear the TLLI was only passed to set it again on the MS, so actually
that ms_update() can be totally dropped since it will act as a no-op.

Change-Id: Ie761c3c7c222458ab0514117ae637ad3267139a0
2021-10-18 12:24:42 +02:00
Pau Espin Pedrol
b0aba59143 tbf: Drop pending polls during free also on states != ASSIGN
The situation holds true as long as the assignment is resolved. Hence,
it can also happen that the TBF is in RELEASE state, because it was
unable to do the assignment (and after retrying, MAX_N3105 moved it into
RELEASING).
Let's not explicitly check states, the other conditions should be
enough.

Related: SYS#5647
Change-Id: I05fb0ea44aeb3fbda9e8e1c449e9366efaa2c511
2021-10-14 19:31:18 +02:00
Pau Espin Pedrol
b3291bc0e3 Abort scheduling of pending Pkt Ul Ass if tbf goes into RELEASE step
Change-Id: I20bab79070274b1d8f6b4e1867b30de61983ab54
2021-10-14 19:31:18 +02:00
Pau Espin Pedrol
880cbd3a8f tbf_ul_ass_fsm: Avoid retrying Pkt Ul Ass if tbf is not in state ASSIGN
It doesn't make sense to keep asking the scheduler to retransmit Pkt Ul
Ass if the tbf_fsm already decided we are going to release the TBF.

It can be seen in following log extract:
"""
tbf_ul_ass_fsm.c:112 TBF(TFI=1 TLLI=0xe1c12303 DIR=UL STATE=ASSIGN EGPRS) start Packet Uplink Assignment (PACCH)
tbf_ul_ass_fsm.c:131 TBF(TFI=1 TLLI=0xe1c12303 DIR=UL STATE=ASSIGN EGPRS) Scheduled UL Assignment polling on PACCH (FN=612941, TS=7)
tbf_ul_ass_fsm.c:188 UL_ASS_TBF(UL-TFI_1)[849e50]{SEND_ASS}: state_chg to WAIT_ACK
pdch_ul_controller.c:330 PDCH(bts=0,trx=1,ts=7) Timeout for registered POLL (FN=612937, reason=DL_ASS): TBF(TFI=1 TLLI=0xe1c12303 DIR=UL STATE=ASSIGN EGPRS)
tbf.cpp:550 TBF(TFI=1 TLLI=0xe1c12303 DIR=UL STATE=ASSIGN EGPRS) poll timeout for FN=612937, TS=7 (curr FN 612937)
tbf.cpp:392 TBF(TFI=1 TLLI=0xe1c12303 DIR=UL STATE=ASSIGN EGPRS) N3105 exceeded MAX (8)
tbf.cpp:602 TBF(UL-TFI_1)[7bd530]{ASSIGN}: Received Event MAX_N3105
tbf_fsm.c:194 TBF(UL-TFI_1)[7bd530]{ASSIGN}: state_chg to RELEASING
pdch_ul_controller.c:330 PDCH(bts=0,trx=1,ts=7) Timeout for registered POLL (FN=612941, reason=UL_ASS): TBF(TFI=1 TLLI=0xe1c12303 DIR=UL STATE=RELEASING EGPRS)
tbf.cpp:550 TBF(TFI=1 TLLI=0xe1c12303 DIR=UL STATE=RELEASING EGPRS) poll timeout for FN=612941, TS=7 (curr FN 612941)
tbf.cpp:589 UL_ASS_TBF(UL-TFI_1)[849e50]{WAIT_ACK}: Received Event ASS_POLL_TIMEOUT
tbf_ul_ass_fsm.c:224 TBF(TFI=1 TLLI=0xe1c12303 DIR=UL STATE=RELEASING EGPRS) Timeout for polling PACKET CONTROL ACK for PACKET UPLINK ASSIGNMENT: |Assignment was on PACCH|No uplink data received yet|
tbf_ul_ass_fsm.c:226 UL_ASS_TBF(UL-TFI_1)[849e50]{WAIT_ACK}: state_chg to SEND_ASS
tbf_ul_ass_fsm.c:308 UL_ASS_TBF(UL-TFI_1)[849e50]{SEND_ASS}: Received Event CREATE_RLCMAC_MSG
tbf_ul_ass_fsm.c:112 TBF(TFI=1 TLLI=0xe1c12303 DIR=UL STATE=RELEASING EGPRS) start Packet Uplink Assignment (PACCH)
tbf_ul_ass_fsm.c:131 TBF(TFI=1 TLLI=0xe1c12303 DIR=UL STATE=RELEASING EGPRS) Scheduled UL Assignment polling on PACCH (FN=612976, TS=7)
tbf_ul_ass_fsm.c:188 UL_ASS_TBF(UL-TFI_1)[849e50]{SEND_ASS}: state_chg to WAIT_ACK
"""

Change-Id: I94243ff99dfaf3664a1a4b3c4c87b5104ba4f7d1
2021-10-14 19:31:12 +02:00
Pau Espin Pedrol
b9fede74ef tbf: Avoid keeping poll nodes in pdch_ulc of temporary control_ts used during PACCH assignment
When MS sends us the Packet Resource Request as RRBP from final UL ACK/NACK, we create a new TBF
with a different set of allocated TS. However, we must send the Pkt UL Assignment with information
of the new TBF using that same TS where we receive the Packet Resource Request, which happens to
be the control TS of the previous/old TBF. The original control TS of the new TBF is kept in
tbf->first_common_ts.

Hence the code does gprs_rlcmac_pdch::rcv_resource_request():
"""
ul_tbf->control_ts = ts_no;
"""

And later, when we receive a CTRL ACK answering the Pkt UL Assigment, we change the control TS of
the new TBF back to the new one, by calling tbf_assign_control_ts(), which basically does:
"""
tbf->control_ts = tbf->first_common_ts;
"""

So, for instance we have a TBF which was allocated with tbf->control_ts=4 and hence is only attached
to PDCH 4 (tbf->pdch[]), but for which is temporarily applied tbf->control_ts=7.  Hence, when a poll
is requested, it is done in control_ts, aka 7, which is not in the array of attached PDCH.

The problem is of course if we never reach the point where the final control_ts is set, due to never
receiving the CTRL ACK. If the TBF is freed (due to timer X2001) before receiving the CTRL ACK and
hence tbf_assign_control_ts() is called, a crash may occur, because potentially a poll for the TBF is
left in TS 7 because it's not a PDCH attached to the TBF and hence poll
entries on that TS are not released, hence keeping a pointer to the
freed TBF.

Related: SYS#5647
Change-Id: I0c49f2695e0d932d956c01976c9458eebee63cd4
2021-10-12 20:07:19 +02:00
Pau Espin Pedrol
ffa2918bc5 pdch: rcv_data_block: Avoid releasing ULC entry if expecting something else on UL
Let's only release PDCH ULC entry if it was indeed what we expected.
In other case, time it out.

Change-Id: I1537587f5ee801c633784691b576ebb1ed521e95
2021-10-12 20:07:19 +02:00
Pau Espin Pedrol
32744c8916 Return void in tbf_assign_control_ts()
This operation cannot fail, hence let's simplify code.

Change-Id: I5675df4b6309d680d1d5e2dbea87991468f30630
2021-10-12 19:36:49 +02:00
Pau Espin Pedrol
ffe998ce9d tbf: Document temporary change of control_ts and move code assigning it back to FSM
Change-Id: I1b7eb7802060778487e5729ee789b2323b6636f8
2021-10-12 19:32:28 +02:00
Pau Espin Pedrol
38a9c873bc tbf: Use define to flag control_ts unset special value
Change-Id: Idd3ccec509b40b6229544b45e54da1142805b6f9
2021-10-12 19:18:57 +02:00
Pau Espin Pedrol
85aa87b61f tbf_fsm: Handle MAX_N3105 in state ASSIGN
Seen on a runnig osmo-pcu against real MS:
"""
pdch_ul_controller.c:329 PDCH(bts=0,trx=1,ts=7) Timeout for registered POLL (FN=751140): TBF(TFI=0 TLLI=0xe8c12143 DIR=UL STATE=ASSIGN EGPRS)
tbf.cpp:542 TBF(TFI=0 TLLI=0xe8c12143 DIR=UL STATE=ASSIGN EGPRS) poll timeout for FN=751140, TS=7 (curr FN 751140)
tbf.cpp:384 TBF(TFI=0 TLLI=0xe8c12143 DIR=UL STATE=ASSIGN EGPRS) N3105 exceeded MAX (8)
tbf.cpp:594 TBF(UL-TFI_0)[9bc050]{ASSIGN}: Received Event MAX_N3105
tbf.cpp:594 TBF(UL-TFI_0)[9bc050]{ASSIGN}: Event MAX_N3105 not permitted
"""

It was first though when FSMs where introduced that an FSM in ASSIGN
state could not receive this kind of event because it was believed to be
sending no CTRL blocks at all until flow state. That's because the
believe was that Assignment over PACCH was done by another existing TBF.
It turns out this is usually the case, but not in all cases. In at least
one case, the tbf object (and tbf_fsm/tbf_{ul,dl}_ass_fsm) itself is
handling its own assignment (hence eg. sending the UL assignment and waiting
response through tbf_ul_ass_fsm. This happens if a UL TBF sends a Pkt
Resource Req as a response to RRBP of final UL ACK/NACK in order to
request a new TBF, where it temporarily uses the control_ts of the
previous TBF to get a new Pkt UL Assignment over PACCH.

If Pkt Ul Assignment doesn't receive a CTRL ACK, tbf_ul_ass_fsm will
retrnamist it, until MAX_N3015 is reached (the event we failed to
handle until now). At this point, we really want to transition to
RELEASING in order to avoid keeping the TBF allocating resources (until
X2001 times out).

Related: SYS#5647
Change-Id: I86d5c1bbccd06673d08451b812d149e727404733
2021-10-12 18:57:44 +02:00
Pau Espin Pedrol
27a4e7371c tbf_ul_ass_fsm: Fix use of incorrect log macro
Change-Id: I61e46199086a3e82985606cf81995e27663c91f5
2021-10-12 17:53:44 +02:00
Pau Espin Pedrol
78ddfbc413 tbf_dl_ass_fsm: Move block msg generation conditions to rts() function
Move the required conditions to generate a message to the rts()
function, this way the scheduler knows this TBF cannot yet attempt the
procedure and hence will not request it to create a message which will
fail.

This way the scheduler will schedule other itneresting messages instead
of failing and scheduling a dummy block as a result.

Change-Id: Idbe4f9bbd23005a43c586b737cf9adc2114287e2
2021-10-12 13:10:49 +02:00
Pau Espin Pedrol
4a1c561ce8 pdch_ulc: Log POLL reason upon timeout
Change-Id: I7cd59b60fe0af0bfdfcdf8a91e4cf8bd3f25b2f7
2021-10-12 11:01:07 +00:00
Pau Espin Pedrol
0043005afb tbf_fsm: rename state NULL -> NEW
This helps distinguishing the case where a TBF is in the initial state
and the unexpected case where osmo_fsm_inst_state_name reports "NULL"
due to fi pointer being NULL.

Change-Id: Ieaabfc9fa0dedb299bcf4541783cf80e366a88c3
2021-10-12 11:01:07 +00:00
Pau Espin Pedrol
7a2b65ed2c Handle Final UL ACK/NACK Confirmation in tbf_fsm
Pass the event over the FSM for better understading of the entire
lifecycle of the TBF.

Change-Id: If30d881037209d33b2b41ecf8bb8419caf36e367
2021-10-12 12:41:53 +02:00
Pau Espin Pedrol
201dbe04f7 pdch: PktResReq: Avoid releasing ULC entry if expecting something else on UL
Let's only release PDCH ULC entry if it was indeed what we expected.
In other case, time it out.

Move the case in the switch statement to the start to easy function
readibility (early return style).

Change-Id: I3d8749acca8e7859295d73cce556b2083169f726
2021-10-12 12:41:18 +02:00
Pau Espin Pedrol
8318d9c25d pdch: Validate poll reason matches in rcv_control_(egprs)_dl_ack_nack()
If we didn't expect this kind of UL messages according to pdch ULC, then
we shouldn't allow going forward and releasing the ULC entry: let it
time out instead so that TBF runs whatever appopiate action is needed in
this case, be it retransamission, releasing itself, etc.

Change-Id: I8ab3f5e4f2f802944269453db13a80c9ede67714
2021-10-11 18:22:01 +02:00
Pau Espin Pedrol
0dbf6f2a54 pdch: Only release ULC entry if rx ul block matches the expected one
If it doesn't match out expectancies, it means we early return and hence
don't push forward / update whatever state was requested upon receival
of the UL message for the expected TBF. Hence, we shall not remove the
allocated ULC entry: in this scenario we need to keep it so that timeout
procedure times out and the tbf applies whatever measures are required,
be it retransmission, releasing itself, etc.

Change-Id: Ia69a7d92c4b5c98ec71a75605c8dc3a755e63a35
2021-10-11 18:14:32 +02:00
Pau Espin Pedrol
e080808cfa sched: Rename function
Properly describe that function is aimed at selecting a DL data block.

Change-Id: Ic0680d15edf70449e66f40eab1ead97313631cbb
2021-10-11 17:42:35 +02:00
Pau Espin Pedrol
b5bad20731 tbf: Assert if FSM allocation fails
Change-Id: Ib3db7a554a4467814785df08e3772455bf00b7d5
2021-10-09 17:03:35 +02:00
Pau Espin Pedrol
abed2e326d rlcmac: Fix CSN1 definition for DownlinkDualCarrierCapability_r7_t in MS RA cap
Related spec: 3GPP TS 24.008 Table 10.5.146

Change-Id: I61b41e06b54024254c71242ffa2206e4eada8559
2021-10-07 20:27:50 +02:00
Pau Espin Pedrol
2282b50e5c tests: RLCMACTest: Add one more sample RA capabilities to suite
This one is larger than some of the other already available.
The decoder is wroking as expected here.

Change-Id: I5d986f68395326f894349446194090b1ddaecd69
2021-10-07 18:32:54 +02:00
Pau Espin Pedrol
cee6b122c2 tbf_fsm: Ignore event DL_ACKNACK_MISS in state RELEASING
Fixes following error log line:
"{RELEASING}: Event DL_ACKNACK_MISS not permitted"

Rationale: We may move to RELEASING state at some point, for instance
due to MAX_N3101/MAX_N3105 while still having some active poll
registered in some PDCH ulc. Upon that poll (most probably) timing out,
it will send a DL_ACKNACK_MISS event to us. Since we are already
determined to release the TBF (waiting for T3195 or T3169 to trigger),
simply ignore the event and avoid logging an error.

Fixes: OS#5240
Change-Id: Ibfb49356d2b3b5fccb6d59db8593b2256e5c51fb
2021-09-28 16:53:00 +02:00
Pau Espin Pedrol
e9db5c7387 assert if tbf pointer for POLL event is NULL
The tbf pointer should always point to a valid TBF.

Change-Id: Ib607a38459802f780826f46c20a1696ec98408fb
2021-09-28 16:38:30 +02:00
Pau Espin Pedrol
a27fb3fce3 cosmetic: Fix missing space
Change-Id: Ib64cbf1d95dd0a881f6ace0cf9a4f517eb58ae0f
2021-09-28 16:38:30 +02:00
Pau Espin Pedrol
c8d2166b86 pdch: refactor rcv_control_ack() with a switch statement
This clarifies the different paths and uniforms them. Makes code far
easier to read and debug.

New improved verification already found some misehavior in some tests.

Change-Id: I7e4a88d6e004bbb7974595320ed73742162c7ad7
2021-09-28 16:05:27 +02:00
Pau Espin Pedrol
422636d752 tests: TbfTest: Fix wrong behavior in test_tbf_dl_reuse()
The test uses get_poll_fn() to submit a UL ctrl block on the next
expected poll TS+FN.

During initial transmit_dl_data(), 2 POLLs for DL_ACK are set on
different TS, but the test only updates the clock for one of them in
send_ul_mac_block(). As a result, one POLL item is left in the rb_tree,
and later on, when send_control_ack() is called, get_poll_fn() will pick
that 2nd DL_ACK poll FN+TS which was left untouched (due to sending no
events to the PCU clock) instead of the FN+TS which was expected to be
allocated by PCU for DL_ASS.

Until now this was not an issue since rcv_control_ack() was not properly
veirfying what the poll scheduler was expecting and accepted it. This is
no longer the case after the follow up patch refactoring
rcv_control_ack() and improving its robustness.

Change-Id: I3a4b089fe66a99e73e07bd1c690cd4d67752fad9
2021-09-28 16:05:03 +02:00
Pau Espin Pedrol
d72f46f020 tbf: refactor poll_timeout() with a switch statement
This clarifies the different paths and uniforms them. Makes code far
easier to read and debug.

Change-Id: I4c56af70c79c20f1e600371e040bd48bcc908a75
2021-09-28 13:28:27 +02:00
Pau Espin Pedrol
c276e4996d nacc: Introduce helper function nacc_fsm_exp_ctrl_ack()
Move FSM internal state checks to its own file. Re-use the helper
function in the 2 places where same stuff is checked.

Change-Id: I9ded6e1c80e6cd7bcf6883bc2e853b6dafb33f7c
2021-09-28 12:53:37 +02:00
Pau Espin Pedrol
864a41496c tbf: poll_timeout(): Validate expected poll reason
Change-Id: I680b00fcb18a15a831ca13403c19162dadc67a2f
2021-09-28 12:41:02 +02:00
Pau Espin Pedrol
ea7cb48c9c tbf_ul_ass_fsm.c: Fix missing state transition in FSM description
As seen operating PCU after BTS restart, lots of following message
sequences due to FSM kept in same state (hence scheduler retyring every
time):
"""
DTBF tbf_ul_ass_fsm.c:306 UL_ASS_TBF(DL-TFI_0){SEND_ASS}: Received Event CREATE_RLCMAC_MSG
DTBF tbf_ul_ass_fsm.c:95 TBF(TFI=0 TLLI=0xf80bd801 DIR=DL STATE=RELEASING EGPRS) We have a schedule for uplink assignment, but there is no uplink TBF
DTBF tbf_ul_ass_fsm.c:97 UL_ASS_TBF(DL-TFI_0){SEND_ASS}: transition to state NONE not permitted!
DTBF tbf_ul_ass_fsm.c:306 UL_ASS_TBF(DL-TFI_0){SEND_ASS}: Received Event CREATE_RLCMAC_MSG
"""

Change-Id: I91d74f70a9106ccbf0c137b6e713877f9ea8f59d
2021-09-22 16:53:43 +02:00
Oliver Smith
fce67bc7fa pdch: has_gprs_only_tb_attached: use m_num_tbfs
Make use of the separate GPRS counters added in previous patch
I0c0a1121b4ae5f031782e7e63a0c28eb0b6c8b42 to shorten
has_gprs_only_tb_attached.

Related: SYS#4878
Change-Id: I1dd7df2c740ea604f07c65bebcb7c0051aebf9ae
2021-09-21 10:28:52 +00:00
Pau Espin Pedrol
eeae776345 PTCCH: skip Tx DL idle blocks when possible
Same was already done for PDTCH in previous commits. Let's now apply
same bits to PTCCH.

Related: SYS#4919
Change-Id: If6617964e67fc35eeee1791b06e13bf63ac88f73
2021-09-20 11:22:32 +02:00
Pau Espin Pedrol
0e3083daf5 scheduler: Skip Tx DL idle blocks in TRX0 when not in DIRECT_PHY mode
We also want to avoid sending idle blocs in TRX0 to the BTS, so that the
BTS can be aware of blocks being idle and then submitting dummy blokcs
by itself applying required BCCH Carrier power reduction.

Related: SYS#4919
Change-Id: Idd58d2a09c3947098b960cfcb5cd1b7b7bca3d84
2021-09-20 10:55:21 +02:00
Oliver Smith
402451b308 Add stats: pcu.bts.N.pdch.occupied.gprs/egprs
Add stats needed for performance measurements in
3GPP TS 52.402 § B.2.1.54-55.

Split m_num_tbfs to count GPRS and EGPRS TBFs separately. Move the code
that updates m_num_tbfs and sets the PDCH_OCCUPIED stats to a separate
function, as it's mostly the same in the TBF attach and detach.

Related: SYS#4878
Change-Id: I0c0a1121b4ae5f031782e7e63a0c28eb0b6c8b42
2021-09-17 17:28:03 +02:00
Pau Espin Pedrol
6c81adda45 nacc_fsm: Move logic checking if SI is being waited for to a func helper
We already have a similar function for Neighbor Address Resolution.
This way we keep as much as possible internal state related logic into
the nacc_fsm.c file.

Change-Id: I7378939825cc3ec3280f76bc51233c0a172d8a27
2021-09-13 13:31:09 +02:00
Pau Espin Pedrol
5557c0af80 Support Neighbor Address Resolution over PCUIF IPA multiplex
While NACC was initially developed, it became clear there was need for
a way to interact PCU<->BSC in order resolve ARFCN+BSIC into CGI-PS
for later RIM usage.
Hence, this resolution was first (until today) implemented using an out
of bands RPC system using the CTRL interface, which required specific
config to be written and matches in osmo-pcu and osmo-bsc VTY (ip+port
of the CTRL interface to use).
However, this has several shortcomings:
* As explained above, specific configuration is required
* Since recently, we do support BSC redundancy in osmo-bts. Hence the BTS
  may switch to a BSC other than first one. If that happened, that'd mean
  the CTRL interface would still point to the initially configured one,
  which may not be the same currently serving the PCU.

During recent development of ANR related features, a similar need for
PCU<->BSC was required, but this time it was decided to extend the IPA
multiplex of the Abis OML connection to pass PCUIF messages,
transparently forwarded to each side by the BTS.
This has the advantage that connection PCU<->BTS is handled by BTS and
both sides send messages transparently.

Let's switch by default to using this new interface, while still
maintaing the old way for a while (announcing them as deprecated) to
avoid breaking existing deployments until they are upgraded to new
versions of osmo-pcu and osmo-bsc.

Related: SYS#4971
Change-Id: I6ad33c7ab10202840cf804dea9ba595978d0e920
2021-09-13 13:31:06 +02:00
Oliver Smith
35d51ca4e3 Add stats: pcu.bts.N.pdch.available/occupied
Count available PDCHs (3GPP TS 52.402 § B.2.1.38) as well as occupied
PDCHs (§ B.2.1.42-44).

Related: SYS#4878
Change-Id: I74760a68ee055510a79e80854ec7bf1521669119
2021-09-06 09:11:47 +00:00
Pau Espin Pedrol
1d663d9277 pdch: Make sure pending ImmAssRej scheduled for disabled pdch are dropped
When a PDCH TS becomes disabled (eg due to dyn TS being used for a
call), we are currently freeing all attached PDCHs in order to avoid
further use of it. However, pdch_free_all_tbf() was only freeing TBFs
attached to the PDCH, that is, TBFs having a valid TFI assigned.
There are some cases where temporary dummy TBFs are created which have
no TFI assigned, such as when creating an ImmAssReject. Let's take those
into account too, and make sure they are freed.

Related: OS#5226
Change-Id: Ibfe78448ebdedc8b049c80664711e166d910f9b7
2021-09-03 16:01:37 +00:00
Oliver Smith
bf7bde1cbb debian/control: remove dh-systemd build-depend
Related: OS#5223
Change-Id: Ieb8669a9a43ea1acc6b2d8d2e363f2466c51697a
2021-09-01 16:07:06 +02:00
Pau Espin Pedrol
25d60cafc4 sched: Lower log level of RTS on disabled pdch
These messages are expected under some circumstances, such as when
direct phy is used and a chan is disabled (eg. dyn TS). This happens
because PCU asks for chan de-activation through PCUIF while at the same
time marking the PDCH locally as disabled. Hence, in the time the BTS
manages to disable it on the lower layers, the phy still sends us
RTS indications.

Let's keep it under NOTICE to avoid clogging the logs in production
setups which are usually using global level of NOTICE or ERROR.

Related: OS#5222
Change-Id: Iab9e1590b504bf05dc693e27550b30db0dffcbc7
2021-08-31 16:50:35 +02:00
Pau Espin Pedrol
0f88bcdebf bts: Use public getter instead of class member
Change-Id: Ia7b37a4c721d7d02c516d8d3a5417d166f1d3bec
2021-08-31 14:56:17 +02:00
Pau Espin Pedrol
767144f7b7 cosmetic: sysmo: Drop unneded comment line
Change-Id: I66685fa69af5ad18e45d97a25e7150815beef805
2021-08-31 14:53:10 +02:00
Pau Espin Pedrol
e376fd57cf Use LOGPDCH macro to standarize log line
Change-Id: If815779b2e2e56707f36c72dbdbfd4c5b07165ed
2021-08-31 14:38:21 +02:00
Pau Espin Pedrol
3bbb3cc1f2 Fix crash with dyn TS when using direct pcu
It seems there may be a race conditon where lower layers (direct PCU)
send UL blocks to us while the PDCH was already disabled (due to a call
entering on a dynamic TS).
As the PDCH is disabled, the ULC is NULL and shouldn't be used before
being enabled again.

Related: OS#5222
Change-Id: I4b8931f0cc7cfc787a1cc35196295402524b15c3
2021-08-31 14:36:02 +02:00
Pau Espin Pedrol
a89a23881f sched: energy saving: Avoid Tx dummy blocks on empty PDCH TS
Related: SYS#4919
Related: OS#4772
Change-Id: I8d66dd5e838748611e7b77b504fc86295f02c019
2021-08-26 12:51:15 +02:00
Pau Espin Pedrol
c4fe1f97b4 cosmetic: Fix typo in comment
Change-Id: I423410416e572141fc2b44c81215b8f41c13f2c6
2021-08-25 14:35:27 +02:00
Pau Espin Pedrol
d06ec27856 fix typo 's/dowlink/downlink/g'
Change-Id: Iae66aff9eed3856f09e58116ee26ec061733b076
2021-08-23 17:14:23 +02:00
Pau Espin Pedrol
f48de627f4 tbf: Move T3193 to tbf_state FSM
Related: OS#2709
Change-Id: Icf8249651e34132eb7ba99188a23662dec6f8653
2021-08-23 17:14:23 +02:00
Pau Espin Pedrol
a161bf48bd Simplify tbf::set_polling()
When setting a POLL, it will always happen on PACCH, so all the CCCH
part makes no sense there. Let's drop it and move the logging of each
case to the caller, where logging file+line is more useful.

Change-Id: I242f97fd6f927131ac64c1a7c9c3812b6389de04
2021-08-23 17:14:23 +02:00
Pau Espin Pedrol
ea8dbddab1 Move tbf ul_ack_state to osmocom FSM
Related: OS#2709
Change-Id: Icf23bf5a4b85fbcbf1542cebceb76b9ba7185d30
2021-08-23 17:14:23 +02:00
Pau Espin Pedrol
3e48cfd9f3 tbf.h: Improve documentation on several flags
Change-Id: Ice2c164ced039fb4ab621d8f7c2fb85f8348788a
2021-08-23 17:14:23 +02:00
Pau Espin Pedrol
405d2d10ec tbf_dl: Clarify requirements for DL ACK/NACK
Method is renamed since it clearly relates to getting DL ACK/NACK, no
CTRL ACK.

use same methods in both scheduler and internal use since they are
expectd to be run in the same code path by the scheduler. This way we
make sure the same conditions apply and it's clearer when looking at
the code.

Change-Id: Ib0e9b9547f5292b95064bab2dc182fdf659f0518
2021-08-23 17:14:23 +02:00
Pau Espin Pedrol
3225290d77 Move timer X2002 to tbf_fsm
Related: OS#2709
Change-Id: I94b71c60ed49d51ebdf6d6b428056b4b94354676
2021-08-23 17:14:23 +02:00
Pau Espin Pedrol
907f037339 tbf: Use type bool for upgrade_to_multislot
Change-Id: I644d91b6230a90cc72e83443c11d24b8d0a2dcac
2021-08-23 17:14:23 +02:00
Pau Espin Pedrol
628d881247 Fix typos in comments documenting fsm st chg macro
Change-Id: I8f1cef5810c84441f7d6d2fbe5b3106e0ae71b69
2021-08-23 17:14:23 +02:00
Pau Espin Pedrol
fbc1baa139 tbf: Merge handle_ack_nack() into rcvd_dl_ack()
There's no real use in having those 2 methods separately, and only adds
complexity. Let's merge it to have 1 TBF code path handling DL ACK/NACK.

Change-Id: I546d2e46bda96a2f551b28673464e57831c71828
2021-08-23 17:14:23 +02:00
Pau Espin Pedrol
afe189e802 Get rid of lots of code only used by tests
There are 2 methods "rcvd_dl_ack()" in osmo-pcu code. One is used by
osmo-pcu itself, and the other is only used in tests.
Changing the tests to use the same method as osmo-pcu allows removing
the second one, and with it, a lot of code and complexity out of
osmo-pcu.

Change-Id: I14d9312cb61534dc97fca83141b9c0cd933c9206
2021-08-23 17:14:23 +02:00
Pau Espin Pedrol
9d67e72e85 Move timer X2001 to tbf_fsm
The side effect is that the timer is enabled for other scenarios where a
PACCH assignment happens, like an Assignment Reject or Ul Assignment
(that's why there's more lines showing up now in TbfTest.err).

Change-Id: Ib8ab2f7397ad05c6fcd5dd74af55a1e2c56e1463
2021-08-23 17:14:22 +02:00
Pau Espin Pedrol
c65c9e56e1 tbf: Drop unuseful flag GPRS_RLCMAC_FLAG_UL_DATA
Same information is available under ul_tbf->m_rx_counter.

Change-Id: I1d993117c7daa2609b132c2d0fd748e0338ef559
2021-08-23 17:14:22 +02:00
Pau Espin Pedrol
5bc6560efc tbf: Drop unuseful flag GPRS_RLCMAC_FLAG_TO_DL_ASS
The flag is only used to print some non interesting stuff, let's drop it
in order to simplify code. We can add later whatever we want in the new
shiny FSM.

Change-Id: I13f92f058c219f230d57b3c00b8ae1d187603813
2021-08-23 17:14:22 +02:00
Pau Espin Pedrol
49a2f404e8 replace dl_ass_state with osmocom FSM
Related: OS#2709
Change-Id: Ia33418478e17986a316ffda48b091030f53fa371
2021-08-23 17:14:22 +02:00
Pau Espin Pedrol
432d4f3b89 tbf: Drop unuseful flag GPRS_RLCMAC_FLAG_TO_UL_ASS
The flag is only used to print some uninteresting stuff, let's drop it
in order to simplify code. We can add later whatever we want in the new
shiny FSM.

Change-Id: I20aa7f83cc4f32de129e64c74a91745b983a7b16
2021-08-23 17:14:22 +02:00
Pau Espin Pedrol
ab8fba3a20 tbf: Reimplement rlcmac_diag() and make it available from C
We never use the std:string anyway, we always call .c_str() to log using
osmocom logging system.
Furthermore, we'll need to use it from C code soon (next commit).

Change-Id: I3ad66f9f3f4d55d11da3a3b8b38656ae2dd50603
2021-08-23 17:14:22 +02:00
Pau Espin Pedrol
6ad11a6990 Replace ul_ass_state with osmocom FSM
Related: OS#2709
Change-Id: Id414eafe9c04a9a8759c6fb1a483bf2ee093a4d2
2021-08-23 17:14:22 +02:00
Pau Espin Pedrol
b0ead922a1 tbf_free: Get rid of uneeded tbf_state transition
We are freeing the object immediately afterwards anyway, so no need to
pretend it went through the normal state release.
Leaving current state as it is actually provides more information on
what was the status/state at the time the TBF had to be freed.

Change-Id: I3016caaccc2c43e1e300f3c6042d69f8adcd9d69
2021-08-23 17:14:22 +02:00
Pau Espin Pedrol
284711d627 Get rid of tbf_dl:abort()
Having that code in a separate function is confusing and adds code
complexity since it looks like an entry point to start feeing a TBF, but
it simply some (not yet really useful) set of instructions to be called
one 1 code path in tbf_free.
Let's move it there, this way it becomes clear tbf_free() is THE place
to be (if you want to get rid of a TBF).

Change-Id: I30febf4d21a0bfab37524c07598bbb0dd32f7f65
2021-08-23 17:14:22 +02:00
Pau Espin Pedrol
131deb059f Move rate_ctr free to tbf subclass destructor
This way we clean up tbf_free entry point, and leave memory freeing for
later on at the end when talloc_free is called.

Change-Id: I1c45e3296e565725bcbbca391d9518772fffa89d
2021-08-23 17:14:22 +02:00
Pau Espin Pedrol
9dacf0b35b Remove duplicate call to gprs_rlcmac_lost_rep
Function is already called by gprs_rlcmac_received_lost(), so next call
following it will be sum=0 and return EINVAL.

Change-Id: I015ba16d18fdd6e2441ec3c256b5ac88771d7a8b
2021-08-23 17:14:22 +02:00
Pau Espin Pedrol
8c4f978483 Drop logging last mas report before freeing TBF
There's no much use in logging it since anyway we are immediately
getting rid of it.

Change-Id: I9b712f720b5874886cc19d998fb8fcd0e618d590
2021-08-23 17:14:21 +02:00
Pau Espin Pedrol
62e06f92e9 Put dl_tbf::cleanup into destructor
It's fine to always attemt dropping the timer since it's set up in the
constructor.
This also drps the double function call abort()+cleanup() which is
confusing.

Change-Id: Ia2aaa43bd8faacf09fe4b36b11b38022bea7a59c
2021-08-23 17:14:21 +02:00
Pau Espin Pedrol
f45ede640b Drop duplicate log line
Same line (or similar if run_diag) is logged immediately below, showing
up twice in log which is confusing:

"""
20210726171543005 DTBF tbf.cpp:455 TBF(TFI=2 TLLI=0xfe563576 DIR=DL STATE=WAIT_RELEASE EGPRS) T3193 timeout expired, freeing TBF
20210726171543005 DTBF tbf.cpp:462 TBF(TFI=2 TLLI=0xfe563576 DIR=DL STATE=WAIT_RELEASE EGPRS) T3193 timeout expired, freeing TBF
"""

Change-Id: Ie171c458e670f8471ac93f78520a05926114c974
2021-08-23 17:14:21 +02:00
Pau Espin Pedrol
cfb61d9536 Move T3169 and T3195 to tbf_fsm
Change-Id: I599f4e7e82b0a8c0f5cf633c2d8b1975435f0b60
2021-08-23 17:14:21 +02:00
Pau Espin Pedrol
55f600b702 Move RELEASING tbf_state transition to tbf_fsm
PdchUlcTest output changes because the original state NULL is not
expected when transactioning to RELEASING upon MAX N310* being hit. In
any case, none of those events should happen in NULL state, but we
don't really care about TBF states there so we are fine with whatever
the state is.

Related: OS#2709
Change-Id: I516b8d989a0d705e5664f8aeaf7d108e0105aa16
2021-08-23 17:14:21 +02:00
Pau Espin Pedrol
efcb046ce1 Move WAIT_RELEASE tbf_state transition to tbf_fsm
While at it, method maybe_start_new_window is renamed to
rcvd_dl_final_ack to make more sense out of the code.

Related: OS#2709
Change-Id: Iebd650c1036ef2d5132789778be7117ce3391c01
2021-08-23 17:14:21 +02:00
Pau Espin Pedrol
c32c4a3648 Move FINISHED tbf_state transition to tbf_fsm
Related: OS#2709
Change-Id: I81f507e3a2821254f03364a58ead02333e63099f
2021-08-23 17:14:21 +02:00
Pau Espin Pedrol
65bba93afa tests: tbf: Fix dl_tbf polled for data without being in FLOW state
Prior code was wrong, as in it did stuff diferent to what is expected
and actually done in osmo-pcu. In osmo-pcu code, the function is guarded
and only called in FLOW or FINISHED state by the scheduler.

Change-Id: If8029bee90adceee128ebb20c033756efd50e90e
2021-08-23 17:14:21 +02:00
Pau Espin Pedrol
720e19e7f3 Move FLOW tbf_state transition to tbf_fsm.
Related: OS#2709
Change-Id: Ia8c7de759c195d09263fb1f083fbf6cfa3087f8d
2021-08-23 17:14:21 +02:00
Pau Espin Pedrol
33e8007100 Move NULL and ASSIGN tbf_state transition to tbf_fsm
At some point later in time the state_flags will most probably be split
into different variables, one ending up in a different FSM. It is moved
so far to the exsiting FSM from the C++ class since it's easier to
access it from C and C++ code, and anyway that kind of information
belongs to the FSM.

Related: OS#2709
Change-Id: I3c62e9e83965cb28065338733f182863e54d7474
2021-08-23 17:14:21 +02:00
Pau Espin Pedrol
88f34812df Revert "Revert "Stop abusing T3169""
This reverts commit 112c63e9b4.

Change-Id: Ic18674ccd38f81ddd46e1ec733159df350991899
2021-08-23 17:14:19 +02:00
Pau Espin Pedrol
b5fece959f Revert "fix: handle NULL return of as_dl_tbf() and as_ul_tbf()"
This reverts commit d8e8ea9c8f.

Change-Id: I8000e78515b25b9be5c28a249bde330dac915dcb
2021-08-23 17:14:17 +02:00
Pau Espin Pedrol
bc139a4af4 Revert "coverity: fix null deref from recent UL TBF leak fix"
This reverts commit 3bd6488889.

Change-Id: I59c4ae726286216850ad9b53fee34ab4bda5630f
2021-08-23 17:14:15 +02:00
Oliver Smith
e54f148ce9 tests: make update_exp: build check_PROGRAMS first
Make test development a bit more convenient.

Change-Id: Ic053aa3ab7485176b58eab6ebb4835cfbe6b8260
2021-08-23 14:56:27 +02:00
Oliver Smith
3f79470453 bts: delete pch_timer list in destructor
Run bts_pch_timer_remove() on each entry of the BTS specific pch_timer
list, so we don't have a memory leak and so the timer doesn't
potentially fire for a deallocated BTS.

Fixes: d3c7591 ("Add counters: pcu.bts.N.pch.requests.timeout")
Change-Id: Ia5e33d1894408e93a51c452002ef2f5758808269
2021-08-23 14:51:18 +02:00
Neels Hofmeyr
3bd6488889 coverity: fix null deref from recent UL TBF leak fix
Fix a possible NULL deref, introduced in recent patch
I8ce21be6836549b47a606c00b793d6f005964c5c /
d8e8ea9c8f

Related: OS#5205 SYS#5561 CID#239246
Change-Id: I603d4a5bc0fe5bd2e9f0dba171604c459e38aeaf
2021-08-18 18:23:44 +02:00
Neels Hofmeyr
d8e8ea9c8f fix: handle NULL return of as_dl_tbf() and as_ul_tbf()
Go through all callers of as_dl_tbf() and as_ul_tbf(), and make sure
they can handle the possible NULL return value.

OS#5205 reports a NULL deref crash of osmo-pcu at pdch.cpp:525. The
immediate cause is that as_dl_tbf() may well return NULL, which this
caller does not handle and instead dereferences immediately.
This is a code path that apparently assumes that a DL-TBF should always
be present. The higher level cause for the NULL DL-TBF has not been
identified.

Related: OS#5205 SYS#5561
Change-Id: I8ce21be6836549b47a606c00b793d6f005964c5c
2021-08-17 12:17:13 +00:00
Neels Hofmeyr
112c63e9b4 Revert "Stop abusing T3169"
This reverts commit 846fd248dc.

The commit introduced a leak of UL-TBF, which do not time out and
accumulate indefinitely, leading to out-of-memory for the running
osmo-pcu process.

A proper fix for the leak is pending on a development branch pespin/fsm,
but that branch is not yet ready for merging. Hence let's re-introduce
timer T3169 to avoid the OOM due to lingering UL-TBF.

Related: OS#5209
Change-Id: I99a7d2ddf68a76739ce2db1d6a44967dd97667b0
2021-08-15 17:46:53 +00:00
Neels Hofmeyr
4163361906 T_defs_bts: remove unit from doc strings
The main reason to change this is that the unit for T3172 is wrong. It
is defined as ms but the doc string says "(s)".

The tdef implementation already includes the unit as defined for each T
in the doc string implicitly, so instead of fixing that string, just
remove the unit strings from all the doc strings.

Now it will show:

 OsmoPCU# show bts-timer
 BTS0:
  T3142 = 20 s	Wait Indication used in Imm Ass Reject during TBF Establishment (CCCH) (default: 20 s, range: [0 .. 255])
  T3169 = 5 s	Reuse of USF and TFI(s) after the MS uplink TBF assignment is invalid (default: 5 s)
  T3172 = 5000 ms	Wait Indication used in Imm Ass Reject during TBF Establishment (PACCH) (default: 5000 ms, range: [0 .. 255000])
  T3191 = 5 s	Reuse of TFI(s) after sending (1) last RLC Data Block on TBF(s), or (2) PACKET TBF RELEASE for an MBMS radio bearer (default: 5 s)
  T3193 = 1600 ms	Reuse of TFI(s) after reception of final PACKET DOWNLINK ACK/NACK from MS for TBF (default: 100 ms)
  T3195 = 5 s	Reuse of TFI(s) upon no response from the MS (radio failure or cell change) for TBF/MBMS radio bearer (default: 5 s)

Related: OS#5209
Change-Id: I140122bb10f750bf996272cc7f9c5b541c9bd364
2021-08-12 15:08:16 +02:00
Oliver Smith
d3c7591304 Add counters: pcu.bts.N.pch.requests.timeout
Implement T3113 for paging over PCH with default value of 7s (same as
T3113 in OsmoBSC). Increase the new counter on timeout.

Related: SYS#4878
Change-Id: I97475c3dbe2cf00b9cbfec39e93a3c65cb7f749f
2021-08-11 13:42:30 +02:00
Oliver Smith
4df959d305 Add counters: pcu.bts.N.pch.requests
Count attempted paging requests over PCH.

Related: SYS#4878
Change-Id: I1026780ef8542f40060b961df2f37213e15c29d7
2021-08-10 10:35:18 +00:00
Oliver Smith
978396732b Add counters: pcu.sgsn.N.rx_paging_{cs,ps}
Related: SYS#4878
Change-Id: Iefba6f3d29c69fd4865c084bd9cf1a3a78f5c202
2021-08-10 10:35:18 +00:00
Oliver Smith
3f561bfbfe test: add 'make update_exp' target
Add convenience target to update the test output.

Change-Id: I225dd3746200cad748ea09b2c3e1c7f9d006d32f
2021-08-06 22:21:14 +02:00
Pau Espin Pedrol
945be91032 tests/tbf: Fix null pointer access if slowly stepping with gdb
When slowly debugging test_tbf_dl_llc_loss, bssgp_tx_llc_discarded() may
trigger, submitting events to the libosmogb code. Since it didn't
properly set up the callback, it would end up in a null pointer
dereference when lib code tried to use backward-compatible API (which
was neither set up properly).

"""
TBF(TFI=0 TLLI=0xc0123456 DIR=DL STATE=ASSIGN) Discarding LLC PDU because lifetime limit reached, count=3 new_queue_size=0
BSSGP (BVCI=2234) Tx LLC-DISCARDED TLLI=0xc0123456, FRAMES=3, OCTETS=57
/git/libosmocore/src/gb/gprs_ns.c:271:2: runtime error: member access within null pointer of type 'struct gprs_ns_inst'
"""

"""
(gdb) bt
 #0  0x00007ffff729cac0 in gprs_active_nsvc_by_nsei (nsi=nsi@entry=0x0, nsei=2234, bvci=bvci@entry=0)
    at /git/libosmocore/src/gb/gprs_ns.c:271
 #1  0x00007ffff72b1fec in gprs_ns_sendmsg (nsi=0x0, msg=0x621000000160) at /git/libosmocore/src/gb/gprs_ns.c:1087
 #2  0x00007ffff72d1803 in _gprs_ns_sendmsg (ctx=<optimized out>, msg=<optimized out>) at /git/libosmocore/src/gb/gprs_bssgp.c:80
 #3  0x00007ffff730226f in bssgp_tx_llc_discarded (bctx=<optimized out>, tlli=<optimized out>, num_frames=<optimized out>, num_octets=<optimized out>)
    at /git/libosmocore/src/gb/gprs_bssgp_bss.c:249
 #4  0x000055555588243e in gprs_rlcmac_dl_tbf::llc_dequeue (this=0x7ffff1622860, bctx=<optimized out>)
    at /git/osmo-pcu/src/tbf_dl.cpp:413
"""

Change-Id: Iee5bcf21afc8980a14f90f5b1ead6d2460a244ea
2021-07-26 16:05:55 +02:00
Pau Espin Pedrol
81db7334da tbf: Drop impossible paths in create_dl_ass()
create_dl_ass() is only called in gprs_rlcmac_sched.cpp on
tbf_cand->dl_ass pointer, which is always assigned under the guard
"!tbf->is_control_ts(pdch->ts_no)", since we only send CTRL messages for
a TBF on its control TS.
Hence, condition "!is_control_ts(ts)" in create_dl_ass will always be
false, and as a result poll_ass_dl will always be 1.
So we can drop different code paths.

Change-Id: Ibea4100a5dc8bd49303cb6a3d02417038c3d3887
2021-07-22 20:06:18 +02:00
Pau Espin Pedrol
890de986ce Make gcc 11.1.0 false positivies happy
After my system's gcc was upgraded, I get false positivies like the one
below:
"""
/git/osmo-pcu/src/gprs_bssgp_pcu.c: In function ‘ns_configure_nse’:
/git/osmo-pcu/src/gprs_bssgp_pcu.c:1103:58: error: ‘%d’ directive output may be truncated writing between 1 and 11 bytes into a region of size 2 [-Werror=format-truncation=]
 1103 |                         snprintf(name, sizeof(name), "pcu%d", i);
      |                                                          ^~
/git/osmo-pcu/src/gprs_bssgp_pcu.c:1103:54: note: directive argument in the range [-2147483648, 1]
 1103 |                         snprintf(name, sizeof(name), "pcu%d", i);
      |                                                      ^~~~~~~
/git/osmo-pcu/src/gprs_bssgp_pcu.c:1103:25: note: ‘snprintf’ output between 5 and 15 bytes into a destination of size 5
 1103 |                         snprintf(name, sizeof(name), "pcu%d", i);
      |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
In this case, i can't never take a value with more than 1 digit, but gcc
seems to be unable to see that.

Let's increase the buffer size a few bytes to make gcc happy, and make
the variable unsigned since it never will get negative values.

Next change is also a false positive, since variables are always
initialized beforehand in the cod epaths where they are used:
"""
/git/osmo-pcu/src/bts.cpp: In function ‘int bts_rcv_rach(gprs_rlcmac_bts*, const rach_ind_params*)’:
/git/osmo-pcu/src/bts.cpp:859:25: error: ‘ts_no’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
  859 |         uint8_t trx_no, ts_no;
      |                         ^~~~~
/git/osmo-pcu/src/bts.cpp:859:17: error: ‘trx_no’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
  859 |         uint8_t trx_no, ts_no;
      |                 ^~~~~~
"""

Change-Id: I1362a335a0c761bde367dbc779de4afa88f13584
2021-07-15 14:39:39 +02:00
Pau Espin Pedrol
4f67a9bf46 pdch: Fix heap-use-after-free in pdch->ulc
In existing previous code, pdch->ulc would be freed in
gprs_rlcmac_pdch::free_resources() when  it became disabled as per PCUIF
info_ind (for instance, when a DYN TS is switched PDCH->SDCCH8).
However, pdch->ulc was so far only allocated during pdch_init, which is
only called during bts_alloc() time.
Hence, after first info_ind disabling it, if it became again enabled
(again by info_ind re-enabling it after SDCCH8 was not longer in use),
the pdch->ulc would be used again but it would point to freed memory.

Let's rearrange how/when resources are freed to make it more logical.
With this patch, pdch internal resources are freed upon ->disable(), and
re-allocated upon ->enable().

Change-Id: Id51f5f6a54ac9f24b784c17bc360ac38f5726fc7
2021-07-01 13:09:10 +02:00
Pau Espin Pedrol
1989a19066 Support proto IPAC_PROTO_EXT_PCU BSC<->PCU
Related: SYS#5303
Change-Id: I633db291107883c2e370a9b56606d562a990b714
2021-06-25 17:20:50 +02:00
Pau Espin Pedrol
8c29236d35 pcuif_proto.h: Add new container message
Related: SYS#5303
Change-Id: Ib6c7bf5ca5a06186a71ec50cfc1a91a5c9b01d9c
2021-06-25 17:20:46 +02:00
Pau Espin Pedrol
ab178903d4 pdch: Fix null MS access gprs_rlcmac_pdch::rcv_control_ack
If bts_ms_by_tlli() at the start of the function fails, ms could be
NULL. As a result "ms->nacc" access at the end of the function would
crash.
Solution:
In the function, we get the related expected TBF from pdch_ulc, and we only
continue if a TBF is found. Since tbf objects are always expected to
have a GprsMs, simply gather it from there.

Change-Id: I666ed5d157f42e74956fa49fc9eea85d27e63d44
2021-06-23 13:50:35 +02:00
Vadim Yanitskiy
b657213773 pcu_l1_if: ignore PDCH interference reports, do not log errors
Change-Id: I88e5c53131ee94bc3f3ff3f095077feb4ff272a7
Related: SYS#5313, OS#1569
2021-06-22 05:58:23 +00:00
Vadim Yanitskiy
94dc6a8b6c PCUIF protocol: add message definition for interference report
Change-Id: I4b5a4c25e984f9262f0afd30f9671f150edee20f
Related: SYS#5313, OS#1569
2021-06-22 05:58:23 +00:00
Vadim Yanitskiy
826576287e gprs_rlcmac_sched: fix incorrect length for CTR_RLC_DL_BYTES
msg->data_len is the total number of bytes available in the buffer,
while for CTR_RLC_DL_BYTES we need to count size of the actual
payload within the buffer.  A consequence of this bug: osmo-pcu
was counting more Downlink bytes than it's actually transmitted.

Change-Id: I6884d220f3d06a79b16c18ccc2d2a6cd047b8251
2021-06-21 02:33:07 +02:00
Pau Espin Pedrol
86f4c093d1 pcuif: Support receiving System Information 2
OsmoPCU will need this SI2 in order to gain knowledge of the BCCH
Frequency List being broadcasted, in order to build a per-MS specific
Neighbour List using NC_FREQUENCY_LIST bits in Packet Measurement Order.

Related: SYS#5303
Change-Id: I4a9c4f70beac6805322a19835a0d30f7247780b4
2021-06-15 19:11:07 +02:00
Pau Espin Pedrol
c6e911cf22 pdch: Log pdch_ulc reason upon rx of pkt ctrl ack
Change-Id: I7c7a421b1e9189e2814e9a28698d66655fc9ba60
2021-06-07 18:16:55 +02:00
Pau Espin Pedrol
9c1db1738f Use new stat item/ctr getter APIs
Patch mostly done with the help of several small spatch snippets.

Change-Id: I600c7a8725f5b229b1a2feb879da7c3b2dce4505
2021-06-04 17:14:32 +02:00
Pau Espin Pedrol
d65bd9d7b2 bts: Fix typo in field name
Change-Id: I5426ff4ccbc45464888e2246cceb8e861d1e477e
2021-06-01 16:43:41 +02:00
Pau Espin Pedrol
c9880b97cf csn1: Implement CSN_CALLBACK type in encoder
Picked code from the Decoder function. I gave it a try
callback_init_Cell_Selection_Params_FREQUENCY_DIFF and looks
like working fine.

Change-Id: Iac962ae3e9f52f417f394060b64fc4d0ebf3d0bf
2021-05-28 18:42:42 +02:00
Pau Espin Pedrol
4c2387026a gsm_rlcmac.c: Fix arg list of 2 callbacks
Other callback functions are properly specified as per what's in
"typedef CSN_CallBackStatus_t". However, these two were wrong.

Change-Id: I280b51d4c8c38c76cc1ccd49656b6b7bbe769760
2021-05-28 18:42:42 +02:00
Pau Espin Pedrol
2761e574de cosmetic: Fix typo s/TIMSI/TMSI/
Change-Id: I64231311633b64d898625c49fdbf3f816dfbb97a
2021-05-21 13:44:18 +02:00
Pau Espin Pedrol
dc2aaac29f tbf: Move existing tbf_state implementation to osmo_fsm
This is only an initial implementation, where all state changes are
still done outside the FSM itself.
The idea is to do the move in several commits so that they can be
digested better in logical steps and avoid major break up.

Related: OS#2709
Change-Id: I6bb4baea2dee191ba5bbcbec2ea9dcf681aa1237
2021-05-19 12:50:25 +02:00
Pau Espin Pedrol
38f80be73b MsTest: Set up tbf talloc destructor
This is right now not an issue, but it will be whenever talloc
destructor contains extra steps like freeing an FSM.

Change-Id: I096ff56321c8ae5e66634537aae8b95804282c65
2021-05-19 12:50:25 +02:00
Pau Espin Pedrol
1a1557a60a Move TBF list from BTS to the TRX structure
The TBFs are managed per TRX. Move the global list from BTS to TRX.

Related: OS#1541
Change-Id: Id3c59c11d57d765fe68aaebaac94290c0d84feb2
2021-05-19 12:50:25 +02:00
Pau Espin Pedrol
f62b0ec37d tbf: Log error path in setup() failing to assign control TS
Change-Id: I047209dfe139e37a80878318cca75cb50905536e
2021-05-19 12:50:25 +02:00
Pau Espin Pedrol
9d2fd018ff bts: Use ms_store when calculating set of target PDCHs for Pkt Paging Request
The ul_tbfs/dl_tbfs lists will become per-trx. Since in this case we
want to operate on the BTS globally, let's iterate over MS objects
instead. This makes more sense too since here we really aim at reaching
a MS (subscriber) instead of specific TBFs. Later on the code can be
optimized easily to schedule a Pkt Paging Request for only 1 of the TBFs
of each MS instad of scheduling it for each TBFs in the MS.

Change-Id: I671e531921bbea2f5cc0f2bfcb8a39ea5c6673b8
2021-05-19 12:50:21 +02:00
Pau Espin Pedrol
bd54205475 Optimize PAGING-CS PDCH set selection when target MS is known
Before this patch, when a PAGING-GS was received in PCU from SGSN, it
would always forward the paging request to all PDCHs in all TRXs of all
BTS (well, it did some heuristics to avoid sending it in some PDCHs
where onyl repeated TBFs would be listening).

The previous behavior, didn't make much sense in the case where the PCU
is asked to page an MS which it knows (ie in which PDCHs is listening
to). Hence, in that case it makes sense to simply send the paging
request on 1 PDCH where the MS is listening, instead of sending it in a
big set of different PDCHs.

This commit also splits the old get_paging_mi() helper which was
erroneously created to parseboth CS/PS-PAGING requesst, since they
actually use a different set of target subscriber information (for
instance, CS-PAGING provides optionally a TLLI, and one provides P-TMSI
while the other provides TMSI).

In this patch, the handling of CS paging request is split into 2 parts:
1- A new helper "struct paging_req_cs" is introduced, where incoming
CS-PAGING requests (from both SGSN over BSSGP and BTS/BSC over PCUIF)
are parsed and information stored. Then, from available information, it
tries to find a target MS if avaialable
2- bts_add_paging() is called from both BSSGP and PCUIF paths with the
helper struct and the target MS (NULL if not found). If MS exists,
paging is forwarding only on 1 PDCH that MS is attached to. If no MS
exists, then the old heursitics are used to forward the request to all
MS.

Change-Id: Iea46d5321a29d800813b1aa2bf4ce175ce45e2cf
2021-05-19 12:46:00 +02:00
Pau Espin Pedrol
4c51eaf05b Use LOGPDCH macro in bts_add_paging()
Change-Id: I58daab719924d70de121f7a5f2cc1f122f8840af
2021-05-19 12:02:34 +02:00
Pau Espin Pedrol
6e25119c18 Clean false positive in newer GCC version checking guard of else clause
Got this today with newer gcc (11.1.0) after system upgrade:
egprs_rlc_compression.cpp:693:9: error: this ‘else’ clause does not guard... [-Werror=misleading-indentation]

The indentation was indeed wrong, provoking a warning in GCC. From code
flow point of view, however, the previous state was fine too, so no
logical change is involved in this commit.

Change-Id: I37bfc8e85daaabbbf10dfd907b305e3e0ec31863
2021-05-19 11:58:57 +02:00
Pau Espin Pedrol
c43570c351 RIM: Refactor Rx path to decode stack in proper order
Previous implementation of the Rx path was first checking the APP ID
before checking the lower layer (container type), which was confusing
because the information is then not verified in ascending order in the
protocol stack.

Let's instead, first, pass the pdu to the correct container type
handler, and only once there, let each container type handler verify the
available applications.

Change-Id: Ibe017c1a6e789f45d74c4a5f5f4608298c8c9f91
2021-05-17 14:21:21 +02:00
Pau Espin Pedrol
c48d27b57b pdch: Use llist_first_entry() API
Change-Id: I96e7188ecf7d2cfc54598975f8d538e7aa94401a
2021-05-13 15:58:55 +02:00
Pau Espin Pedrol
48517620b9 sched: Clean up param passing and improve logging
Change-Id: If137a2aaac7744e60564ca833a1b5564ed7d93bb
2021-05-12 14:24:02 +02:00
Pau Espin Pedrol
9b63cd04e4 ul_tbf: Fix accessing zeroed block when checking if transfer is complete
The logic checking whether the UL TBF had already been sent all the data
(and hence was marked as finished and requesting UL ACK to be sent) was
not taking into account the case where there was still no valid block
stored, ie. when the first received UL data block was discarded for some
reason (ex: because TLLI was not set during content resolution).

Related: OS#1940
Change-Id: I739e67ae1bb40555a362170f26fb98ac69caabb2
2021-05-12 14:24:02 +02:00
Pau Espin Pedrol
58916318ef ul_tbf: Simplify function rcv_data_block_acknowledged
Let's avoid different code paths in the loop based on is_tlli_invalid.
Instead, always do the proper storing of the block, and if later on the
corner case is found (no TLLI received while in Content Resolution
process) when checking tlli related stuff, then simply invalidate the
block.

Related: OS#1940
Change-Id: I77afaa617d7ce045c0f6d994fc0d8e03fe69de53
2021-05-12 14:24:02 +02:00
Pau Espin Pedrol
f53815f2fc Drop existing tbf->ms() check condition
Since a while ago, tbf should always have an MS attached since its
creation, so there's no sense to check for it here.

Change-Id: If056a3fb83b43a48c2a6382fc30c6c81fe2b2651
2021-05-12 14:24:02 +02:00
Pau Espin Pedrol
632542348a sched: Clean up helper function and improve logging
Change-Id: I8c19d0924e73c324a36ea038cab7cc4e096b866b
2021-05-12 14:23:59 +02:00
Pau Espin Pedrol
c432e062ea encoding: Encode TA in UL ACK/NACK if available
Change-Id: I3b060ee16aeac5f5d9b314b6bc46383f5e9c44c3
2021-05-11 13:24:13 +02:00
Pau Espin Pedrol
6bab522e90 encoding: Use gsm48_ta_is_valid() API
Change-Id: Ieaa4c2f926611576e22eaac8a7ac595135809e2c
2021-05-11 13:01:53 +02:00
Pau Espin Pedrol
eb13c79cc0 Tx ul ack/nack: Avoid sending invalid/unknown TLLI
It could happen that if MS sends first UL blocks without TLLI (wrongly,
due to being in contention resolution), the submitted UL ACK/NACK would
contain an invalid TLLI.

Related: OS#1940
Change-Id: Ibae5df6cfbb56f8f8007cb9fec9c29006d673b72
2021-05-11 12:55:44 +02:00
Pau Espin Pedrol
faf0ccb241 tbf_ul: Use is_tlli_valid() API
Change-Id: I4abb46913b05d1e89ebe9e361b0a774880dee998
2021-05-11 12:55:44 +02:00
Pau Espin Pedrol
4b6f0bfe69 Implement T3141
Related: OS#1940
Change-Id: I154984b835b2b436b1ebe4631a8afcebb7338c65
2021-05-11 11:32:44 +02:00
Pau Espin Pedrol
20271c421c Split ul_tbf alloc on CCCH into new function
This allows more easily finding when this specific scenario happens, and
can easily be compared against the PACCH one.

Change-Id: I609792a40fda2a798ca71a0e9f5639d0a0f011d7
2021-05-11 11:32:44 +02:00
Pau Espin Pedrol
c6571b5581 Rename function s/tbf_alloc_ul/tbf_alloc_ul_pacch/
Change-Id: I70ca0b5be0a29a05c6e65b9c92cc6d3b5c43d3dc
2021-05-11 11:32:38 +02:00
Pau Espin Pedrol
50272a4776 alloc_algorithm_b: Rearrange variable initialization
Untangle variable assignment at the start of the function. Changes end
up in same kind of assignment, but are far easier to understand based on
the variable use later on.
* reserved_{dl,ul}_slots contain mask of TS either "previously-reserved" or
  "intended to be reserved now" based on MS's ms_class.
* {dl,ul}_slots contain a derived mask from the one above, filtered
  further based on more factors like type of allocation requested (multi
  vs single), available USFs (UL), etc.

Change-Id: If3cfa82f8b793a87e97145ee8a6fc0fe1a61add6
2021-05-10 12:25:24 +02:00
Pau Espin Pedrol
393484a5d0 Simplify helper function tbf_select_slot_set()
Store direction check to simplify the code.
Get rid of 2-step LOGP to avoid multi-row logs in gsmtap log.

Change-Id: Ia2e061da82ddce564b2d768d8ade1672c22934e2
2021-05-10 11:21:52 +02:00
Pau Espin Pedrol
0e35aee194 rim: Constify param in func
Change-Id: I47c471929a62d6a5340ae4a4ca88bd0b758c208d
2021-05-06 19:47:15 +02:00
Pau Espin Pedrol
c6dcfe32f3 sched: Rename func to describe its used only for RLCMAC CTRL blocks
Change-Id: I20e15047af2aac4d51e1dae263ab16e479bb0c46
2021-04-30 17:42:25 +00:00
Pau Espin Pedrol
4b7a71f93f bts: constify arg in func bts_ms_store()
Change-Id: I4cc8c4fc075cdd07e689511df8f1c267e5360014
2021-04-30 17:42:25 +00:00
Harald Welte
292d04d19d manual: Include QoS chapter and add osmo-pcu specific example
Change-Id: I4d409b55861f05ba229dc5cb97f99370356e3dbd
Requires: osmo-gsm-manuals.git Id344c29eda2a9b3e36376302b425e9db1f6c0f28
2021-04-29 22:16:49 +02:00
Harald Welte
d9367e34db vty: Add configuration for Gb DSCP and socket priority
While libosmogb / ns2 supports that natively in the VTY, the PCU
doesn't want to use the complexities of the full NS2 vty.

Change-Id: I7bfbad46582e65e5ad2ac0cc66545538bc632df8
Related: SYS#5427
2021-04-29 22:13:05 +02:00
Harald Welte
4e453b41f3 manual: Update copyright years
Change-Id: Ia0dde7100dd90c6ad6279efbaf02b9bd3f868635
2021-04-29 22:07:10 +02:00
Harald Welte
fa48b4b720 manual: remove revhistory, as we don't maintain it manually anyyway
Change-Id: Ibbe08cac143f4bff6192125940ef190cc943d307
2021-04-29 22:07:10 +02:00
Pau Espin Pedrol
f593fc5cbb doc/tbf.txt: Update and improve some information
Change-Id: I3cd643ef462637708c69895c62c488554a428571
2021-04-27 12:01:52 +02:00
Pau Espin Pedrol
039ee8200a Clarify, document Assignment related timers
Related: OS#3928
Change-Id: Iad31a5c6f83cd78793adf05a6af782ceacae8b11
2021-04-26 18:49:58 +02:00
Pau Espin Pedrol
34f61af3d0 sched: Simplify else-if condition
The code path running into first call of "create_packet_access_reject()"
is a superset condition of the second one, so the second one will never
be hit.

As a result first, this block:
"""
else if (tbf == tbfs->ul_ass && tbf->direction == GPRS_RLCMAC_DL_TBF)
    if (tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
        msg = tbfs->ul_ass->create_packet_access_reject();
    else
        msg = tbfs->ul_ass->create_ul_ass(fn, ts);
"""

Can be simplified into:
"""
else if (tbf == tbfs->ul_ass && tbf->direction == GPRS_RLCMAC_DL_TBF &&
	     !tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
        msg = tbfs->ul_ass->create_ul_ass(fn, ts);
"""

Next, one can see that previous condition still forces
!tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ) to be always true
if we ever reach that code, so it can be dropped.

Change-Id: I62e2255e28fc4f43fe0a31259ebf18ad00e7e357
2021-04-26 18:02:54 +02:00
Pau Espin Pedrol
2ab840a1fa Make WaitIndication T3172 configurable
Tbftest expectatins need to change because 5000/20 = 250 < 255, hence
the message is now sent as units of 20ms instead of seconds.

Related: OS#3928
Change-Id: I48b34b94b1a5dfb046a3a6cf8a0d944a7c9b6754
2021-04-26 17:53:09 +02:00
Pau Espin Pedrol
54742f287c ul_tbf: Clean up handle_tbf_reject()
Document the function, make it look similar to usual TBF creation path
tbf_alloc_ul()->tbf_alloc_ul_tbf->tbf::setup(), which it mimics with
some differences.
Get rid of unneeded stuff like creating MS and settings its TLLI (that's
already done in only caller of the function). There's no need for
calling update_ms() either.

Change-Id: I61df2e4f0f0df1f8db941741a2d35a2319252c5e
2021-04-26 17:19:07 +02:00
Pau Espin Pedrol
14339f6fac Use negative numbers for non-spec osmo-specific timers
(values -1 and -2 cannot be used because they are already taken).

Related: OS#3928
Change-Id: Ibcdb05ff5bb8efe6cb95cf94e2c8e418dc8deced
2021-04-26 14:15:08 +02:00
Pau Espin Pedrol
25ebf3c8f9 Make use of T3142 received from BTS
Related: OS#3928
Change-Id: I4e26f181db9693d3a267a879e2aebda12eab2a8c
2021-04-26 14:10:11 +02:00
Pau Espin Pedrol
846fd248dc Stop abusing T3169
Now that we finally handle N3101 and N3103 correctly, we can fix abuse
of T3169 we were doing to make sure TBFs were freed.

According to 3GPP TS 44.060, T3169 should be armed:
* N3101_MAX reached
* N3103_MAX reached

Furthermore, when T3169 is enabled, the tbf should be in state
RELEASING so that its USF is not used.

See full description: https://osmocom.org/issues/5033#note-2

Related: OS#5033
Change-Id: I2cec531e2633281b88f69ba065c0105580c81076
2021-04-26 11:20:19 +02:00
Pau Espin Pedrol
710e0e9ad8 pdch: tbf_by_tfi(): Allow returning TBFs in state RELEASING
During RELEASING state the TFI, USFs, etc. are still reserved and
assigned to the TBF, and hence the TBF may still use it.
If callers of this function rely on not taking TBFs under RELEASING
state, they should check that explicitly.

It still makes sense being to operate on RELEASING TBFs, since under
some circumstances the TBF may go under a previous state. See for
instance 3GPP TS 44.060 sec 8.1.1.3a.2:

"""
If N3101 reaches the value N3101max, the network shall stop sending
PACKET UPLINK ACK/NACK messages to the mobile station for that TBF
and shall start timer T3169 for the TBF. If an RLC/MAC block is received
from the TBF when timer T3169 is running, the network shall stop timer
T3169 and resume sending PACKET UPLINK ACK/NACK messages to the TBF.
When T3169 expires, the network may consider the TBF as released and
reuse the TFI value.
"""

Change-Id: Ibb471e727388512d42794d3faa26597e2545b852
2021-04-22 21:07:06 +02:00
Pau Espin Pedrol
434799720c pdch: rcv_resource_request: Improve robustness
Use recently added PDCH UL Controller to verify expectancies.

Test test_packet_access_rej_prr is rewritten since it didn't make sense
as it was before, since it relied on osmo-pcu not checking stuff
properly to trigger the reject. The RACH requests are changed to
allocate 8 SBAs (maximum of 7 concurrent USFs). Allocating the SBA
doesn't reserve a USF, that happens at PKT RESOURCE REQUEST, hence we
end up exhausting resources there and triggering the REJECT at that
point.
Previous version of the patch allocated TBFs directly through RACH req,
and then submitted an extra PKT RESOURCE REQUEST which PCU didn't expect
to trigger the reject.

Change-Id: I157e72160317340ee7742c78c62a25d3d98fc01e
2021-04-22 20:28:40 +02:00
Pau Espin Pedrol
ffc533b1af sba: Drop unused function find_sba_rts
This function is not longer used since commit below, let's drop it.

Change-Id: I633676fc3a573acd0dccdd035ffe557c9c71a56e
Fixes: fd1fbdb8db
2021-04-22 19:46:54 +02:00
Pau Espin Pedrol
ab3aca65c5 RIM: Improve logging
Change-Id: I0adbb8ea4480912463dc1dded6c06a1b8f7ed807
2021-04-19 17:47:26 +02:00
Pau Espin Pedrol
16e1678bfc tbf: Get rid of attribute poll_ts
That field is not needed anymore, and it works only under the assumption
that only 1 poll request can be active at a time per TBF, which is not
true.

Change-Id: I9b8bed7741d385bab4cd8c64b841a78a02a05fe1
2021-03-31 17:39:50 +02:00
Pau Espin Pedrol
58046e45d1 tbf: Get rid of attribute poll_fn
That field is not needed anymore, and it works only under the assumption
that only 1 poll request can be active at a time per TBF, which is not
true.

Change-Id: I63a34a702f028b871530fb7caeb13e8ea1cc78ac
2021-03-31 17:39:50 +02:00
Pau Espin Pedrol
2c0931cedc Get rid of param 'poll' with constant value
Value 'false' is always passed by all callers of the function, so
there's no need to pass it. Furthermore, since it's false, there's no
need to access poll_fn since RRBP will always be invalid.

Change-Id: Ia48ce2a021865e76e813dedb22aca9c2522c5693
2021-03-31 17:39:50 +02:00
Pau Espin Pedrol
127e7392f6 tbf: get rid of poll_state completely
The poll_state logic was part of previous implementation (prior to pdch
ul controller) where the ssumption was that TBF could only had 1 POLL
request in transit, which is really not true. With current
infrastructure we don't need this state tracking at all.

Change-Id: Ie5b807ccd38aa736ae11b3310ca61ad0156ca4d4
2021-03-31 17:39:50 +02:00
Pau Espin Pedrol
feee2b9b83 Remove unneeded poll_state check
The related ul_ass_state already implies polling is ongoing since we are
waiting for an ACK to be received from MS. Hence there's no need to
check poll_state there.

Change-Id: I5e12280a6835407fa452bd4d5df799d2672790ec
2021-03-31 17:39:50 +02:00
Pau Espin Pedrol
42445ea944 tbf: Allow multiple concurrent polls
There's no good reason to allow only for 1 concurrent POLL requested to
a TBF, it was onyl done this was as an implementation limitation factor.
It can well happen that several multiple POLLs may be in transit at the
same time, eg to get DL ACK/NACK as well as to get a CTRL ACK for a Pkt
Cell Change Continue (NACC).

Change-Id: Ic4080db684a4626cae90dd574d123081981284ca
2021-03-31 17:39:50 +02:00
Pau Espin Pedrol
ed066b5328 tbf: Get rid of unneeded poll_scheduled()
This API is not really needed anymore, since anyway it works under the
assumption there can only be 1 POLL in transit per TBF, which isn't
necessarily true.

Change-Id: I875f51cade95faeb2d79dcebfead4c83e23a731b
2021-03-31 17:39:50 +02:00
Pau Espin Pedrol
86580e1966 pdch_ulc: Store TBF poll reason
This allows easily checking the initial reason to trigger the poll when
either it is received or times out.

Later on this reason can be transformed into an FSM event and sent to
the related FSM.

Related: OS#5020
Change-Id: Ie8fefd1f47ad674ce597a8065b15284088956bde
2021-03-31 17:39:50 +02:00
Pau Espin Pedrol
b5ae0811d1 Drop unused function tbf_check()
Change-Id: I90d75a75ae5b528c6ca7b409e60bd158d6043b35
2021-03-31 17:39:50 +02:00
Pau Espin Pedrol
50a1ede693 pdch_ulc: Support picking RRBP other than N+13
Current algo always tries to sched RRBP the soonest possible.

Related: OS#5020
Change-Id: Ic6ddeea70e1f914cf423d0daab8fc492d0c992e2
2021-03-31 17:39:50 +02:00
Pau Espin Pedrol
ce3bd2522a Pick unreserved UL FN when allocating an SBA
Make sure an unreserved FN is picked and reserved when allocating and
scheduling an SBA.
In practice this has no change in behavior right now, since anyway using
an offset of 52 FNs ensure no USF or POLL has alredy been scheduled that
far in the future. Since it's also impossible to allocate more than 1
SBA per PDCH and RTS FN, we are also safe about multiple SBAs being
allocated, because we use a hardcoded offset of 52.
However, that could change in the future, when we dynamically tweak the
current offset of 52 FN based on information from BTS about its AGCH
queue load:
* If load is high, we may need to increase the offset since it
will take more time for the BTS to transmit the TBF and hence we must
reserve a TBF starting time further in the future (higher FN).
* If load turns low, we may schedule next SBA a bit more nearby in time
  than the previously allocated SBA, hence here there could be a
  collision.

Related: OS#5020
Change-Id: I2d4e21e2307de6c17748e8da5c7e149c947a7eb9
2021-03-31 17:39:50 +02:00
Pau Espin Pedrol
222f674116 pdch_ulc: Optimize rbtree FN search
Use logarithmic lookup algo to find if FN is available instead of
iterating over the whole tree.

Change-Id: I2843aedb5ce74c909bde82d29269d0f956e9a093
2021-03-31 17:39:50 +02:00
Pau Espin Pedrol
54e6450293 sba: Document AGCH_START_OFFSET after some experimental tests
Related: OS#5020
Change-Id: Id1460207be25750aeb5c1d7af2fac6591cf5e424
2021-03-31 17:39:46 +02:00
Pau Espin Pedrol
0b998b15da Properly implement N3101
N3101 is incremented by unanswered USF requests, not from unanswered
POLLs.

Related: OS#5033
Change-Id: I1a55bdd39db8843976915b9f74fadb0942298413
2021-03-24 17:16:29 +01:00
Pau Espin Pedrol
c1f38c7f0b Track scheduled UL blocks through USF
This way PCU can now detect whether scheduled UL blocks through USF
were never received. This allows in a follow-up patch to start
increasing N3101 properly.

Related: OS#5033
Change-Id: Ia99c9edad6e5bd837e9baeb4fb2683b227887957
2021-03-24 17:14:19 +01:00
Pau Espin Pedrol
ade9c2f553 pdch_ulc: Create helper API pdch_ulc_release_node
Change-Id: I6362ad7382c2b73e6fedb11182964be96e5c8d35
2021-03-24 17:12:01 +01:00
Pau Espin Pedrol
9a6f0b191a pdch: Add mising pdch_ulc_release_node in Rx Cell Change Notif
All other RX CTRL block paths have it, this one was missing.

Change-Id: Ief315d7b6d4fea946d43e5bd87cf8a0394adc855
2021-03-24 17:12:01 +01:00
Pau Espin Pedrol
7bd92a3e1d Set matching USF if available when polling a UL TBF
When the scheduler detects it's time to receive a UL block due to a
scheduled poll, if that polling is done on a UL TBF, then use its USF if
available instead of using USF_UNUSED (=7) when sending a DL block on
that same FN.

This is not really needed for correct work, since MS take care
themselves of scheduling a UL block when they receive the poll (RRBP)
some time before, and don't check the USF at the time of transmitting.
In any case, it helps understand better when looking at pcap traces that
indeed it a UL block from that MS was requested, instead of setting USF
to 7.

Related: OS#5033
Change-Id: I2ad9d8ea6afc8f83192033470bd27010a7474430
2021-03-24 17:12:01 +01:00
Pau Espin Pedrol
4bab867d9f sched: Simplify usf selection code
Simply use the UL TBF pointer all along until the end, instead of setting
both the UL TBF pointer plus the usf var.

This commit is also a preparation for next commit which also selects UL
TBF when a poll is available, to set its USF in the DL message instead
of "USF_UNUSED".

Change-Id: I3aa3886932ef87db18ed7ff6991ea315f481990b
2021-03-24 17:12:01 +01:00
Pau Espin Pedrol
107e94c9f8 sched: Fix scheduling UL TBF not matching conditions
With previous code, a skipped TBF could be returned despite not matching
the conditions, since at the end of the loop the tbf pointer was
returned.

Related: OS#5020
Change-Id: If6dccec86c7a655bf1c62f333cfbc8d2c507c94f
2021-03-24 17:12:01 +01:00
Pau Espin Pedrol
56f223d8d1 Fix: left shift cannot be repesented in type int
Caught by ASan:
runtime error: left shift of 1 by 31 places cannot be represented in type 'int'

Change-Id: I30aed795d027dc063f06e08c8455bad2dd92cf24
2021-03-24 14:27:19 +01:00
Alexander Couzens
5d376845bb gprs_bssgp_pcu: add comments to the pcu states
Related: OS#3879
Change-Id: Iccf6508ce46162e6dfd6b00abd44e24cb425b346
2021-03-23 17:47:22 +00:00
Alexander Couzens
82519264ca gprs_bssgp_pcu: ensure only known BVCI can be resetted by the SGSN
Related: OS#3879
Change-Id: I04e36ce4a29e51d85e67a0d3a81aa0e1eb9e9c08
2021-03-23 17:47:22 +00:00
Harald Welte
d7f0558b5c pdch_ul_controller: Fix compiler warning on gcc-10.2
pdch_ul_controller.c: In function ‘pdch_ulc_release_tbf’:
pdch_ul_controller.c:217:7: error: ‘item_tbf’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
  217 |    if (item_tbf != tbf)
      |       ^

Change-Id: I42120fdf23753945ebc16bb5469d9fd253c3da37
2021-03-20 16:17:30 +01:00
Pau Espin Pedrol
755a8d61bb direct_phy: Fix condition dropping rx DATA.ind payload in in
Related: OS#5020
Fixes: 81c549d5be
Change-Id: Iad8e50b856009439d78c596c5b54dc3e9836e1d4
2021-03-18 14:03:35 +01:00
Pau Espin Pedrol
fecab50066 sysmo: fix wrong FN jumps in rx RA.ind
There's no need for setting the FN in RA.ind since we anyway already
receive a DATA.ind beforehand.
Furthermore, the applied delay of 5 in the call is not really used at
all.

Change-Id: I437f4f95d054aea96bec3b9343e495451020ff3c
2021-03-17 15:58:16 +01:00
Pau Espin Pedrol
c7cc4162e1 ulc: Fix FN store order upon wrap around
Related: OS#5020
Change-Id: I0a742f7fa1541b1837739207b9383772f981fb25
2021-03-15 19:36:56 +01:00
Pau Espin Pedrol
95f8fa1f7c tests: ulc: Show current bug with FN wrap around
Issue will be fixed in next commit. Leaving ASSERTs disabled so that
test passes in jenkins.

Related: OS#5020
Change-Id: I657db6b300363f8f3a9e4cfaf7a7f49e361a0512
2021-03-15 19:36:27 +01:00
Pau Espin Pedrol
582a15e413 tests: Introduce unit tests for PDCH UL Controller
Related: OS#5020
Change-Id: Ie1ff0ca3d7fc8a9824d6fe4dceb746e301082bda
2021-03-15 19:35:43 +01:00
Pau Espin Pedrol
5bc9612c02 cosmetic: tests/Makefile.am: Split content into several lines
Change-Id: I67361862b992d761b314d9565165ece7e380606d
2021-03-15 19:34:35 +01:00
Pau Espin Pedrol
3a42d17b14 bts: Detect FN jumps
Change-Id: I29fb27981597edc69abb976049ba41aa840488cb
2021-03-15 19:34:35 +01:00
Pau Espin Pedrol
fd1fbdb8db sched: Use new PDCH UL Controller
Take the time to also do small refactorings to clarify and simplify the
function, by using rts_next_fn() already available in pcu_utils.h and
getting rid of poll_tbf from tbf_candidates, which clearly follows
another objective.

Using PDCH UL Controller has the advantage that we don't need to check
poll_scheduled() on each TBF, but only do the query once.

Related: OS#5020
Change-Id: Ia60bb5249a9837dec1f42180e44d9848334d86d6
2021-03-15 19:34:20 +01:00
Pau Espin Pedrol
99360a304f Replace PollController with newly added PDCH UL Controller
TbfTest is updated to submit empty blocks to have somehow meaningful
output (at least as meaningful test results as before, not much). That's
because we must update bts->curr_fn to have polls expire.

Related: OS#5020
Change-Id: I683ca738ce5a133c49c36a1d94439a942d64a831
2021-03-15 19:32:36 +01:00
Pau Espin Pedrol
15c58ace75 Add new PDCH UL Controller, drop SBAllocator class
Right now we handle different types of UL allocations in different
classes like PollAllocator and SBAllocator, and they usually don't take
into account the other one in most cases. Furthermore, those objects are
usually per-BTS object, instead of per PDCH object.

This is a first step towards having a unified per-PDCH controller which
takes care of controlling what is scheduled and hence expected on the
uplink. Each PDCH has a UL Controller which keeps track of all reserved
uplink frame, be it SB, RRBP poll or USF assigned, all under the same
API.

As a first step, only the SBA part is fully implemented and used (being
it the easiest part to replace); TBF poll+usf will come in follow-up
patches later on. As a result, the SBAllocator per-BTS class dissappears
but some of its code is refactored/reused to provide more features to the
gprs_rlcmac_sba object, which is also further integrated into the new UL
Controller.

Related: OS#5020
Change-Id: I84b24beea4a1aa2c1528f41435f77bd16df2b947
2021-03-15 19:32:26 +01:00
Pau Espin Pedrol
68bfdff3f0 pdch: Log FN when decoding UL Ctrl block
Change-Id: I5a44ebf49f7489211a77607052db6d9731f38704
2021-03-12 07:40:11 +00:00
Pau Espin Pedrol
4f2c8cd96a tbf: Fix wrong variable printed in log
Change-Id: Iad99e48fcb7488daed40a5095c5dcdc02def00c5
2021-03-12 07:40:11 +00:00
Pau Espin Pedrol
df58ddf6d8 Improve logging in DATA.req and ACT.req
Change-Id: Id57d50d8bf528adfef3713c594102d31ab49c149
2021-03-12 07:40:11 +00:00
Pau Espin Pedrol
c1f31c46ac Improve DATA.ind logging
pdch object is obtained prior in the stack so it is available for
logging.

Change-Id: If51f7bdbd626a44c7b8e182a3460dad49fda6ec3
2021-03-12 07:40:11 +00:00
Pau Espin Pedrol
91cc780b40 pdch.h: Drop uneeded include bts.h
This header is not needed and creates include loop issues in follow-up
patches.

Change-Id: Ic12ab293f27b5e13d1401c6f17d4d549bf5115f9
2021-03-12 07:40:11 +00:00
Pau Espin Pedrol
1e97951582 tests: rlcmac: Fix C vs C++ linkage of extern symbol
RLCMACTest.cpp:31:30: error: conflicting declaration of ‘const log_info gprs_log_info’ with ‘C’ linkage
   31 | extern const struct log_info gprs_log_info;
gprs_debug.h:54:30: note: previous declaration with ‘C++’ linkag

Change-Id: I5922950dd0057bf7eb8578e2144f127082323fc6
2021-03-12 07:40:11 +00:00
Pau Espin Pedrol
702ebee751 Introduce init() APIs for PDCH and TRX objects
This will make it easier to keep object specific initializations in
expected place.

Change-Id: Idf1dbdf8bc0b1e16d86eeeffb1193fdf3a57d6ef
2021-03-12 07:40:11 +00:00
Pau Espin Pedrol
30617115ba Track TDMA clock with DATA.ind instead of TIME.ind
Since recently (see Depends below), BTS side submits DATA.ind with len=0
to announce nothing was received on that UL block FN. This will allow
osmo-pcu track time more accurately, and use this information to quickly
find out if a UL block was expected as requested by RRBP or USF poll and
increment counters such as N3101 (finally being able to properly
implement timers such as T3619).

Depends: osmo-bts.git Change-Id I343c7a721dab72411edbca816c8864926bc329fb
Related: OS#5020

Change-Id: Ibc495173119465e74f726ddc36e312334e6dc0fd
2021-03-12 07:40:11 +00:00
Pau Espin Pedrol
5447f3acf6 pcu_utils.h: Fix trailing whitespace
Change-Id: I113766e342a00f61f9894dee1bb89b8ae8354007
2021-03-11 17:01:25 +01:00
Pau Espin Pedrol
81c549d5be direct_phy: Support submitting DATA.ind with len=0 to upper layers
Since recently (see Depends below), BTS side submits DATA.ind with len=0
to announce nothing was received on that UL block FN. This will allow
osmo-pcu track time more accurately, and use this information to quickly
find out if a UL block was expected as requested by RRBP or USF poll and
increment counters such as N3101 (finally being able to properly
implement timers such as T3619).

This patch does the same for direct phy feature, where the osmo-pcu
process receives the DATA.ind directly from the DSP.

Depends: osmo-bts.git Change-Id I343c7a721dab72411edbca816c8864926bc329fb

Related: OS#5033
Change-Id: I9a835e16ef0e5a68c003a93d1a33233aa43464ae
2021-03-08 13:11:03 +01:00
Pau Espin Pedrol
3cba94d70e pdch: Silently ignore DATA.ind with len=0
Since recently (see Depends below), BTS side submits DATA.ind with len=0
to announce nothing was received on that UL block FN. This will allow
osmo-pcu track time more accurately, and use this information to quickly
find out if a UL block was expected as requested by RRBP or USF poll and
increment counters such as N3101 (finally being able to properly
implement timers such as T3619).

Depends: osmo-bts.git Change-Id I343c7a721dab72411edbca816c8864926bc329fb
Related: OS#5020
Change-Id: I17c28abf63b153448b533971ac5cf2e48daadea8
2021-03-08 10:39:31 +01:00
Pau Espin Pedrol
58fdc54a7f tbf: Log N310* counter increments
Change-Id: Iacd2fb894b4f2a9aade7e66aa40969fea031c3b2
2021-03-04 14:11:23 +01:00
Pau Espin Pedrol
0057bd76fd TODO-RELEASE: document requirement of master libosmocore
Recent commit started using ->is_sgsn field which is only available as
of current libosmocore master (> 1.5.1)

Fixes: 423bf8c408
Change-Id: I3d579bd253363efc3cf183a922d6affca1a499e0
2021-03-04 11:24:50 +00:00
Pau Espin Pedrol
2ad15d51fa sched: sched_select_downlink(): Clean up param list and improve logging
Passing TRX and TS is redundant since the info is contained in pdch
object.

Change-Id: I1b154d82c4a3e09f7fe7ef771de2abca0160cc7b
2021-03-03 20:52:26 +01:00
Pau Espin Pedrol
3973eb5fe8 sched: sched_select_ctrl_msg(): Clean up param list and improve logging
Passing TRX and TS is redundant since the info is contained in pdch
object.

Change-Id: Ic3ec7547cae2ddd0f9c33b82e15ec83cd941e6c8
2021-03-03 20:47:09 +01:00
Pau Espin Pedrol
11f01105a0 gprs_ms: Use standarized logging on more messages
Change-Id: If7f471f4932c2347cd857cd59f761a36d9e735d1
2021-03-03 20:37:38 +01:00
Pau Espin Pedrol
4fe090146f ms: clarify delayed MS release process related code and logging
Change-Id: Ieaea6ab07b4b2822bcf394f2d0e9298b9f3c5854
2021-03-03 20:28:51 +01:00
Alexander Couzens
423bf8c408 gprs_bssgp_pcu: rework BSSGP Reset messages to support SGSN originated BSSGP-RESET
Use primitives instead of parsing the message a second time.
Set bctx->is_sgsn to false to allow the BSSGP layer to send back a
RESET_ACK with cell information.

Related: OS#3879
Depends: Ibcbaffa94cbdc4296a8a7c372304ac11d50d9559 (libosmocore)
Change-Id: I3afaf826798e362270ffa622c24bfd124ef25cd1
2021-03-03 15:37:19 +00:00
Pau Espin Pedrol
cf6c71263f tbf_dl: fix FBI not set upon X2031 = 0
If Idle TBF timer (X2031) is set to 0, it means the TBF release is
immediately started once all queued data has been scheduled. In that
case, we must set FBI=1 (by setting cv=0) and move to FINISH state.

This used to work over the usual path where X2031 != 0, because release
start will alays happen at a later sched poll time where a dummy LLC
frame is sent and FBI set accordingly.

Change-Id: Ib20602936ae084c413f6bfe14eea33b602020be0
2021-03-02 18:25:33 +01:00
Pau Espin Pedrol
8afc6bad80 tbf_dl: Fix m_last_dl_drained_fn not set under some conditions
Old commit getting rid of LLC UI dummy and updating create_new_bsn()
function introduced a bug by not moving update of value m_last_dl_drained_fn
prior to a new break introduced.
As a result, the value is not updated in the case LLC queue becomes
drained but last few bytes are drained at exactly that moment.
Furthermore, then the IDLE tbf timer (X2031, keep_open())) returns always
true since according to it the drain never happened.

The impact of the bug is basically delaying a bit more than expected the
time the TBF stays in IDLE state with the TBF release process yet
to be started.

Related: OS#4849
Fixes: 7d0f9a0ec3
Change-Id: I7420aeffda3500bcdc990291e4a56511af433ff9
2021-03-02 18:24:46 +01:00
Pau Espin Pedrol
5b9d0bb8e5 tbf: log keep_open condition status
Change-Id: I069e84926aaa8f13b23c3ea4083b4c68dbc6cff2
2021-03-02 13:13:52 +01:00
Pau Espin Pedrol
a89008b724 tbd_dl: Don't re-initialize class field twice
Change-Id: Ia92c24032dc1f8965008ff03a3a0a94bbb93893a
2021-03-02 13:09:32 +01:00
Pau Espin Pedrol
a70bf72ce5 llc: use memset to fill llc dummy frame padding
Change-Id: Iaa55549f979ca23dad0bddd308c1144aa4b17255
2021-03-02 12:28:32 +01:00
Pau Espin Pedrol
9688dc9aca bts: Add new stats to detect TBF allocation failure reasons
This is specially useful to detect for instance if a cell is handling
too many users, ending up in TFI or USF exhaustions. This information
can be later in the future used to tune TBF allocation algorithm behavior
(either manually/statially through config file, or
automatically/dynamically in code based on some thresholds).

Related: OS#5042
Change-Id: I5402e937ff8d800684655e500ef8e5c867141dc3
2021-03-01 13:18:36 +01:00
Pau Espin Pedrol
c85e093969 Remove uneeded ms param from alloc_algorithm_func_t func
Since a while ago, the data architecture was changed so that TBF is
guaranteed to always have a MS object associated. Hence, it makes no
sense to pass the MS object as a separate param as we can take it from
tbf object and makes code less confusing.

Change-Id: Idc0c76cf6f007afa4236480cdad0d8e99dabec5f
2021-02-26 11:50:21 +01:00
Pau Espin Pedrol
36177c6b58 tbf: Improve logging when TBF being allocated or no TBF avail
Change-Id: I68491fe2c643262e35b4d4f1ecac34afcf61848f
2021-02-26 11:50:15 +01:00
Pau Espin Pedrol
95e2266832 pdch: Standarize and improve logging
Change-Id: I1686a72eb9f9014ed3365376bc43d59d60bee8a5
2021-02-26 11:36:55 +01:00
Pau Espin Pedrol
4e1c9adb67 bts: Count TBF TS allocation failure
Related: OS#2282
Change-Id: I0696bf77364bd31b96c00614a58ce66809683d1c
2021-02-25 17:49:59 +01:00
Pau Espin Pedrol
7c9a4a41bc tbf: Log timeslot allocation failure
Change-Id: I48fc1eac37eeb74649bfc0888e06afc0079a58f8
2021-02-25 17:21:10 +01:00
Pau Espin Pedrol
ed2afa3bed Support uplink multi-slot allocations
Before this patch, allocate_usf() was implemented to only allocate 1 USF
per TBF, regardless of the available ul_slot mask.

As a result, only 1 slot at max was allocated to any TBF. That's a pity
because usual multislot classes like 12 support up to 2 UL slots per TBF
(in common TS with DL).

This patch reworks allocate_usf() to allocate as many UL multislots as
possible (given mslot class, current USF availability, TFI availability,
related DL TBF slots for the same MS, etc.).

As a result, it can be seen that AllocTest results change substantially
and maximum concurrent TBF allocation drops under some conditions.
That happens due to more USFs being reserved (because each TBF has now
more UL slots reserved). Hence now USF exhaustion becomes the usual
limitation factor as per the number of concurrent TBFs than can be
handled per TRX (as opposed to TFIs previously).

Some of the biggest limitations in test appear though because really
high end multislot classes are used, which can consume high volumes of
UL slots (USFs), and which are probably not the most extended devices in
the field.

Moreover, in general the curren timeslot allocator for a given
multislot class will in general try to optimize the DL side gathering
most of the possible timeslots there. That means, for instance on ms
class 12 (4 Tx, 4Rx, 5 Sum), 4 DL slots and 1 UL slot will still be
selected. But in the case where only 3 PDCHs are available, then with
this new multi-slot UL support a TBF will reserve 3 DL slots and 2 UL
slots, while before this patch it would only taken 1 UL slot instead of
2.

This USF exhaustion situation can be improved in the future by
parametrizing (VTY command?) the maximum amount of UL slots that a TBF
can reserve, making for instance a default value of 2, meaning usual
classes can gather up 2 UL timelosts at a time while forbidding high-end
hungry classes to gather up to 8 UL timeslots.

Another approach would be to dynamically limit the amount of allowed
reservable UL timeslots based on current USF reservation load.

Related: OS#2282
Change-Id: Id97cc6e3b769511b591b1694549e0dac55227c43
2021-02-24 13:48:01 +00:00
Pau Espin Pedrol
50aa492b85 Bump version: 0.8.0.396-fe8d-dirty → 0.9.0
Change-Id: I4b9405df5f40e8f2724ba8aa8f88e2602c1e8374
2021-02-23 14:41:01 +01:00
Pau Espin Pedrol
fe8de457ac Use ALPHA value received in SI13 from PCUIF
The old VTY command is marked as deprecated and still overrides the use
in case it's used.

Related: SYS#5358
Depends: libosmocore.git Change-Id I74fb0a3afc1ac4aadbfc609b882d929401f790eb
Depends: osmo-bsc.git Change-Id I8b97ea11bad5fe05f2f634945b5703ee9abde81d
Change-Id: I46f2a955b157a409055fca7fb917dc4f75482426
2021-02-22 12:29:12 +00:00
Pau Espin Pedrol
4df2658884 find_multi_slots: Avoid multiple calls to mslot_class_get_type()
Change-Id: I9cda52befe32a7727ab479bc151d10106fb94688
2021-02-19 17:35:11 +01:00
Pau Espin Pedrol
10475f5832 find_multi_slots: Mark mslot_class properties const
This way it's clear for reader that those variables are never touched
during the function.

Change-Id: Ief038c75bc02d0e987135f29599014eab88447dd
2021-02-19 17:33:23 +01:00
Pau Espin Pedrol
dfbf3d2c09 find_multi_slots: Avoid multiple calls to mslot_class_get_rx()
Change-Id: I06c97d81636e251f81c26f3aa042c70717be083a
2021-02-19 17:32:14 +01:00
Pau Espin Pedrol
47a3b780db find_multi_slots: Avoid calling mslot_class_get_tx() on each iteration
Change-Id: I397495c158bce1c2715991371368b0d84cf69261
2021-02-19 16:56:36 +01:00
Pau Espin Pedrol
1f8e229221 Use NULL as default value for pointer type
Using zero there is confusing since it's a pointer to an integer.

Change-Id: Ief2368954c71005c529e3eea3fee5df2630e44c1
2021-02-19 16:49:24 +01:00
Pau Espin Pedrol
00f52cc3d6 tests: Replace deprecated API log_set_print_filename
Change-Id: Idcc4875592c81f17ac98c4f6098492b3d9dd33d2
2021-02-19 16:24:11 +01:00
Pau Espin Pedrol
b18d2a5fd9 tests: Explicitly drop category from log
Let's disable category here since we don't care about its formatting here.

In any case, every test relying on logging output validation should
always explicitly state the config to avoid issues in the future if
default values change.

Change-Id: I7f9c56313cfaa74ebe666f44763a83d8102f5484
Related: OS#5034
2021-02-19 16:24:11 +01:00
Alexander Couzens
151bc5b0d3 gprs_bssgp: use gprs_ns2_sns_add_bind() to allow the NSE to use the binds for IP-SNS configuration
The gprs_ns2 now requires to specify every bind which should be used by the NSE for IP-SNS

Related: SYS#5354
Depends: I9ab8092bf286e7d90e92f5702a5404425e959c84 (libosmocore)
Change-Id: I35c987224ce098f7ee9f189ce0fce9e68ad3feac
2021-02-19 12:00:47 +00:00
Pau Espin Pedrol
9345eb34d3 sched: Avoid selecting TBF to tx NACC Dl msg if no TFI is assigned
The DL NACC related message (PKT Cell Neighbor Data/Change Continue)
are filled with the TFI of the target TBF. Hence, only select the tbf
for NACC transmission if the related TBF already has a TFI assigned.

Otherwise, "OSMO_ASSERT(tbf_is_tfi_assigned(tbf));" in nacc_fsm.c when
generating messages may be hit.

Related: SYS#4909
Change-Id: I72b2dff28aacdb04909c098c94834ff79f55b31d
2021-02-18 15:50:47 +01:00
Pau Espin Pedrol
cf6b3bc08f cosmetic: fix line indentation
Change-Id: Ia8335ce5c005885e4db1864faf775c4bff509c53
2021-02-18 14:02:50 +01:00
Pau Espin Pedrol
66e8a49734 vty: Write 'neighbor resolution' config to file
Fixes: c0a250d17d
Related: SYS#4909
Change-Id: I44eef3826939e05ba88e0c5a67e1fef535582ba7
2021-02-17 20:25:03 +01:00
Alexander Couzens
94a367f224 gprs_bssgp: rename gprs_ns_config -> gprs_ns_update_config
Improve the naming of the function to match it's purpose.

Related: SYS#5354
Change-Id: Ib8e4ae734503fd6f6695d9d6767d809e1bf79d22
2021-02-16 21:32:47 +00:00
Alexander Couzens
13a12e2e3b gprs_bssgp: rework and rename ns_create_nsvc -> ns_configure_nse
Add support for multiple SNS endpoints.
Move the NSE allocation to the top in preparation of IP-SNS binds.
The future gprs_ns2 library will require to manual add every bind to the NSE for IP-SNS.
Rename the function to match more it's purpose.

Related: SYS#5354
Change-Id: I69cf48ab168a6dca4f649157bf6556d7cd27d4fb
2021-02-16 21:32:47 +00:00
Pau Espin Pedrol
9313da517d nacc_fsm: Improve log when sending RIM RAN-INFO to gather SI from remote cell
Change-Id: I6972f46f0f3223ce00672178e5610bd3a012fb19
2021-02-15 12:12:51 +01:00
Pau Espin Pedrol
a78f0d5bfe nacc_fsm: Support receiving Pkt Cell Chg Notif while in some advanced states
Related: SYS#4909
Change-Id: Iee9cb67bf2c0c6f36b788498f4ef2672e33204b7
2021-02-11 18:49:03 +01:00
Pau Espin Pedrol
8e69fc0c3a nacc_fsm: nacc_fsm: Support receiving Pkt Cell Change Notify in state WAIT_REQUEST_SI
Similar to what's done in the previous commit, but this time when we are
further forward in the resolution process.
This can be triggered for instance because we are taking too much time
to resolve and MS has timer to retransmit the Pkt cell Change Notify in
case no response was received in time.

This commit fixes osmo-pcu exiting due to ASSERT(0) since the event was
already accepted but not being handled in the state function.

Related: SYS#4909
Change-Id: I0c29e5979fec6eebe9dfb151907a4cd2f5e4a737
2021-02-11 13:23:52 +01:00
Pau Espin Pedrol
069a637be8 nacc_fsm: Support receiving Pkt Cell Change Notify in state WAIT_RESOLVE_RAC_CI
If the message is a duplicate (same tgt cell), simply ignore it.
If the message contains a different tgt cell, restart the resolution:
* Avoid re-creating the socket in that case
* Avoid potentially picking a CTRL response for an older request

Related: SYS#4909
Change-Id: Ia2ed2580bbbdd6d3464833257b0dcb8ec6f8d699
2021-02-11 13:17:16 +01:00
Pau Espin Pedrol
41ff273226 nacc_fsm: Remove NACC_EV_RX_SI from in_event_mask of some states
We don't care about those messages anymore if we already transitioned
further than NACC_ST_WAIT_REQUEST_SI. Furthermore, RIM code dispatching
the event to the FSM is only doing it in the mentioned state above.

Related: SYS#4909
Change-Id: If420b49e437ff02073669522408763e5e84fe477
2021-02-11 13:17:16 +01:00
Pau Espin Pedrol
0c10b3cdc1 nacc_fsm: Move code filling struct to helper function
Same filler will be needed in different places since that message can
arrive at different points of time (different states).
It also helps supporting newer key types in the future.

Change-Id: Idfd4db8408f767b1847b04c88047a1c4996e543e
2021-02-11 12:26:24 +01:00
Pau Espin Pedrol
abba102d7b cosmetic: fix typo in comment
Change-Id: I5a384985ef54234e915bf6334125f8d087988d1d
2021-02-08 18:35:13 +01:00
Vadim Yanitskiy
830ca26034 vty: register libosmocore's FSM introspection commands
Change-Id: Id268e44de6eb873138f38720a61cabe589a5d2c8
2021-02-05 23:22:37 +01:00
Pau Espin Pedrol
55aca83098 rlc.h: Fix struct bit fields on big endian systems
Related: OS#5003
Change-Id: I534539cdf197ce5c038752a177aff7efeefc9883
2021-02-04 12:59:40 +01:00
Pau Espin Pedrol
44768f2127 nacc: Avoid RIM procedures targeting cells under same PCU
Now that we have the required System Information in osmo-pcu to craft
the Packet Neigbour Cell Change packet for cells it controls, let's
avoid starting a RIM procedure to gather the SI info, since the SGSN
would end up routing the RIM request back at us and we'd answer back to
ourselves.

This same optimization cannot be done on the first step (CTRL Neighbor
Resolution against BSC), because the PCU cannot know if the target
ARFCN+BSIC requested by the MS is actually managed by a cell under the
PCU or it's another cell managed by another external PCU, because
ARFCN+BSIC keys can be resued among non-neighbor cells. Hence, it shall
always ask the BSC since only it holds the information about neighboring
cells.

Related: OS#4909
Change-Id: I928875b6b66dff55fc12f51b6b1ad919fef7d03b
2021-02-03 08:34:12 +00:00
Pau Espin Pedrol
952cb3d5d7 nacc: Implement Pkt Cell Change Continue retransmission
Use the fact that the MS must answer the RRBP of the Pkt Cell Change
Continue with a CTRL ACK to find out whether the message was received
successfuly or a retransmission is potentially required.

3GPP TS 44.060:
"""
When the mobile station receives the PACKET CELL CHANGE ORDER or
the PACKET CELL CHANGE CONTINUE message the mobile station shall
transmit a PACKET CONTROL ACKNOWLEDGMENT message in the specified
uplink radio block if a valid RRBP field is received as part of the
message; the mobile station may then switch to a new cell.
"""

Related: SYS#4909
Change-Id: I7cc28922e71699598da0ef6eb90136a47d3c002f
2021-02-03 08:34:04 +00:00
Philipp Maier
a58ec61514 gprs_bssgp_rim: add serving BSS NACC application
Answer an incoming RAN INFORMATION REQUEST RIM PDU with RAN INFORMATION
PDU that contains system information type 1, 3 and 13

Depends: osmo-bts I5138ab183793e7eee4dc494318d984e9f1f56932
Change-Id: Id72118120c14984d2fb1b918b41fac4868150d41
Related: SYS#5103
2021-02-02 21:57:09 +01:00
Pau Espin Pedrol
1aef113bb7 nacc: Fix typo in function name
Change-Id: I74857eacf4664508dce70eb0c6dd2acd7bfb72e4
2021-02-01 19:34:53 +01:00
Pau Espin Pedrol
57dcde4242 tbf: Constify some methods
Change-Id: I2681a98583f4fb26a274c75d0279084239f76a68
2021-02-01 18:14:37 +01:00
Pau Espin Pedrol
b71aab5646 tbf: Reuse stored result in variable in check_polling()
Change-Id: Ie6fbe3699bcb4f63f7b617243c769e60881d8aac
2021-02-01 16:37:46 +01:00
Pau Espin Pedrol
4668dc6f07 encoding: Fix comment description of S/P field
Those fields were ment to be 0 (non-valid), just the comments were
copied over from somewhere else, and they are misleading.

Change-Id: Ic95853e115f60c65f7f11187d49d6e870d08c7bb
2021-02-01 16:37:46 +01:00
Pau Espin Pedrol
79784d0249 Move src/tbf.txt to doc/
At least there it will pass less unnoticed, I just discovered this file
by chance.

Change-Id: I65a443ae498ae4c5e837e5a069fd87863f259152
2021-02-01 14:28:35 +01:00
Pau Espin Pedrol
fdbcea3532 Drop comment about an already implemented TODO
The comment target is already implemented just above it.

Change-Id: I05534bbbad24ad8ba602244b834cdbadcabcc7ec
2021-02-01 14:02:15 +01:00
Pau Espin Pedrol
05be90367a Update TS 04.60 references to new TS 44.060
Change-Id: Ib7c3a74b502b2251da2f7b9d6d711f3e32133bc3
2021-02-01 13:06:45 +01:00
Vadim Yanitskiy
3877848e22 contrib/osmo-pcu.spec.in: add missing libosmoctrl dependency
Change-Id: I37c478f3f430f72620af17f1c3329f6ee8515402
Fixes: Id35f40d05f3e081f32fddbf1fa34cb338db452ca
2021-01-30 19:14:55 +00:00
Vadim Yanitskiy
e0fb465678 contrib/osmo-pcu.spec.in: require libosmo* version 1.4.0
Keep this file in sync with the requirements in configure.ac.

Change-Id: I8fec52d0d77778775df0022da6cd53a213c057bc
2021-01-30 19:14:55 +00:00
Vadim Yanitskiy
809dc8b046 tests/rlcmac: add more test vectors for Packet Resource Request
All vectors should be valid, since they were generated by an MS.
As can be seen, osmo-pcu fails to decode one of the vectors.

Change-Id: I37a2ddd394eeffa1cae0f3e419eeee0200a57fcf
OS#4955

Change-Id: Ib5677048f5668185ffe752f97c97d5612eee4d72
2021-01-30 14:14:24 +00:00
Pau Espin Pedrol
f7e1df0da2 nacc: Improve log line failing to establish CTRL neigh conn
Change-Id: Ic92158f9fe986ff427ee2ed2cfbf95549e348746
2021-01-29 16:48:54 +01:00
Pau Espin Pedrol
ced5c1f5c8 doc: Introduce section documenting NACC support
Change-Id: I74e4828ed1b99a7f4d28ea4797c93ee85c0068cb
2021-01-29 15:52:44 +01:00
Pau Espin Pedrol
7b7fb225ce doc: Mark PCU node red in network node diagram
Change-Id: I81ab7bf144eacbfab94d3cee3d75af5a05e1c71a
2021-01-29 13:04:36 +01:00
Pau Espin Pedrol
a06ac18d22 NACC: Send only Pkt Cell Chg Continue if SI retrieve fails
If fore some reason we fail to fetch SI of target cell, we move
directly to NACC_ST_TX_CELL_CHG_CONTINUE in order to submit a Cell
Change Continue against the MS without providing any Packet Neighbor
Cell Data beforehand, as per spec that's probably the best we can do
in this scenario (TS 44.060):
"""
1)  The network responds with a PACKET CELL CHANGE CONTINUE message.
If a mobile station as response to a PACKET CELL CHANGE NOTIFICATION
message receives a PACKET CELL CHANGE CONTINUE message without receiving
any neighbour cell system information, the mobile station shall stop timer
T3208, stop timer T3210 if still running, leave CCN mode and continue cell
reselection in NC0/NC1 mode.
"""

This commit also fixes a use-after-free triggered by TTCN3 test
TC_nacc_outbound_rac_ci-resolve_fail_parse_response, where the "cmd"
pointer passed to nacc_fsm_ctrl_reply_cb() was freed during FSM
termination (its talloc ctx was under ctx->neigh_ctrl_conn) and the
libosmocore code calling that callback was later on accessing
cmd->defer.
Since due to this change the FSM is no longer syncrhonously freed, the
issue is gone.

Related: SYS#4909
Change-Id: Ie3f12a08ad611b1086d3f4ab7c3d34af43c07961
2021-01-29 12:59:30 +01:00
Pau Espin Pedrol
41a22a7ab8 NACC: Configure neighbor and SI resolution timeout values
Upon timeout, we move directly to NACC_ST_TX_CELL_CHG_CONTINUE in order
to submit a Cell Change Continue against the MS without providing any
Packet Neighbor Cell Data beforehand, as per spec that's probably the
best we can do in this scenario (TS 44.060):
"""
1)  The network responds with a PACKET CELL CHANGE CONTINUE message.
If a mobile station as response to a PACKET CELL CHANGE NOTIFICATION
message receives a PACKET CELL CHANGE CONTINUE message without receiving
any neighbour cell system information, the mobile station shall stop timer
T3208, stop timer T3210 if still running, leave CCN mode and continue cell
reselection in NC0/NC1 mode.
"""

Related: SYS#4909
Change-Id: Ia9932ab082ec095294e85dc4d532046970e17986
2021-01-29 12:59:30 +01:00
Pau Espin Pedrol
ab7159f6ec NACC: allow setting keep time for entries in neigh and si cache
Related: SYS#4909
Change-Id: Ifa336aa27dd88ff5b78dbc5a2799740f542bb369
2021-01-29 12:59:30 +01:00
Pau Espin Pedrol
c0805e6389 NACC: delay CTRL conn socket init until it's needed
This way, we don't open a socket and do the IPA handshake in the event
the request is already cached.

Related: SYS#4909
Change-Id: Ib1ea85e1196c8b9dc40c8837ab5d4a54f2a1f2d4
2021-01-29 12:59:30 +01:00
Pau Espin Pedrol
202a47886c NACC: Fix crash freeing struct if CTRL conn was refused during alloc
Older versions of osmo_ctrl_conn_alloc() may not properly initialize
write_queue.bfd.fd to -1, which means if osmo_sock_init2_ofd() failed
during nacc_fsm_alloc(), the destructor would wrongly enter the conditon
where the whole structure is set and unregister the unregistered fd.

Related: libosmocore Change-Id I98f744d2880fbb883719cdf1d3eb31f2b22a13b6
Related: SYS#4909
Change-Id: I253bd9087b1f7ab039aa1127e9dc586f5106905a
2021-01-29 12:59:30 +01:00
Pau Espin Pedrol
c0a250d17d Introduce NACC support
A new nacc_fsm is introduced per MS object, with its partner priv
structure struct nacc_fsm_ctx, which exists and is available in the MS
object only during the duration of the NACC procedure.

The NACC context is created on an MS whenever a Pkt Cell Change
Notification is received on Uplink RLCMAC, which asks for neighbor
information of a given ARFCN+BSIC.

First, the target ARFCN+BSIC needs to be translated into a CGI-PS
(RAC+CI) address. That's done by asking the BSC through the Neighbour
Resolution Service available in osmo-bsc using the CTRL interface.

Once the CGI-PS of the target cell is known, PCU starts a RIM RAN-INFO
request against the SGSN (which will route the request as needed), and
wait for a response containing the SI bits from the target cell.

After the SI are received, the scheduler is instructed to eventually
poll a TBF for the MS originating the CCN, so that we can send the SI
encapsulated into multiple Packet Neighbor Cell Data messages on the
downlink.

One all the SI bits are sent, the scheduler is instructed to send a
Packet Cell Change Continue message.

Once the message above has been sent, the FSM autodestroys itself.

Caches are also introduced in this patch which allows for re-using
recently known translations ARFCN+BSIC -> CGI-PS and CGI-PS -> SI_INFO
respectively.

Change-Id: Id35f40d05f3e081f32fddbf1fa34cb338db452ca
2021-01-29 12:59:30 +01:00
Pau Espin Pedrol
1e77ca88af tbf: Make tbf_ms() param const
Change-Id: I041c564b15d17d05ce97ea0085fcd9192a346578
2021-01-29 11:54:18 +00:00
Alexander Couzens
54211b1e1b gprs_ns2: migrate to the new vty syntax
This also changes the vty configuration. If only timeout has been
configured for ns the new configuration is compatible.

For further information see:
https://osmocom.org/projects/libosmocore/wiki/Network_service_(NS)

Depends-on: I8c3f2afecc74b78f7f914f7dce166cbcb63444eb (libosmocore)
Change-Id: I14af821a8d1fda670643c3d5f81299a3abf3c583
2021-01-28 19:55:14 +01:00
Alexander Couzens
f7ec52560f follow gprs_ns2 API enum changes
All gprs_ns2 enums have now GPRS_NS2 as prefix.

Depends-on: I548ff12f7277cbb7e1a630a3dc02b738ce89be72 (libosmocore)
Change-Id: Ifdc7956318c07d680feab33c22bc2c6f20927bf9
2021-01-28 12:55:41 +00:00
Oliver Smith
91e3567a15 configure.ac: set -std=gnu11
Change-Id: Iac2c0b14252c46aec2b00d46800fcc9f87a5a586
2021-01-28 09:28:34 +00:00
Pau Espin Pedrol
1a5439b739 sched: Avoid picking TBF with nacked dl blocks when GMSK is required
Sine we don't yet implement properly all resegmentation of blocks from
same MCS family type, when requiring a GMSK DL block (due to GPRS+EGPRS
multiplexing limitations) we need to skip retransmitions, otherwise we'd
be incorrectly picking a DL block which was already built with a
potentially higher MCS value.

The "DL_PRIO_NEW_DATA" prio serves two purposes:
* There's new data to send
* There's some nacked data to be retransmitted

The 2nd purpose has, later on, more priority over the 1st one when the tbf
is selected (see gprs_rlcmac_dl_tbf::take_next_bsn()).

Until now we were handling correctly the case where the tbf was skipped
in case the prio was to resend unacked data (DL_PRIO_SENT_DATA), but
was incorrectly selected when it'd send nacked data. Let's fix it by
specifically checking w->resend_needed() < 0.

Change-Id: I253de8e1a190a9adb56160f38892c9e43e2c0272
2021-01-26 16:26:34 +01:00
Pau Espin Pedrol
fc464935a4 Fix Dl EGPRS data blocks being generated occasionally on GPRS TBFs
Under some circumstances, it could happen that a DL TBF is created as a
GPRS TBF due to not yet having enough information of the MS, and only
after the TBF is created the PCU gains that information and upgrades the
MS mode to "EGPRS". Hence, there's the possibility to run into a
situation where a GPRS TBF is attached to a EGPRS MS.

It may also happen sometimes that despite the TBF and the MS be EGPRS,
there's need to further limit the DL MCS to use, eg. MCS1-4 (GMSK).

As a result, when asking for the current DL (M)CS to use, we must tell
the MS which kind of limitations we want to apply. The later reasoning
was already implemented when GPRS+EGPRS multiplexing was added, but the
former was not being checked. Hence, by further spreading through the
call stack the "req_kind_mode" we match both cases.

Related: OS#4973
Change-Id: Ic0276ce045660713129f0c72f1158a3321c5977f
2021-01-25 16:56:59 +01:00
Pau Espin Pedrol
201da4e5b2 ms: Properly handle EGPRS_GMSK mode in ms_max_cs_dl/ul()
Change-Id: Ied3e02a12145112fafa12282ed7aefa5b0fa6eb6
2021-01-25 16:19:37 +01:00
Pau Espin Pedrol
7bb8cd683c ms: Set proper initial MCS values setting mode EGPRS_GMSK
Before this patch, shared logic with EGPRS case would allow keeping
MCS>4.

Change-Id: I94cbf0c120fd37deb2dfd077d35b3811c7da0675
2021-01-25 16:19:37 +01:00
Pau Espin Pedrol
7963edba09 encoding: fix typos in comment
Change-Id: I0867935ad08d6e49c62e061742d3d76eeac35844
2021-01-25 16:19:37 +01:00
Pau Espin Pedrol
2238228e3c tbf: Drop always-true condition checking for MS
The TBF can sometimes be detached from an MS, for eg. when switching
from one MS object to another due to them being merged after we found
duplicate objects upon receiving new information from it, but that
change is instantaneous so it shouldn't be a problem. The only other way
where an MS can be detached from an MS is during the end of its (or the
MS) life, where it is not sending data anymore.

Hence, it is safe to drop those checks for MS not being null. Those
being trigger, it should be considered a bug.

Change-Id: If292a53a09a64664031e756bff4735b9c6ee8651
2021-01-25 16:18:19 +01:00
Pau Espin Pedrol
8f1701fe24 sched: Check if egprs is enabled in TBF rather than MS being egprs capable
It could happen as of current implementation that a TBF was created as
GPRS due to the MS being non-egprs, and later on the MS was upgraded to
EGPRS due to newly received information from the MS.
Hence, in order to infer if the data block is EGPRS or GPRS, let's
better check for the TBF info, which is the one really mandating the
kind of dl block to generate.

Change-Id: I49720fb3a69ca972cd1973de937ac8ee77615431
2021-01-25 11:31:59 +01:00
Pau Espin Pedrol
0298c0b6a0 ms: Drop always-false check
MS is always assigned to a BTS, since it's set during MS constructor.
Hence, the check removed in this patch would never hold true (and if it
did, it'd be a bug).

Change-Id: I86a71c64623f7bec031226938a54306148370ffb
2021-01-25 11:20:46 +01:00
Pau Espin Pedrol
a100a6bc56 gprs_pcu: Use libosmocore osmo_cgi_ps_cmp API
it was noticed that gprs_pcu_get_bts_by_cgi_ps() sometimes failed to
return the BTS even if the CGI-PS fields matched, probably due to memcmp
checking too padding bytes which may not be zero-initialized in one of
the two memory regions being checked. Let's be on the safe side and use
libosmocore APIs to check them.

Depends: libosmocore.git Change-Id I00e329bc5be8674b30267dec238e7656ddfc21db
Change-Id: I7c8ee2c447634e45b367bb8f84adf0140ae48591
2021-01-22 20:40:08 +01:00
Pau Espin Pedrol
3a27102e59 Initial handling support for RIM messages
This code doesn't do anything yet app-related with the received RIM
messages, but already provides the initial infrastructure to handle them
in the future, and does first checkings.

Related: SYS#5103
Change-Id: Ia0ade0e97ea781ec655439c008b6cefaf3e90dec
2021-01-22 16:37:12 +01:00
Pau Espin Pedrol
db5e339da4 Get rid of singleton gprs_bssgp_pcu_current_bctx()
Access it from existing pointers instead.

Change-Id: I77455da5221090ebea142ecd49d5dba0065bfc5c
2021-01-21 18:02:57 +01:00
Pau Espin Pedrol
2e6b60df45 bts: Store RAC+CI from info_ind
Having those values at hand will be needed later for RIM / NACC related
purposes.

Change-Id: Ia3596e9e81cd71443be2cc6f2450bb7f91d2667d
2021-01-20 15:00:38 +01:00
Pau Espin Pedrol
d1049dc8cc Allow multiple bts objects in PCU
This patch doesn't really tests whether osmo-pcu can work on a multi-bts
environment, but it prepares the data structures to be able to do so at
any later point in time.

Change-Id: I6b10913f46c19d438c4e250a436a7446694b725a
2021-01-20 12:36:21 +01:00
Pau Espin Pedrol
e91c4c72b1 Convert osmo_bts_sock.cpp to C
There's no real point in using C++ there, and using C++ makes the
compiler fail to use llist_head in multi-bts patches added later due to:
"""
'offsetof' within non-standard-layout type is conditionally-supported
"""

Change-Id: I8965b5cc5a713e64788b5b6aa183d3035341ddbb
2021-01-19 16:28:13 +01:00
Pau Espin Pedrol
906aafc9e2 Move tbf::free_all static methods to proper object files
Move each method to the object on which they operate, be it a trx or a
pdch ts.

Change-Id: Ida715cbf384431d37b2b192fbd7882957c93a4d1
2021-01-19 16:28:13 +01:00
Pau Espin Pedrol
8a35e640a3 Convert gprs_bssgp_pcu.cpp to C
There's no real use of C++ in that file, and it causes problems when
using llist_head entry macros in future patches adding initial support
for multiple BTS in PCU object, so let's move it to plain C.

Change-Id: Ic771a89fd78b5e66151a5384f0ff6a8895589466
2021-01-19 16:28:13 +01:00
Pau Espin Pedrol
4a5209d8bc Get rid of unused gsm_timer.{cpp,h}
Those files are not really being used other than for calling
get_current_fn() which is just a placeholder to call
bts_current_frame_number on the global bts object.

Change-Id: I6d50a8c15c1de5e2a308a24b313a7776f94ae54f
2021-01-19 16:28:13 +01:00
Pau Espin Pedrol
289f90048b bts: combine bts_{init,cleanup} into consturctor/destructor methods
The bts_init/cleanup functions were kept during the C and C++ structure
merge process to make the patch simpler. It's not needed anymore,
let's move all the destructor logic into one function and keep that
together.

Change-Id: I73a9457d5c92f62261561ef6afe392953576aec4
2021-01-19 16:28:13 +01:00
Pau Espin Pedrol
0ece97d718 Rename 'bts_data' leftovers to 'bts'
Before, we used tho have a BTs object split into 2 parts, a C
gprs_rlcmac_bts struct and a C++ BTS struct, and "bts_data" naming was
used to distinguish them in variable names. Nowadays the struct is
finally combined into one, so there's no point in using this "bts_data"
terminology, we use always "bts".

Change-Id: I9852bf439292d1abc70711bea65698b21bde0ee8
2021-01-19 16:28:13 +01:00
Pau Espin Pedrol
a45aafd39c Get rid of bts singletons
There's no BTS single global object anymore, get rid of those APIs. Move
users to use "pcu->bts", which will evolve to a linked list in the
future.

Change-Id: I9cf762b0d3cb9e2cc3582727e07fa82c8e183ec5
2021-01-19 16:28:13 +01:00
Pau Espin Pedrol
2182e627cd Unify BTS into a C usable structure
Previous work on BTS class started to get stuff out of the C++ struct
 into a C struct (BTS -> struct gprs_glcmac_bts) so that some parts of
it were accessible from C code. Doing so, however, ended up being messy
too, since all code needs to be switching from one object to another,
which actually refer to the same logical component.

Let's instead rejoin the structures and make sure the struct is
accessible and usable from both C and C++ code by rewriting all methods
to be C compatible and converting 3 allocated suboject as pointers.
This way BTS can internally still use those C++ objects while providing
a clean APi to both C and C++ code.

Change-Id: I7d12c896c5ded659ca9d3bff4cf3a3fc857db9dd
2021-01-19 16:28:10 +01:00
Pau Espin Pedrol
793583ea21 Fix configuration mess of initial_cs/mcs between PCUIF and VTY
Both values (optionally) set (forced) by VTY and the values received
from PCUIF were stored in the same variable, meaning that for instance
the PCUIF values wouldn't really be used if someone applied eg "no cs"
during runtime.

This commit does something similar to what was already done for the
max_(m)cs fields. We store PCUIF values in one place and VTY ones in
another place, and then trigger a bts object internal process to find
out exactly which initial CS should it be using.

Change-Id: I80a6ba401f9c0c85bdf6e0cc99a9d2008d31e1b0
2021-01-18 11:57:14 +01:00
Pau Espin Pedrol
f473ec9d7a Move llc_* fields from BTS to PCU
Change-Id: Iffb916e53fdf99164ad07cd19e4b35a64136307e
2021-01-18 11:54:57 +01:00
Pau Espin Pedrol
519d071131 Move ws_* fields from BTS to PCU
Change-Id: I997bc52f0d924c8f2a0b1d6cf23af98828ad4258
2021-01-18 11:54:57 +01:00
Pau Espin Pedrol
47f15fb6fd tests/tbf: Allocate PCU per test instead of globally
Otherwise some state may be left from one test to another.

Change-Id: I18e2fe7dd1cc5940570252a2a6a106de49d8a7dd
2021-01-18 11:54:57 +01:00
Pau Espin Pedrol
e891222920 Move fc_* fields from BTS to PCU
Change-Id: I816d49e732d0fc7a3c9aa1f0e9a83b83d25e6a32
2021-01-18 11:54:57 +01:00
Pau Espin Pedrol
113fb419ec Move ns_dialect field from BTS to PCU
Change-Id: Iffb22b776b91f93d6d2a7ccfa47deeecc22c33f0
2021-01-18 11:54:57 +01:00
Pau Espin Pedrol
54b159aab9 Move (m)cs_lqual_ranges fields from BTS to PCU
Change-Id: I39e2fc7e229851610d797c594d84902af6079411
2021-01-18 11:54:57 +01:00
Pau Espin Pedrol
ad79b857cd Move cs_downgrade_threshold field from BTS to PCU
Change-Id: I3e1c65eb3cccff565d5d84588bdce93a47909a0f
2021-01-18 11:54:57 +01:00
Pau Espin Pedrol
e8dcf64881 Move cs_adj* fields from BTS to PCU
Change-Id: I2b00a83279dccd4feeeeb95e34878c4405e7972c
2021-01-18 11:54:57 +01:00
Pau Espin Pedrol
97296b299c Move dl_arq_type field from BTS to PCU
Change-Id: I0b82ab59edd58d60e5581c707dc49f58de0ba203
2021-01-18 11:54:53 +01:00
Pau Espin Pedrol
05f9f59a67 Move dl_tbf_preemptive_retransmission field from BTS to PCU
Change-Id: I3ab32fcafe83f3ecb116a5b8a05f58f3fddc5451
2021-01-18 11:38:38 +01:00
Pau Espin Pedrol
a281495008 Move alpha,gamma fields from BTS to PCU
Change-Id: I2fdd9c8a7393157183fff64084bb10e2a3b1dc63
2021-01-18 11:38:38 +01:00
Pau Espin Pedrol
03de898d19 Move force_two_phase field from BTS to PCU
Change-Id: I68a6e032f725cde87992b99f039c5280e912faf7
2021-01-18 10:37:05 +00:00
Pau Espin Pedrol
924aaad4bc Move T_defs_pcu from BTS to PCU object
Change-Id: I0cac5c12dff2e90b52d00383a00b4b94a9603a0a
2021-01-18 10:37:05 +00:00
Pau Espin Pedrol
ac3fd12026 Split PCU global PCU object from BTS object
Currently the BTS object (and gprs_rlcmac_bts struct) are used to hold
both PCU global fields and BTS specific fields, all mangled together.
The BTS is even accessed in lots of places by means of a singleton.

This patch introduces a new struct gprs_pcu object aimed at holding all
global state, and several fields are already moved from BTS to it. The
new object can be accessed as global variable "the_pcu", reusing and
including an already exisitng "the_pcu" global variable only used for
bssgp related purposes so far.

This is only a first step towards having a complete split global pcu and
BTS, some fields are still kept in BTS and will be moved over follow-up
smaller patches in the future (since this patch is already quite big).
So far, the code still only supports one BTS, which can be accessed
using the_pcu->bts. In the future that field will be replaced with a
list, and the BTS singletons will be removed.

The cur_fn output changes in TbfTest are actually a side effect fix,
since the singleton main_bts() now points internally to the_pcu->bts,
hence the same we allocate and assign in the test. Beforehand, "the_bts"
was allocated in the stack while main_bts() still returned an unrelated
singleton BTS object instance.

Related: OS#4935
Change-Id: I88e3c6471b80245ce3798223f1a61190f14aa840
2021-01-18 10:37:05 +00:00
Alexander Couzens
695ce77167 gprs_rlc_ts_alloc: ensure no rolling slots are allocated
When allocating multiple slots for a UE the following example
is not allowed 'UU----UU' for a UE class 12.
The time slot number can not roll over 7 and move to 0.
44.060 or 45.002 only specifies contigous however it was unclear
it this is an allowed pattern.

Only the example 45.002 B.3 in release 12 cleared this up.
It gives an example for a multi slot class 5 UE which has 7 possible
configuration this means the rolled over is not allowed.

Multislot class type 2 UE doesn't have this limitation.
Further if a UE supports 8 time slots this is not a limitation because
the window size (45.002 B.1) can include all time slots.

Releated: SYS#5073
Change-Id: I16019bdbe741b37b83b62749b840a3b7f4ddc6c7
2021-01-15 15:50:41 +00:00
Pau Espin Pedrol
54faf023be Workaround ASan false positive runtime errors under some platforms
Under some platforms (RPI4, ARM) container older ASan, it will log false
positive log errors which will make unit test fail because then output
changes:
"""
pcu_l1_if.cpp:847:2: runtime error: member access within misaligned address 0xb3f0b78c for type 'struct GprsMs', which requires 8 byte alignment
"""

The pointer is indeed misaligned, but it's not actually a bug, because
the pointer is never derreferenced. That happens during
llist_for_each_entry operation where it does cast the pointer but it
only checks if the list has actually reached the end.

To workaround the issue, simply defer casting it by using llist_for_each
instead, where the pointer is assigned only in the case it really points
to a GprsMS struct.

Change-Id: I149fb42706501eb33f9c6fe48f76a03ddee5954a
2021-01-14 12:12:45 +01:00
Oliver Smith
f842e06c88 contrib/jenkins: don't build osmo-gsm-manuals
Related: OS#4912
Change-Id: If45bb7d4958b200ca6b5d1c5b8a52eba06944909
2021-01-13 17:30:56 +00:00
Vadim Yanitskiy
eb70a4098f bts: fix uninitialized memaccess in BTS::send_gsmtap()
Change-Id: I7c5105c9e8a2c680dc8b24cc5e3bda6f7405a3ee
Fixes: CID#216511
2021-01-13 13:58:16 +01:00
Vadim Yanitskiy
55022ea1b1 bts: fix uninitialized memaccess in BTS::send_gsmtap_rach()
Unfortunately, RACH.ind on the PCU interface contains no Uplink
measurements: neiter RSSI nor C/I.  In order to avoid sending
garbage, let's zero-initialize 'struct pcu_l1_meas'.

Change-Id: I8c3210c428da17d23d798f3ef9df941ded6e162a
Fixes: CID#216512
2021-01-13 13:57:13 +01:00
Pau Espin Pedrol
f5a251bcee gprs_ms: Mark ms_ctrg_desc static
Change-Id: I3b1f0d0ee932a97414375a679962356c9178c2eb
2021-01-12 20:57:56 +01:00
Pau Espin Pedrol
1e00947c29 AllocTest: Avoid queuing tons of to-be-freed ms
When both TBFs (Dl, Ul), are detached, ms_detach_tbf() will call
ms_start_timer() which will hold a reference of the MS (ms_ref()) and
wait for X seconds (VTY config, T=-2030, 60 seconds by default) before
unrefing the MS, which will trigger ms_update_status() finally (ref==0)
and will in turn call cb.ms_idle(), which will tell the ms_storage to
free the MS.

This mechanism is used to keep MS objects around for a certain time so
that when new TBFs are established, we have cached interesting
information about the MS, ready to use.

However, in AllocTest, tons of MS are allocated in a loop calling a
function (such as test_alloc_b_ul_dl()). In that function, a BTS is
allocated in the stack and at the end of the function BTS::cleanup() is
called due to implicit destructor, which ends up calling
ms_storage::cleanup() which removes all MS from its list and frees them
*if they are not idle*. The problem here, is that due to T=-2030, an
extra reference is hold and hence the ms is not considered idle
(ms_is_idle() checks ms->ref==0). As a result, the MS is never freed,
because we don't use libosmocore mainloop here (and in any case, it
would take 60 seconds to free it).

By setting the timeout of T=-2030 to 0, ms_start_timer will avoid using
the timer and will also avoid holding the extra reference, hence
allowing ms_storage to free the object during cleanup().

This fix really helps in improving performance for AllocTest specially
after MS object contains a rate_ctr. As tons of MS objects were left
alive, they stood in the rate_ctr single per-process queue, making the
test last crazy amount of time and spending 50% of the time or more
iterating the list full of MS related rate counters.

Change-Id: I6b6ebe8903e4fe76da5e09b02b6ef28542007b6c
2021-01-12 18:01:53 +00:00
Pau Espin Pedrol
bed48cc14f ms: Replace struct var with rate_ctr
Let's use usual osmocom rate_ctr instead of having one variable +
setter/getter functions, so we can easily add new counters and also
because it makes code more clear (no need to look at what the "update"
function is doing).

Using rate counter also provides info about how recently the MS has been
interacting with the network.

Related: OS#4907
Change-Id: I744507fde4291955c1dbbb9739b18a12a80145b1
2021-01-12 18:01:53 +00:00
Pau Espin Pedrol
c2d3625bf6 tbf: remove 'software error' logs from tbf_free
It is expected that the tbf object is freed at any moment in time, for
instance if osmo-pcu drops PCUIF connection with osmo-bts. I couldn't
find any reason why it would e dangerous to free the tbf, so let's
remove this message.

related: OS#4779
Change-Id: I4ab5ccaa5bf6257b18d8fd5ba06baab083821817
2021-01-11 19:17:05 +01:00
Eric
1188167a92 tbf: add virtual destructor
This ensures spec compliance, because currently the base class
destructor would be called through a base class pointer to derived
class instead of the most derived one, which ist unexpected and actually
undefined behavior in c++11 and beyond.

Change-Id: Ic4abde1658a983bb0ccf9a526177dce50ff6dc23
2021-01-11 17:55:07 +00:00
Vadim Yanitskiy
480c8acc8b gprs_rlcmac_sched: fix incorrect SBA frame number assignment
There is a big difference between:

  if ((a = foo() != 0xffffffff)) { ... }

and

  if ((a = foo()) != 0xffffffff) { ... }

In the first case, 'a' is the result of '!=' operation, i.e. either
0 (false) or 1 (true).  In the second case, 'a' will hold the value
returned by foo(), and this is exactly what must have been used in
gprs_rlcmac_rcv_rts_block().

The bug was there since SBA allocation feature was added in 2012.

Change-Id: Ifd607ae8a33382e48f9d9e50a28a4bdf4eaf73a2
Fixes: 07e97cf8a5
Related: CID#215835
2021-01-06 12:34:07 +00:00
Pau Espin Pedrol
d3123ea0f5 doc: Improve CS/MCS GPRS/EGPRS considerations in User Manual
Related: OS#4544
Related: SYS#4869
Change-Id: I7b205f5cab5862058a408f628925beb9f0f60a92
2021-01-05 15:48:58 +00:00
Pau Espin Pedrol
a5b9fb5304 .gitignore: ignore files ending with ~
I'm lately getting this kind of files in git after building:
config.guess~
config.sub~
configure~
install-sh~

Let's ignore them.

Change-Id: I976e3a33f638f4cd19650b9c799e61713d73bf8a
2021-01-05 11:34:45 +01:00
Pau Espin Pedrol
8b4b19a012 tbf: Fix wrong verb used in log message
Change-Id: Id9f8df9a5c0e0f88a811c5d7f06821cb4f30ab93
2021-01-05 10:34:25 +00:00
Pau Espin Pedrol
da971ee502 Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).

GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.

Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.

For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.

Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2021-01-05 10:34:25 +00:00
Alexander Couzens
86fad1ec4e gprs_rlcmac_sched: don't leak a sched_dummy()
Change-Id: Iea0fc51809c78514c85e45e7f499a531c4ea1bcf
2020-12-30 23:30:16 +01:00
Pau Espin Pedrol
d6b913fc39 sched: Convert code handling next_list array to be size independant
Change-Id: Id209fe66f85501437a79f7ca0c8e3cf816177611
2020-12-17 15:27:41 +00:00
Pau Espin Pedrol
b77a2c992e gprs_rlcmac_sched: Use helper structure to store several tbf pointer params
The resulting code is cleaner since it becomes clear the relation
between all those pointers, which are set in one function and used in
another one, passed through the caller of the former two.

Moreover, if more tbf candidates are to be added for other type of
actions, having them in a struct is much easier since only one pointer
is passed.

Change-Id: I55482aa5af7be5a176a7b4879a672ad37e618020
2020-12-17 15:27:41 +00:00
Alexander Couzens
3db4789be8 gprs_ns2: set default dialect to ipaccess
The default pcu dialect is ipaccess. Keep it that way.

Fixes ttcn3 pcu testcases.

Change-Id: I426f507fb8863abd8995bc615dc6a6fa7ae69217
2020-12-16 21:39:07 +01:00
Alexander Couzens
1e6c350a9f ns2: follow ns2 sns api changes
Related: OS#4472, OS#4890
Depends: libosmocore.git I71cdbfb53e361e6112fed5e2712236d797ef3ab2
Change-Id: Id530e5497c17885817493f8a8a9436bc187c88aa
2020-12-16 12:51:08 +01:00
Alexander Couzens
b644fd1f09 ns2: follow changes to add a unique name to all binds
Related: OS#4472, OS#4890
Depends: libosmocore.git I8f1d66b7b3b12da12db8b5e6bd08c1beff085b3e
Change-Id: I3638c7204db576116ba2e20dae27539ce6143bd7
2020-12-16 12:51:04 +01:00
Alexander Couzens
5012e07685 ns2: follow ns2 dialect changes
NS2 introduce a ns dialect to differentiate
between the 4 possible dialects.

Related: OS#4472, OS#4890
Depends: libosmocore.git Ia118bb6f994845d84db09de7a94856f5ca573404
Change-Id: I16dc82c38eb75c2b9d1197640a955fec7df84efc
2020-12-16 12:50:50 +01:00
Harald Welte
398f60e11c migrate to DLBSSGP as log sub-system for BSSGP
Change-Id: I94864c5fa2688fc91b8b6077a14ad098851afdc7
Depends: libosmocore.git Change-Id I506190aae9217c0956e4b5764d1a0c0772268e93
2020-12-12 20:05:51 +01:00
Harald Welte
b645365546 manuals/gb/ns.adoc: Update documentation regarding SNS capability
The SNS capability was added to our Gb stack quite some time ago,
but it seems the manual was not updated.  Let's update the document
accordingly.

The individual sub-sections about the SNS-* messages are empty as
I think lynxis is currently most qualified to fill them in, I'll
ask him to submit a follow-up patch.

Related: SYS#5212
Change-Id: I21e891fc7662b582681cd9bd7568e4b65d357751
2020-12-12 20:05:43 +01:00
Pau Espin Pedrol
95e4a2b3ae rlcmac: Fix typo in MT_PACKET_CELL_CHANGE_NOTIFICATION value_string
Change-Id: I1b327bf955069ab10b2c6aa643ecf975fa23c1b5
2020-12-11 19:57:29 +00:00
Harald Welte
ea2147af90 gb manual: NS is implemented in libosmogb, not libosmocore
Change-Id: Ie833370d9839017cbc508912992fb084ee879fe3
2020-12-10 13:50:11 +00:00
Harald Welte
8938431fdc gb manual: 08.16 -> 48.016 / 08.18 -> 48.018
Change-Id: I10505d9aebe65e1f0952df75b13951e2b40d6997
2020-12-10 13:50:07 +00:00
Pau Espin Pedrol
7d0f9a0ec3 Dl TBF: Get rid of LLC UI dummy blocks following other data
According to:
* 3GPP TS 44.060 version 16.0.0 "9.3.1a Delayed release of downlink Temporary Block Flow"
* 3GPP TS 44.064 version 16.0.0 "6.4.2.2 Unconfirmed Information (UI) Dummy command"

LLC UI Dummy frames are to be used when there no more data to send, only
in order to delay the release of a TBF. Hence, while not incorrect per
se, makes no sense to send those LLC UI Dummy frames inserted into
rlcmac blocks which already contain other LLC frames, since the MS in
that case is already being kept active.
It only makes sense to send those LLC UI Dummy frames when we have
nothing else to send, that is, alone inside a RLCMAC block without other
LLC frames.

Related: OS#4849
Change-Id: Ifae1a7b2b3dfad8df19585063088ba0df2749c8f
2020-12-01 15:57:37 +01:00
Alexander Couzens
30d9a5989e NS2: rework handling of unknown primitive
Use prim_str() method to get the human readable string.
Define unhandled events with a nop in the switch()

Depends-on: Ibf610fbd929dddc4a4e235152447caff522d4eb2 (libosmocore)
Change-Id: I50188afb83ac142e22d4bda4e8050eb4de962e70
2020-12-01 12:07:18 +00:00
Pau Espin Pedrol
022f9e56e5 Implement downgrade to DL MCS1-4 when USF for GPRS_only MS
In previous status, if USF for GPRS-only MS was selected, then EGPRS
TBFs were skipped and either a GPRS TBF was selected or a Dummy Block
was sent. That means the behavior was unfair towards EGPRS TBFs, because
sometimes they were skipped in favor of GPRS ones.
This patch imporves the situation in the above mentioned USF scenario, by
first, under specific conditions, allowing selection of an EGPRS TBF and
then forcing it to transmit in EGPRS-GMSK (MCS1-4) so that the
USF-targeted MS can still decode the USF, while at the same time
providing more fairness by allowing the EGPRS TBF to transmit data.

The specific conditions mentioned above are, mainly, related to the fact
that once a DL data block has been sent, and hence a BSN was assigned to
it, it cannot be retransmitted later using another MCS, since lower
MCS1-4 wouldn't be able to contain higher MCS RLC payload.

The set of conditions could be expanded in the future by also selecting
the EGPRS TBF if retransmition is required and the block to be
retransmitted was originally transmitted as MCS1-4.

Related: OS#4544
Change-Id: I9af23e175435fe9ae7b0e4119ad52fcd4707b9ca
2020-12-01 11:56:01 +00:00
Pau Espin Pedrol
7fd9a29eba tbf: Log previous TS when changing Control TS
Change-Id: I5f37f3512dde60e4eb1ccebbb2d96de24604d241
2020-12-01 11:53:39 +00:00
Pau Espin Pedrol
58cd1d2f8a main: generate coredump and exit upon SIGABRT received
Previous code relied on abort() switching sigaction to SIG_FDL +
retriggering SIGABRT in case the signal handler returns, which would
then generate the coredump + terminate the process.
However, if a SIGABRT is received from somewhere else (kill -SIGABRT),
then the process would print the talloc report and continue running,
which is not desired.

Change-Id: I8f02925eedd8855bb58555df20b443f79d5c6da8
Fixes: OS#4865
2020-11-26 13:40:57 +01:00
Alexander Couzens
4e8f2c310f gprs_bssgp_pcu: follow ns2 library changes
Depends-on: I18dfd40a2429cd61b7c4a3dad5f226c64296f7d8 (libosmocore)
Change-Id: I056fe624160f2fe01d405690eba5cc0032780837
2020-11-25 20:53:37 +00:00
Pau Espin Pedrol
74aa3523f3 csn1: Log CSN_VARIABLE_ARRAY values as hex
Change-Id: If84c4b3cb870068a85405116f1d505ffcff9c26e
2020-11-24 11:24:11 +01:00
Pau Espin Pedrol
259a694ba7 csn1: Fix readIndex pointer change in CSN_VARIABLE_ARRAY
There's actually 3 errors:
* Its value should be updated, not the pointer itself
* Value should be increased, not decreased
* bitvec_read_field() API is already advancing it, no need to do it

Fixes: OS#4838
Change-Id: I009abc373794e148091e637ffee80c6461960945
2020-11-24 11:22:06 +01:00
Pau Espin Pedrol
9c4f9ffec1 pdch: Log hexdump of decde failure for dl rlcmac block
Change-Id: I591212a43bf92984348d1992df8c9b4fdddeba3b
2020-11-23 16:36:42 +01:00
Pau Espin Pedrol
7d3ee9ed8d pdch: packet_paging_request: Put back non-fitting paging entry where where it was
dequeue_paging() dequeues the first paging (at the start of the list).
If a paging request is dequeued but later it cannot be added to the
message being sent, it has to be re-added to the list for later
processing on next message. However, existing code was enqueueing it at
the end, which meant that paging request was delayed for no reason.

Change-Id: Iad8e7045267d56e32f42db0fbb8448b1b1185f05
2020-11-23 16:04:11 +01:00
Pau Espin Pedrol
228628860f Support multiplexing of GPRS and EGPRS TBFs in one PDCH
There are some restrictions to have both GPRS-only and EGPRS MS attached
to the same MS:
* Any MS needs to be able to successfully decode a DL block at least
  every 18 DL blocks (360 ms). That means a Dl block with CS1-4 must be
  sent at least once during that time.
* Any MS needs to be able to decode USF targeting it. GPRS-only MS can
  successfully decode USF from DL blocks using GMSK: CS1-4 and MCS1-4.

In this patch, if USF of a GPRS-only MS is selected, then all DL EGPRS
TBFs are discarded from data block selection. However, this logic can be
further improved later by still allowing selection of DL EGPRS TBFs and
then forcing construction of a DL EGPRS data block using MCS1-4.

Sources:
* 3GPP TS 03.64 version 8.12.0 "6.6.4.1.1.2 Multiplexing of GPRS and EGPRS MSs"
* 3GPP TS 05.08 version 8.23.0 "10.2.2 BTS output power"

Related: OS#4544
Change-Id: Ib4991c864eda6864533363443f76ae5d999532ae
2020-11-17 16:42:15 +01:00
Pau Espin Pedrol
daa6c1c645 sched: Use correct GMSTAP category for EGPRS DL data blocks
Change-Id: I3bd8b6a2328e13543b7d4c4a945e86f14ff35bda
2020-11-17 15:05:10 +01:00
Pau Espin Pedrol
4c5a7d315c sched: Fix sending GSMTAP DL data blocks with unset USF
Change-Id: Ib5ceb83a85b517ee9bf2c59cf27fe818373abe60
2020-11-17 15:05:10 +01:00
Pau Espin Pedrol
49efd9b606 encoding: Fix duplicate word in log str
Change-Id: Ifb0b359c43e79bab5599625fae20750ae5a6ae54
2020-11-17 09:52:29 +00:00
Pau Espin Pedrol
e309d80dd4 Fix ctr reports: Remove ctr description from already removed counter
Recent commit removed the counter enum but forgot to remove the
description, so the descriptions were all shifted by 1 counter.

Fixes: 133fe4a852
Change-Id: I82ee9f36d60a1fd129ae3a864508fcd886e4bfef
2020-11-13 16:19:25 +01:00
Harald Welte
2bbdf2e3d7 Use osmo_fd_*_{disable,enable}
Change-Id: I16563a1033ad12a32104bfee974045ac4302bc74
Depends: libosmocore.git Idb89ba7bc7c129a6304a76900d17f47daf54d17d
2020-11-11 20:15:37 +00:00
Vadim Yanitskiy
cb98894eb1 TLLI 0x00000000 is a valid TLLI, use 0xffffffff instead
The assumption that TLLI 0x00000000 is invalid and can be used
as the initializer is wrong.  Similar to TMSI, 0x00000000 is a
perfectly valid value, while 0xffffffff is reserved - use it.

According to 3GPP TS 23.003, section 2.4, a TMSI/P-TMSI with
all 32 bits equal to 1 is special and shall not be allocated by
the network.  The reason is that it must be stored on the SIM,
where 'ff'O represents the erased state.  According to section
2.6 of the same document, a local/foreign TLLI is derived from
P-TMSI, so the same rule applies to TLLI.

I manually checked and corrected all occurances of 'tlli' in the
code.  The test expectations have been adjusted with this command:

  $ find tests/ -name "*.err" | xargs sed -i "s/0x00000000/0xffffffff/g"

so there should be no behavior change.  The only exception is
the 'TypesTest', where TLLI 0xffffffff is being encoded and
expected in the hexdump, so I regenerated the test output.

Change-Id: Ie89fab75ecc1d8b5e238d3ff214ea7ac830b68b5
Related: OS#4844
2020-11-10 17:06:39 +00:00
Pau Espin Pedrol
305763dc6f tbf_ul: Log mismatching TLLI on log message
Change-Id: Ia2ac7062c1f3308a1485da6d769cb8a869fa8100
2020-11-06 20:03:24 +01:00
Pau Espin Pedrol
9897b26e3e gprs_ms: Avoid enabling EGPRS if no MCS are supported
This patch avoids enabling EGPRS on MS objects if BTS/VTY assigned no
MCS supported/available for use.
As a result, if NO MCS is enabled/supported EGPRS won't be used despite
the MS announcing through EGPRS MS class that it supports EGPRS.

Change-Id: Ib19e9e006d851c2147de15f4aec36ab65250bdd3
2020-11-05 15:48:11 +01:00
Pau Espin Pedrol
f1159c559b Fix mcs_is_valid(): UNKNOWN value is not a valid (M)CS
Also add a few more asserts to make sure a valid CS/MCS is passed in
some placed where we expect (M)CS to be set.

Change-Id: I0a973e10cd9477f72d8bd47a06048414b33ae96a
2020-11-05 15:48:04 +01:00
Pau Espin Pedrol
b47b137c66 Fix configuration of initial_(m)cs
Properly clip initial_(m)cs values to be lower-equal than maximum
configured.

Regarding initial_mcs, use values provided by BTS, which were not used
before.

Change-Id: Ifc6bc7c2734d1ae404adc2497afec5366e4f9e50
2020-11-04 21:39:43 +01:00
Pau Espin Pedrol
87eec1fd74 Get rid of bts->egprs_enabled
BTS simply notifies the PCU about the supported MCS, and PCU is
responsible for providing correct data formatting supported for the BTS
and the target MS.

Related: OS#4544
Change-Id: Ifcf23771bd23afc64ca6fea38948f98f2d134ecb
2020-11-04 21:39:43 +01:00
Pau Espin Pedrol
133fe4a852 tbf_ul: Allow non-egprs phones if EGPRS is enabled
We'll be able to still serve GPRS-only phones if EGPRS is enabled.

Related: OS#4544
Change-Id: I2e01b9d0de7506e0c0960342d73dba29187fe61f
2020-11-04 21:39:43 +01:00
Pau Espin Pedrol
569f0d27c7 tbf_dl: Don't fake EGPRS MS class when no related info is available
For instance if PCU received DL data to be sent to an MS from an SGSN,
and the MS is not currently cached in the PCU (because there's no TBF
active for it), it will page it and transmit the DL data to it.
The SGSN is capable of sending (EGPRS) MS Class information in that same
DL data message, so it's the one responsible for providing that
information if not available at the PCU.
In the PCU if we don't have information about that MS and SGSN didn't
provide us information about it, we cannot assume the MS is going to be
EGPRS capable and even less expecting a specific EGPRS MS class.
So let's drop this code.

Related: OS#4544
Change-Id: Icce66cadb51af25ae0c3b3719940eccb548fe33b
2020-11-04 21:39:42 +01:00
Pau Espin Pedrol
4808d1ceb8 pdch: Process received CS1-4 data blocks regardless of egprs_enabled
EGPRS "enabled" or "not enabled" is a bit of confusing idea, since there
are different levels of EGPRS support. For instance we may have been
instructed by config to not transmit using MCS5-9 (8PSK), or not use
MCS1-9 at all (GMSK+8PSK). However, we cannot control what is sent to us
or what we receive on lower layers, so if the BTS PHY/receiver was able
to decode+receive, let's try to process it anyway...

Related: OS#4544
Change-Id: Ie70ec8e4a2d688762d7d320d6ad58d5a0cc52ea1
2020-11-04 21:39:42 +01:00
Pau Espin Pedrol
270c9ea5d9 Enable egprs support through PCUIF from BTS/BSC
This VTY command was added due to EGPRS being introduced later as an
experimental feature. It's no longer needed and causes more problems
than goodness (since people sometimes forgets to enable it).

Let's rather simply enable EGPRS support based on what BTS/BSC requests
over PCUIF.

Related: OS#4544
Change-Id: Ic80970a136361584da9c912252a07e7c3c9d85d0
2020-11-04 21:39:42 +01:00
Pau Espin Pedrol
343ec9b9fd Take into account BTS supported (M)CS values when retrieving the maximum
Change-Id: I2d3a8bbae2f9887400ce56d2f8303ea30abaecfa
2020-11-04 21:39:42 +01:00
Pau Espin Pedrol
8072e354cc Move EGPRS MS mode set to gprs_ms.cpp
Some tests were wrong (TypesTest) and required modification, since they
were setting a EGPRS MS but then expecting a GPRS assignment.

Change-Id: I9d3ee21c765054a36bd22352e48bde5ffca9225a
2020-11-04 21:39:42 +01:00
Pau Espin Pedrol
d87722d03c pcuif: Improve BTS-supported CS/MCS handling
Take into account the MCS values supported by the BTS. In osmo-bts,
in general all MCS are enabled if "mode egprs" is selected in BSC,
and none otherwise.

Change-Id: Ie8f0215ba17da1e545e98bec9325c02f1e8efaea
2020-11-04 21:39:42 +01:00
Pau Espin Pedrol
46fd7a0316 Move BTS initial values inside bts.cpp
This way everytime any program or test initiates a BTS object, the
bts_data structure has the same values.

Change-Id: Iffd6eecb1f08bda0091f45e2ef7c9c63b42e10b3
2020-11-04 19:32:12 +00:00
Alexander Couzens
a2848546d2 NS2: follow the change of ownership
When receiving a primitive from the NS2 layer
the PCU must free the msg buffer if given.

Change-Id: I180433735bfbb3375c41318d7a7709d5845199ba
2020-11-03 22:37:49 +00:00
Vadim Yanitskiy
2c1fed20ab BSSGP: constify argument 'tp' of gprs_bssgp_pcu_rx_paging_{cs,ps}
Change-Id: I24e48964a0ff86c2ca962ab3928d07b1c9200390
2020-11-02 09:10:00 +00:00
Vadim Yanitskiy
e9bddabc01 BSSGP: use tlvp_val8() in gprs_bssgp_pcu_rx_paging_cs()
Change-Id: Ic1e37cb9938323c9b9f0466be5cf7251a6db1008
2020-11-02 09:10:00 +00:00
Pau Espin Pedrol
b6450840dc gprs_ms: Use proper function to get CS
Change-Id: I161e733991ac4fa7bd25a0f12b20e5701c76fc52
2020-10-30 19:44:43 +01:00
Pau Espin Pedrol
b1bbcae71c cosmetic: tests: pcu_emu: fix trailing whitespace
Change-Id: I889498c75a78fd8aa406cff5600e4773785782de
2020-10-30 17:10:55 +01:00
Pau Espin Pedrol
758ace867b tbf_dl: Update (egprs_)ms_class for already known MS
If SGSN provides us with MS class information upon DL data, let's use it
and set it in an already existing MS object if not yet known.

Also remove all unneeded code passing ms_class to append_data() which
would simply try to (again) set the ms_class.

Change-Id: I4979c9344bffd3ba7657bbab94981d233eab801f
2020-10-29 12:38:17 +01:00
Pau Espin Pedrol
16705a4db1 cosmetic: Fix ws between if keyword and parenthesis
Change-Id: I5932f21c58e76552f7187a175b8e281c5846536c
2020-10-29 11:34:17 +00:00
Pau Espin Pedrol
4d1663594b bts: define egprs_enabled as bool
Change-Id: I66a8254ee392ad75226c58b7df5746f409463f0f
2020-10-29 11:34:17 +00:00
Pau Espin Pedrol
983bb7eb31 alloc_algo_b: Select TRX with least assigned TFIs during TBF alloc
Before this patch, it would always allocate all TBFs on the first TRX
until all TFIs were filled, then second, and so on. But it would
actually fail around 8th MS requesting an UL TBF because despite a TFI
was successfuly assigned, because all USFs were already exhausted for
that PDCH.

Related: OS#1775
Change-Id: Iccfc8acfbfdc258ed16cc5af01f12b376fe73b72
2020-10-29 11:34:17 +00:00
Pau Espin Pedrol
528820dbe1 tbf: Clean up gprs_rlcmac_dl_tbf::handle()
Avoid passing tons of params to internal helper function
tbf_nel_dl_assignment() in order to either fetch again the ms object or
create a new one. Let's instead create the ms function earlier if needed
and fill it with all the discovered information prior to calling the
helper function. This provides cleaner code and also better log output.

This way we also avoid trying to fill the MS twice and unneeded
getter+setter for TA.

tbf::imsi(): There' always an ms, so simply forward call to
ms()->imsi().

We can also get rid of assign_imsi, since the modified code is the only
place where it's used and there's already some code in place there to
update the MS. We instead merge it with set_imsi and keep the
duplication code to catch possible bugs from callers.

Move merge_and_clear_ms from tbf class to GprsMS, where it really
belongs.

Change-Id: Id18098bac3cff26fc4a8d2f419e21641a1f4c83b
2020-10-29 11:34:17 +00:00
Alexander Couzens
87c6dd3c26 pcu_l1_if: fix misaligned assignment of remote address
Found-by: asan
Change-Id: I9c87d3fc1b6f03d79b53f1da3a146630061c3459
2020-10-27 15:29:05 +00:00
Pau Espin Pedrol
38de84cdc4 tests: ms: Pass correct pointer in constructor instead of NULL
The BTS field will be used in code paths after next patch changes,
otherwise the test fails accessing the NULL pointer.

Change-Id: I5098292bdafa9f4f70fef1a053b80a33deca722c
2020-10-26 13:46:33 +01:00
Pau Espin Pedrol
9f6d1a85e1 tbf: Drop unused function disable_egprs()
Change-Id: I8e3e1d9e70d46c7ca65ce67c408830f8bd20ce3b
2020-10-25 23:29:20 +01:00
Pau Espin Pedrol
b385969039 Move dl_tbf allocation code to correct file
Change-Id: I50d41b1c6f244edcfb78646d0fac4e47c2e3e561
2020-10-24 22:14:42 +02:00
Pau Espin Pedrol
442198cd41 Move ul_tbf allocation code to correct file
Change-Id: Ifd98abbcce49e4605c764267965903fbf9f35867
2020-10-24 22:14:42 +02:00
Pau Espin Pedrol
e9f77d377b tbf: Set MS during constructor time
This is another step forward towards a more clear data model where a TBF
always has a MS object (which may be lacking some information, and at a
later point when more information is found, it may actually be a
duplicated MS object and hence one duplicate removed and the TBF moved
to the object being kept).

This helps for instance in removing duplicated information stored in
the TBF which is really per MS, like ms_class, ta, etc. Since there's
always a MS object there's no need to keep a duplicate in both classes
in case there's no MS object.

It can already be seen looking at unit test logging that this kind of
data model already provides better information.
Some unit test parts were needed to adapt to the new model too.

Change-Id: I3cdf4d53e222777d5a2bf4c5aad3a7414105f14c
2020-10-24 22:14:42 +02:00
Pau Espin Pedrol
7bde60f260 tbf: Implement enable_egprs() once
There's no real need for having different copies of this method in each
children. Furthermore, having the method implemented in the base class
made me shoot my foot while trying to move this to the tbf constructor
(see next commit), so let's simplify this and avoid other people
following into the same issue.

enable_egprs() in tbf.h is moved to be public since it needed (as it was
for the duplicated children mehtods with same name), but anyway it will
be moved to private in next commit.

Change-Id: Id7de060318201a42e51f277f898463f4b9a84eba
2020-10-24 22:14:42 +02:00
Pau Espin Pedrol
b3f239785c tbf: Make window() available to tbf base class
Return an interface to the window base class so that the tbf base class
can access the common window methods, such as set_ws(). It will be used
in next commit to get rid of duplicated function enable_egprs in both
dl_tbf and ul_tbf subclasses.

The user of the function can then decide to access more specific
functionaltiites of the window class by static casting it to the
specific direction (which is known by the caller since it operates on a
ul_tbf or a dl_tbf).

Change-Id: Ia2e1decf91be1184668e28297c2126affb9c7ae4
2020-10-24 22:14:42 +02:00
Pau Espin Pedrol
834cbab97d Move constructor gprs_rlcmac_dl_tbf::BandWidth to correct file
Change-Id: I7587fd2ee97424020a099a8513f95544d6635f3d
2020-10-24 19:49:43 +00:00
Pau Espin Pedrol
9767e44fd4 Move gprs_rlcmac_ul_tbf::window to correct file
It wasn't move when all the UL specific code was moved to a separate
file.

Change-Id: I6f8c2d568ffdea3826ec1e11358d24eea6c9335b
2020-10-24 19:49:43 +00:00
Vadim Yanitskiy
1136fdb563 main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
Change-Id: If82208ecb931a6024f1a83c8648c5855b15dcc96
Depends: Ie2022a7f9e167e5ceacf15350c037dd43768ff40
Related: SYS#4910
2020-10-24 05:15:20 +07:00
Vadim Yanitskiy
35668a2e06 main: remove line breaks in print_help(), increase spacing
Change-Id: I07010afec3bb85d875926813772a6504f3bdb7b5
Related: SYS#4910
2020-10-24 05:14:39 +07:00
Pau Espin Pedrol
a611b4f6a0 Fix several calls to LOGPAL
In those cases since a string pointer was passed, it always printed
"single" instead of whatever really was being used, since the string
pointer was not NULL.

Change-Id: Idab7d18e8f519e10fc3df4007634661c46f9256d
2020-10-23 17:13:04 +02:00
Pau Espin Pedrol
8353d97904 Improve debug logging for alloc algos
In general we want to see explicitly the kind of requested allocation at
the start. The MS class is not needed since it's printed in the previous
log line in any case.

Change-Id: I9eb0a592c15be96da9d140ff373c1afead76b18c
2020-10-23 17:08:43 +02:00
Harald Welte
8347359cb0 Use osmo_fd_setup() whenever applicable
Change-Id: I8abd4c50b172f6b312bb4ba3c29e74396f6e6b93
2020-10-19 11:36:33 +00:00
Vadim Yanitskiy
a6c5db954f fix tbf_select_slot_set(): use LOGP() instead of LOGPC()
Change-Id: I8e9ae854d147735357921f71d9a02862376a50b2
2020-10-16 22:33:54 +07:00
Vadim Yanitskiy
1ab35d100b doc/manuals: (re-)generate XML VTY reference automatically
Change-Id: I0818be314e36d5fb848c9248be6b8cd7fe20fb39
Related: SYS#4937
2020-10-16 15:29:19 +00:00
Alexander Couzens
5bece2a0ed Rework NS configuration over the info indication
Add support of the second NSVC in the info indication.
Add support to update a previous NS configuration.
Allow to update of a NS-VC while the NSE is still available over the
second.

Depends-on: I917f25ebd1239eae5855d973ced15b93731e33a0 (libosmocore)
Depends-on: I3a0cd305fd73b3cb9ec70246ec15ac70b83e57f2 (libosmocore)
Depends-on: I5a2bb95d05d06d909347e2fb084a446ead888cb3 (libosmocore)
Depends-on: I54f110acc3acccb362f6e554324d08cc42b7c328 (libosmocore)
Depends-on: Ia00753a64b7622a0864341f51ea49b6963543755 (libosmocore)
Depends-on: Ic8f6f8aca10da23a18fab8870be7806065a34b47 (libosmocore)
Depends-on: I5f67e6a9bf4cb322bd169061fee0a528012ed54d (libosmocore)
Change-Id: I589ebaa2a2b7de55b7e4e975d8fd6412dd5f214b
2020-10-13 08:45:30 +00:00
Pau Espin Pedrol
3839605ec9 contrib/jenkins: Enable parallel make in make distcheck
Change-Id: Ie3f5b6063167875ecb2f25e194806a2476aec984
Related: OS#4421
2020-10-13 08:21:00 +00:00
Vadim Yanitskiy
974cc7b324 gprs_bssgp_pcu: fix: do not crash on receipt of subsequent INFO.ind
It's expected to receive subsequent INFO.ind messages at run-time,
e.g. when a dynamic TCH/F_TCH/H_PDCH timeslot is switched from
PDCH to TCH/F or TCH/H, osmo-bts would send us INFO.ind with the
updated PDCH slot-mask indicating that this timeslot is disabled.

In gprs_nsvc_create_and_connect(), do not bind() on the received
NSVC address unconditionally - we may already be bound to it.
Instead, return early and keep everything unchanged.

I don't know how the PCU is supposed to handle NSVC address change,
at least the new NS2 library does not handle this internally, nor
it provides any API for that.  Let's leave it for later.

Change-Id: I159138e41e147cd30212da548b0ccd3f81d61b4e
Related: I4c3bc883d795e5d1ee5ab175ac03684924692a7c
Fixes: Ib389925cf5c9f18951af6242c31ea70476218e9a
Related: SYS#5108
2020-10-10 06:16:11 +07:00
Vadim Yanitskiy
d038361e95 struct gprs_rlcmac_bts: remove unused 'nsei' field
The code in gprs_nsvc_create_and_connect() stores NSEI there for
no reason, despite it's already stored in struct gprs_ns2_nse.

Change-Id: Ib30152a12384cf0448104a1ee1cfb949f4a27553
2020-10-10 05:05:33 +07:00
Vadim Yanitskiy
0d0ac1b949 gprs_bssgp_pcu: fix possible memleak in gprs_nsvc_create_and_connect()
Change-Id: I9f44cd22b1353ded6eeb2fe9f8dd4818de758003
2020-10-10 05:05:33 +07:00
Vadim Yanitskiy
194b6f06a0 gprs_bssgp_pcu: make osmo_sockaddr local/sgsn arguments const
Change-Id: I40e51de300533cfcbb3c9f90a0858a489a3c2b6b
Depends: I0df6a00ac1830bd64a10b9336b827e113fa772bb
2020-10-09 21:43:09 +00:00
Philipp Maier
08c5037d60 pcu_main: add commandline option --vty-ref-xml
The commandline option --vty-ref-xml is needed to enable automatic
generation of the VTY reference manual.

Change-Id: Ie1829a06b83f69f4cd8256adbf9437388ca3d7e0
Related: SYS#4937, OS#1601
2020-10-09 20:42:16 +02:00
Vadim Yanitskiy
781f04a8f8 pcu_l1_if: print NSVC address in more common format
Change-Id: I5aac1b0624d127ef92a7b9c791f6b409e9e6055a
2020-10-09 20:54:03 +07:00
Vadim Yanitskiy
24c643e143 pcu_l1_if: use proper format string specifiers: %d -> %u
Change-Id: Ieb81dcb4096acbc7a308d003c9e653800071e149
2020-10-09 20:34:21 +07:00
Vadim Yanitskiy
19622aee5b pcu_l1_if: cosmetic: make {local,remote}_sockaddr scoped variables
Change-Id: Ib5ddf3622226ac9c612a03ca32d373bcc9b208cf
2020-10-09 20:03:16 +07:00
Vadim Yanitskiy
c1a726c573 pcu_l1_if: correct logging level in pcu_rx_info_ind()
Change-Id: Ib4bb9b907fc2bf279cf22a3bb9b95ea1a0fb3db6
2020-10-08 23:03:39 +07:00
Vadim Yanitskiy
5d5b50aaf3 pcu_l1_if: cosmetic: use ARRAY_SIZE() in pcu_rx_info_ind()
Change-Id: Iff11c854b1687f75c1125a5bcd616da95ade69ee
2020-10-08 23:03:36 +07:00
Philipp Maier
9459ebde32 vty: add attributes to VTY commands indicating when they apply
Change-Id: I63978ce3ea87593c9a41e503ed3b761c64e1e80f
Related: SYS#4937, OS#1601
2020-10-08 07:16:31 +00:00
Alexander Couzens
290d9030e9 Use the new NS2 lib
Depends: Id7edb8feb96436ba170383fc62d43ceb16955d53 (libosmocore)
Depends: I2a9dcd14f4ad16211c0f6d98812ad4a13e910c2a (libosmocore)
Change-Id: Ib389925cf5c9f18951af6242c31ea70476218e9a
2020-10-06 16:38:38 +02:00
Harald Welte
0a369e560c bts.cpp: Increase constructor priority
It seems that some gcc versions do not consider the priority of
"C" __attribute__((constructor)) definitions in the same order as
they do C++ static initializers, which are called in the order in which
they appear in the compile unit (source file).

The problem has been observed at least in a
environment based on T2 SDE with GCC 6.3.0 and binutils 2.28.

Let's work around this by making sure the __attribute__((constructor))
function always gets the highest priority value permitted by gcc (101).

Closes: SYS#5093
Change-Id: I65de69a32ac929e6ddd4e58980027f9e76813153
2020-10-01 19:39:58 +02:00
Pau Espin Pedrol
84abd2f65e Fix crash accessing NULL tbf->pdch[first_ts]
Fixes consistent crash under some specific scenarios explained in
OS#4756.

The crash was caused due to a bug in channel allocator algorithm
incorrectly populating tbf->pdch[] array as a result of mismatching
first_ts and resulting pdch selected slot bitmask.

The issue happens because when allocating a UL TBF in allocator B, the
subset is always further forced into allocating one single TS. As a
result, on that branch several variables are updated, but first_ts was
not.

The field used to be updated in older versions, but a bug was introduced
during code refactoring in commit listed below (31 Jan 2018).

Fixes: 0cc7212cfd
Related: OS#4756
Change-Id: I79596803f7dab6f21b58bfe39c2af65d9c5b39d5
2020-09-22 20:57:06 +02:00
Pau Espin Pedrol
01aef5ed8b cosmetic: Fix typo in comment
Change-Id: I6567065b8ec0082b21c371bbd0c2e85fecc96c90
2020-09-22 20:57:05 +02:00
Pau Espin Pedrol
5d2d2ec466 cosmetic: Fix indentation of for loops
Change-Id: Id7087bd00d9003235688ff34f2b039d525caa777
2020-09-22 20:57:05 +02:00
Pau Espin Pedrol
a1ac2f017f vty: Add 'show bts pdch' command
This allows to see which channels have been enabled according to PCU.

Change-Id: If72e67ba80aab4e0c68408e6996d74d2ff70c322
2020-09-22 20:56:53 +02:00
Pau Espin Pedrol
5fc95771bb cosmetic: fix indentation alignment
Change-Id: I105cf184a478cf178d69e16cd35fa36c51133a18
2020-09-22 20:46:20 +02:00
Pau Espin Pedrol
bd9973a64f Free all MS TBFs when receiving GPRS Suspension Request
Otherwise the TBFs are kept, and hence PCU will continue reserving resources and
DL data queued will still be sent over the air, despite the MS not
listening anymore on the PDCH.

Change-Id: I4ae1c3706b2ed6e4d271cd16f7cd7f8937b84836
2020-09-22 20:46:15 +02:00
Pau Espin Pedrol
e3ef51ec06 gprs_ms_storage.h: Set pointer to NULL instead of 0
Change-Id: If8acd012909c9f452b609e4a804c9a059379f07d
2020-09-22 13:56:57 +02:00
Pau Espin Pedrol
cd6c466922 tbf: Don't log rlcmac_diag() output in separate lines
Output of all diag in different lines is really confusing, since the
user reads a timeout ocurred and then later in another line something
like "Downlink ACK was received" while no GSMTAP message shows any ACK.

Change-Id: I6a7d79c16c930f0712bc73b308409ececb1946ba
2020-09-22 13:44:15 +02:00
Pau Espin Pedrol
6ce1299567 gitignore: Add __pychache__ dir
That's created by VTY tests running python.

Change-Id: Idb67909ddd5027dbdcbf5a3882d1bb0d4ff9d29e
2020-09-22 13:44:15 +02:00
Alexander Couzens
9b5c960f55 pcuif_proto: version 10: add support for IPv6 NSVCs
Introduce a address_type in the NSVC configuration pass the given
protocol.  The remote_ip is network byte order, the default
encoding for in_addr and in6_addr.

Change-Id: Ia0852f9f4395f1248b39363ef90f6f5673b24e2f
Related: SYS#4915
2020-09-22 00:49:28 +07:00
Vadim Yanitskiy
8e2bd1e79d pcuif_proto: version 10: add frequency hopping parameters
Change-Id: I6863830883d90954006a7126c22d348aa2a83271
Related: SYS#4868, OS#4547
2020-09-22 00:49:11 +07:00
Alexander Couzens
40a9e603b9 Revert "pcuif_proto: version 0xa: add support for IPv6 NSVCs"
This reverts commit 38aaa10ed4.
It was to early because the frequency hopping wasn't ready to be merged.

Change-Id: Ibf055d5adfd9bffaaf51cb8468c79be597467e0f
2020-09-16 03:24:55 +02:00
Alexander Couzens
38aaa10ed4 pcuif_proto: version 0xa: add support for IPv6 NSVCs
Introduce a address_type in the NSVC configuration pass the given
protocol.
The remote_ip is network byte order, the default encoding for in_addr and in6_addr.

Change-Id: If26958d5b584973dca79159cf9e7f3f266519ce9
2020-09-15 22:21:43 +02:00
Vadim Yanitskiy
2597cf1494 encoding: fix gen_freq_params(): do not check pdch twice
This is a left-over from an earlier version of [1] that makes
Coverity think that there can be NULL pointer dereference,
even despite we assert(pdch != NULL).

[1] I8adc0cdb1b05a87b4df5d4bc196f6d381283a06f

Change-Id: I3490c38e0c1186dfd2fae63526a05c694547cebb
Fixes: CID#214230
2020-09-10 18:23:03 +07:00
Vadim Yanitskiy
542e388e4f encoding: implement handing of hopping parameters
The following test cases verify coding of the hopping parameters:

  + (RR) Immediate Assignment
    - TC_pcuif_fh_imm_ass_ul_egprs,
    - TC_pcuif_fh_imm_ass_ul,
    - TC_pcuif_fh_imm_ass_dl,

  + (RLC/MAC) Packet Uplink/Downlink Assignment:
    - TC_pcuif_fh_pkt_ass_ul,
    - TC_pcuif_fh_pkt_ass_dl,

all of them pass with this (and the upcoming) change applied.

Change-Id: I8adc0cdb1b05a87b4df5d4bc196f6d381283a06f
Related: SYS#4868, OS#4547
2020-09-08 02:47:02 +07:00
Vadim Yanitskiy
db56a3563e encoding: use CSN.1 codec to generate Packet Uplink Assignment
It's quite odd to see that in write_packet_downlink_assignment()
we initialize an 'RlcMacDownlink_t', so then the caller can use
the power of CSN.1 codec to generate the final sequence of bytes
to be transmitted, while in write_packet_uplink_assignment() we
already compose the final RLC/MAC message straight away using
the low-level bitvec API (like bitvec_write_field()).

I guess the reason is that at the time of writing this code, the
CSN.1 codec was not stable enough, so it was safer to generate
the message 'by hand'.  This would also explain why we *decode*
the final RLC/MAC message in create_ul_ass() right after encoding.

Rewrite write_packet_uplink_assignment(), so now it initializes
a caller-provided 'RlcMacDownlink_t' structure.  Given that it's
allocated on heap using talloc_zero(), do not initialize presence
indicators of fields that are not present in the message.

This would facilitate handling of frequency hopping parameters
in the upcoming changes, in particular we can now introduce a
function that would compose Frequency Parameters IE for both
write_packet_{downlink,uplink}_assignment().

Tested manually by running a GPRS-enabled network, as well as by
running test cases from ttcn3-pcu-test => no regressions observed.

Change-Id: I2850b91e0043cdca8ae7498a5fc727eeedd029b6
Related: SYS#4868, OS#4547
2020-09-08 02:47:02 +07:00
Vadim Yanitskiy
962d717f41 encoding: clarify docstring for write_packet_downlink_assignment()
Change-Id: I7c4458c2e7767b6cf03462ba79acdd9c9904ee83
2020-09-08 02:47:02 +07:00
Pau Espin Pedrol
1d68cdff92 Fix recent typo preventing MS from registering
Fixes: 0052051c07
Change-Id: Icbbf7340d78ef709ea00d527036533a14e9c21f9
2020-08-26 13:43:39 +02:00
Vadim Yanitskiy
1156776572 encoding: pass pdch slot directly to encoding functions
In order to be able to encode frequency hopping parameters, let's
pass a const pointer to 'gprs_rlcmac_pdch' (PDCH slot) directly,
instead of passing all related parameters separately.

Change-Id: I6bccad508f0fdccc4a763211008dd847a9111a8d
Related: SYS#4868, OS#4547
2020-08-24 10:53:08 +00:00
Vadim Yanitskiy
a76cdaceb2 encoding: use bool for use_egprs in write_packet_uplink_assignment()
Change-Id: Iab4fb44c666a0d4fe8c98f5ff9221e23a6f1f2fa
2020-08-24 10:53:08 +00:00
Vadim Yanitskiy
59d64fb234 encoding: fix RRBP field in write_packet_uplink_assignment()
Do not hard-code 0x00, write what was passed as a parameter.

Change-Id: I9eb362292e9f4c16d4b8f8d4253df0422062eeb4
2020-08-24 10:53:08 +00:00
Vadim Yanitskiy
7a1fdfde8a encoding: do not encode out of range Timing Advance values
According to 3GPP TS 44.060, section 12.12 "Packet Timing Advance",
the 'TIMING_ADVANCE_VALUE' field is optional, and takes 6 bits
if present.  This means that a value that fits in range 0..63
(inclusive) can be encoded (0b111111 == 63).

It's possible that tbf->ta() returns GSM48_TA_INVALID == 220,
so the bitvec API would encode only 6 LSBs of it:

  220 & 0b111111 == 28

Let's ensure that the 'TIMING_ADVANCE_VALUE' is present iff
tbf->ta() returns a correct (0 <= x <= 63), and absent otherwise.

Change-Id: I342288ea4ef1e218e5744e9be6a8e528d4e697fa
2020-08-24 10:53:08 +00:00
Vadim Yanitskiy
c0ddaa94e6 encoding: constify 'tbf' in UL/DL assignment functions
Change-Id: I9b80ce22914a355592502c936046b233c3ba216d
2020-08-24 10:53:08 +00:00
Vadim Yanitskiy
e4bcd2103c tbf: allocate the bitvec on stack in create_{dl,ul}_ass()
Initialize the bit vector to use already allocated memory,
so we would not need to allocate additional 23 bytes and
copy them from the bit vector to a msgb.

Change-Id: I4190707d7fa5b1c4c3db745635f88d5afb9e21ca
2020-08-24 10:53:08 +00:00
Vadim Yanitskiy
c82c1f5efc tbf: cosmetic: use GSM_MACBLOCK_LEN where possible
Change-Id: Ib42770cb009e8d559f733ebedd058e2f0a18820a
2020-08-24 10:53:08 +00:00
Vadim Yanitskiy
48587d5475 gsm_rlcmac: use consistent naming for [Extended] Packet Timing Advance
Change-Id: I6382c81f7569b4c5a68521c04f6b03a34bfc39dd
2020-08-24 10:53:08 +00:00
Vadim Yanitskiy
1d52c1e02e pcu_l1_if: cosmetic: correct error message in pcu_rx_info_ind()
Change-Id: I26ad0e990f6bf049a14f63b1255722d60c7ff868
2020-08-24 10:53:08 +00:00
Vadim Yanitskiy
0f41b710b7 pcu_l1_if: cosmetic: move struct 'gprs_rlcmac_pdch' into the for loop
Change-Id: I5bc270ddb6064e5086a801061c2eff074c293e77
2020-08-24 10:53:08 +00:00
Vadim Yanitskiy
11b0f00c00 pcu_l1_if: cosmetic: rename both 'trx'/'ts' to 'trx_nr'/'ts_nr'
Change-Id: Id481eba9bd462e411b2ba047ee5b849ddba8ac6b
2020-08-24 10:53:08 +00:00
Vadim Yanitskiy
ce0dae9fda pcu_l1_if: constify the argument of pcu_rx_info_ind()
Change-Id: I0b146c9f8c1e566c3aff4bd7869ca9699f888d4f
2020-08-24 10:53:08 +00:00
Vadim Yanitskiy
fad2128d17 pcu_l1_if: use proper format specifier for PCUIF version
Change-Id: Ibd15a678a7a8fc840422e2280b0d358138a67e0c
2020-08-24 10:53:08 +00:00
Pau Espin Pedrol
b69928cfaf pdch: rcv pkt meas rep: Allocate MS object early in path and use it
Let's create the MS object early if doesn't exist and fill in the
information, so that we can operate on it in an early way (for instance,
logging macros), this way it's easier to trace the lifecycle of
subscribers.

Change-Id: I3ec7eb970310698dd228ae6ad65ec5ca833bab3f
2020-08-24 07:50:49 +00:00
Neels Hofmeyr
59fc0bda6e paging: pass struct osmo_mobile_identity, not encoded IE bytes
In get_paging_mi(), before this, an encoded buffer of Mobile Identity bytes is
returned. Code paths following this repeatedly decode the Mobile Identity
bytes, e.g. for logging. Also, in get_paging_mi(), since the TMSI is read in
from a different encoding than a typical Mobile Identity IE, the TMSI was
manually encoded into a typical Mobile Identity IE. This is essentially a code
dup of osmo_mobile_identity_encode(). Stop this madness.

Instead, in get_paging_mi(), return a decoded struct osmo_mobile_identity. Code
paths after this use the struct osmo_mobile_identity directly without repeated
decoding.

At the point of finally needing an encoded Mobile Identity IE (in
Encoding::write_paging_request()), do a proper osmo_mobile_identity_encode().

Since this may return errors, add an rc check for the caller of
write_paging_request(), gprs_rlcmac_paging_request().

A side effect is stricter validation of the Mobile Identity passing through the
Paging code path. Before, invalid MI might have passed through unnoticed.

Change-Id: Iad845acb0096b75dc453105c9c16b2252879b4ca
2020-08-24 01:12:16 +00:00
Neels Hofmeyr
0052051c07 use new osmo_mobile_identity api (avoid deprecation)
Note: subsequent patch Iad845acb0096b75dc453105c9c16b2252879b4ca will change to
passing a struct osmo_mobile_identity in the Paging code path, instead of
passing the encoded IE data.

Change-Id: Ibb03b8e601160427944f434761ca59811d1fc12f
2020-08-21 16:40:14 +02:00
Pau Espin Pedrol
b75c27febd Support setting rt-prio and cpu-affinity mask through VTY
Change-Id: I92bfabd57fab28b23bd4494a577373106be1daec
Depends: libosmocore.git Change-Id If76a4bd2cc7b3c7adf5d84790a944d78be70e10a
Depends: osmo-gsm-masnuals.git Change-Id Icd75769ef630c3fa985fc5e2154d5521689cdd3c
Related: SYS#4986
2020-08-20 08:44:54 +00:00
Pau Espin Pedrol
183b01eb4c doc: Update VTY reference xml file
Change-Id: I5800f1607878ab764323dc12c537a8e28d387cc9
2020-08-20 08:44:54 +00:00
Pau Espin Pedrol
fad557ec0f configure.ac: Fix trailing whitespace
Change-Id: I54b360c537e04ab3a9cb30ac3e3f9730bcad1c91
2020-08-20 08:44:54 +00:00
Pau Espin Pedrol
f2dad593ae Introduce log macro helper LOGPMS
Change-Id: Ib304ced06531a5154b7ec8bf87f9717dfd7d1397
2020-08-18 20:26:25 +02:00
Pau Espin Pedrol
5f10fbb166 pdch: Drop unneeded notice log message in rcv pkt meas report
It's totally fine to receive Packet Measurement Report messages with no
SBA present, since the MS also sends measurements on PACCH while
transmitting data.

Related: OS#4719
Change-Id: I8f642d9cdeb342df7d5f2fa30516ea69554a6270
2020-08-18 19:39:26 +02:00
Pau Espin Pedrol
a2c574ede1 Fix typo in log message
Change-Id: Ifd65542eca9180a2fcaaca290861396569e453ec
2020-08-17 18:15:46 +02:00
Vadim Yanitskiy
74b750df81 debian/control: change maintainer to the Osmocom team / mailing list
Change-Id: Ic114f2552ceadfeaaa00f8e719a5c7bd5d6b4917
2020-08-13 16:09:03 +07:00
Vadim Yanitskiy
811c90c5fd direct-phy: fix handle_ph_ra_ind(): handle PH-RA.ind on PRACH SAPI
In [1] I restricted L1 SAPI of PH-RA.ind to PDTCH and PTCCH, and
this seems to have caused a regression reported in [2]:

  DL1IF ERROR sysmo_l1_if.c:251 Rx PH-RA.ind for unknown L1 SAPI PRACH

I assumed that PH-RA.ind belonging to a Control Acknowledgement
message (in format of 4 Access Bursts) would have PDTCH SAPI,
while apparently it's actually arriving on PRACH.

[1] I482d60a46b9d253dfe0b16140eac9fea6420b30c
[2] https://osmocom.org/issues/1526#note-39

Change-Id: Ib0a6da37de7a1db4cad2b96293b31b9f32e7d9eb
Related: OS#1526
2020-08-12 18:20:33 +00:00
Vadim Yanitskiy
9d5a115ee2 encoding: assert() presence of Downlink TBF
This is not something that should normally happen.  If it happens,
then it's definitely a bug, and we should not tolerate it.

Change-Id: I6e46ba42650f0db2399649b536a1d2b3f0fcbf04
2020-07-18 20:54:24 +07:00
Vadim Yanitskiy
8f628ab77f encoding: drop log_alert_exit(), use OSMO_ASSERT() instead
Change-Id: Id5ef1c3c08dc7f264ad801e519d727d86f5ae5b8
2020-07-18 20:47:47 +07:00
Vadim Yanitskiy
eb1e0fa859 bts: cosmetic: use DUMMY_VEC for padding where possible
Change-Id: I725a7bd1b0c4d2b0d73f1b6d1f16543bf3d9d9fe
2020-07-18 20:33:49 +07:00
Pau Espin Pedrol
5935707489 pdch.cpp: Store TLLI promptly on newly created TLLI in rcv_resource_request
The TLLI is tried to be updated later anyway during tbf_alloc_ul(), but
this way it's clear that information is stored where it belongs as soon
as possible. The change already shows clearer log lines in TbfTest.err.

Change-Id: I20ce4eb94ecf85ce2835275d0056d9ecd1b558c3
2020-07-09 13:40:55 +02:00
Pau Espin Pedrol
9e7361a8ba pdch.cpp: Fix wrong annoying log line about non-scheduled ResourceReq received
It's perfectly fine receiving a Resource Request message under some
circumstances (as stated in the comment added in the commit).

To print issues only under non-expected circumstances, the function
rcv_resource_request need to be refactored:
* Destroying older UL_TBF is delayed because it is needed further
  down.
* When the old UL_TBF is FINISHED, it's an acceptable time to receive a
  Resource request, so we check if that's the case and don't print a
  warning in that case.

Change-Id: I4b4367126d6a16055cd2f45afc4a6b9c15a7c980
2020-07-09 13:37:15 +02:00
Pau Espin Pedrol
bcb226d607 pdch.cpp: Avoid resetting (egprs_)ms_class to unknown if not found in MS RadioAccCap
If the information is not found in the message, 0 (unknown MS class)
will be returned. If the MS already had some previous information on the
MS class, let's not lose it by setting it back to 0.

Take the opportunity to drop related log lines which are no needed,
since set_(egprs_)ms_class() functions already log the value changes.

Change-Id: Icd52209fd4395d78dc770e7869d1b1fe45a18ca0
2020-07-09 13:37:15 +02:00
Pau Espin Pedrol
85faa762c3 pdch.cpp: Avoid dropping existing DL TBF during rcv_resource_request
There's no real good explanation on why the DL TBF is dropped there,
since PKT RESOUCE REQUEST is used basically during UL TBF establishment.
Also, as decribed by TS 44.060 11.2.16 "Packet Resource Request":
"""
This message is sent on the PACCH by the mobile station to the network
to request a change in the uplink resources assigned.
"""

Change-Id: Iab4afb66f0d671f7ad54909d2685a1613e12ab4d
2020-07-09 13:37:15 +02:00
Pau Espin Pedrol
f2c6c83816 encoding.cpp: Fix missing spacing in function param
Change-Id: I4f30a0cea615d57cd7783a92ae782790c8075a6c
2020-07-09 13:37:15 +02:00
Pau Espin Pedrol
732373d7b4 encoding: Encode TA as unsigned and check validty against GSM48_TA_INVALID
According to 3GPP TS 44.018 sec 10.5.2.40, Timing Advance value is 8 bit
and range is 0-63 (0-219 on GSM400). Unsigned value (uint8_t) is used
everywhere else, so avoid using a signed one here, and simply check for
GSM48_TA_INVALID here, which we use everywhere else to initialize when the
value is not known. Ideally we should check for value based on band, but
it makes more sense to check that when receiving the data and storing in
in set_ta().

Change-Id: I82b13561d0fe5ebafb5c3a8b9a501045c29809bc
2020-07-09 13:36:47 +02:00
Pau Espin Pedrol
26743ac4f9 tbf_dl: uint8_t is enough to store a TA value
According to 3GPP TS 44.018 sec 10.5.2.40, Timing Advance value is 8 bit
and range is 0-63 (0-219 on GSM400). So there's no need for 16 bits to
store it. uint8_t is used in all other places in the code.

Change-Id: I38aa063ae30ca5680fef6252d2cef22cea98c123
2020-07-07 17:18:14 +02:00
Pau Espin Pedrol
f861d312fe decoding.cpp: Improve logging in malformed UL data parsing
Change-Id: Ic4ad14b88fddde8d9e62e0a2587b501d36821f01
2020-06-30 21:33:49 +02:00
Pau Espin Pedrol
72e395656d Set correct GSMTAP channel type for PDTCH messages returning error
For instance, that may happen because the len of the message is not
filling the expect size (because padding is missing for example). Still,
in this case we know the channel type, so we set it so that wireshark
tries to decode the message as a data one.

Change-Id: Ifea94095d669b528874e64ca823a776cd6e22b4b
2020-06-30 18:34:58 +02:00
Pau Espin Pedrol
259532fcf9 pdch.cpp: Fix logging line format in rcv_block wrong length
Change-Id: I2f818021cef41ab6f4569cd87026072811853352
2020-06-30 17:48:45 +02:00
Pau Espin Pedrol
d21e961a8b tbf: Drop unneeded method set_tlli_from_ul
Since commit 322456ed47 (and previous
one), it is expected that a tbf object ALWAYS has a MS object referend
to it, even if it's a temporary copy which will later be merged when
TLLI/IMSI is retrieved and it is found that several MS objects relate to
the same MS.

The purpose of set_tlli_from_ul was mainly to update TBF's ms() to
old_ms before going through usual tbf->update_ms() path. That's not
really needed since ms() is already always set and TBFs for old_ms are
already freed in update_ms() and children function.

Change-Id: Ie8795e7a02032336e53febb65c11f9150c36d2a0
2020-06-26 14:35:01 +02:00
Pau Espin Pedrol
cbf05f5146 gprs_ms: Transfer known EGPRS MS class when mergling old MS
Since not all the the information about the MS is known during TBF
creation in all scenrios, it may happen that when TBF is created it
creates a MS which later will end up being found a duplicate of an
already previously existing MS.
At that point, the old object is dropped and information retrieved from
both is merged into the new one.

The GPRS MS class was being transferred, but the EGPRS MS class was missing.

Change-Id: Ieb9929b60254b12f79392d6acb8b456d71cccb9e
2020-06-26 14:16:50 +02:00
Pau Espin Pedrol
43f0bce253 gprs_ms: Small clean ups in IMSI storage related code
Change-Id: I987af0d33b79302c037d062c9d1c828a0e027147
2020-06-26 13:09:44 +02:00
Vadim Yanitskiy
81b610ea2c bts: fix send_gsmtap_rach(): properly pack 11 bit RA
According to 3GPP TS 44.004, section 7.4a, two alternative RACH
block formats are specified: 8 bit (1 octet) and 11 bit. The
bit order is LSB (right to left), byte order is MSB.

In PCUIF RACH.ind structure (see gsm_pcu_if_rach_ind) we use
a field of type uint16_t to store RA values regardles of the
block format. Thus when packing it to bytes, we cannot just
cast uint16_t* to uint8_t*, we need to do some bit shifting.

Change-Id: I08a0a908f855b0d8a002df732e02781126d27dfb
2020-06-11 01:29:40 +07:00
Vadim Yanitskiy
07b6487c5a bts: add send_gsmtap_rach(), also send PTCCH/U over GSMTAP
Change-Id: I5cc4c3d2522215a31924121f83fcc2ac9ac6fe9c
2020-06-11 01:29:12 +07:00
Vadim Yanitskiy
b90d34b3d1 BTS::parse_rach_ind(): properly handle EGPRS Packet Channel Request
Let's finally use the API we introduced in [1].

[1] I96df3352856933c9140177b2801a2c71f4134183

Change-Id: Ia15761c33c8048d35c7f7bc93dbea781dd0894b7
Related: OS#1548
2020-06-03 12:49:40 +00:00
Vadim Yanitskiy
a0a0b7fb0e bts: refactor handling and parsing of RACH.ind
This patch is a set of tightly related changes:

  - group all RACH.ind parameters into struct 'rach_ind_params';
  - group Channel Request parameters into struct 'chan_req_params';
  - get rid of egprs_mslot_class_from_ra(), priority_from_ra(),
    and is_single_block(), introduce unified parse_rach_ind();
  - improve logging, get rid of redundant information.

This is needed for proper EGPRS Packet Channel Request handling.

Change-Id: I5fe7e0f51bf5c9eac073935cc4f4edd667c67c6e
Related: OS#1548
2020-06-03 12:49:40 +00:00
Vadim Yanitskiy
088dcbc016 doc/manuals: fix typo in overview.adoc: s/Omsocom/Osmocom/g
Change-Id: I7730e65728a3917dc4923728738253155e1e0428
2020-05-31 03:54:54 +07:00
Vadim Yanitskiy
4c593c8c8a encoding: fix write_ia_rest_egprs_uplink_sba(): add missing CHECK(rc)
Change-Id: I8e41c2912aaff689b0e311c8e2d3e961d2f1ac2c
2020-05-25 09:15:09 +00:00
Vadim Yanitskiy
a2d972a38a RLC/MAC: implement decoding of EGPRS Packet Channel Request
According to 3GPP TS 44.004, section 7.4a, two alternative RACH block
formats are specified: 8 bit (1 octet) and 11 bit. This change adds
CSN.1 definitions for 11 bit EGPRS Packet Channel Request as per
3GPP TS 44.060, table 11.2.5a.2.

Change-Id: I96df3352856933c9140177b2801a2c71f4134183
Related: OS#1548
2020-05-23 19:38:35 +07:00
Vadim Yanitskiy
3f2022e2cb encoding: cosmetic: use RLC_MODE_ACKNOWLEDGED where possible
Change-Id: Ic69d120f622f512f05016596bfdd4a89b96e3e3b
2020-05-23 19:28:03 +07:00
Vadim Yanitskiy
93ad3fd9b9 csn1: fix: never use enumerated types in codec structures
I faced a problem while working on EGPRS Packet Channel Request
coding support: the unit test I wrote for it was passing when
compiled with AddressSanitizer, but failing when compiled
without it o_O. Somehow this was observed only with GCC 10.

Here is a part the standard output diff for that unit test:

   *** testEGPRSPktChReq ***
   decode_egprs_pkt_ch_req(0x2b5) returns 0
  - ==> One Phase Access
  + ==> unknown 0xdd5f4e00
   decode_egprs_pkt_ch_req(0x14a) returns 0
  - ==> One Phase Access
  + ==> unknown 0xdd5f4e00
   decode_egprs_pkt_ch_req(0x428) returns 0
  - ==> Short Access
  + ==> unknown 0xdd5f4e01

At the same time, debug output of the CSN.1 decoder looked fine.
So WYSINWYG (What You See Is *NOT* What You Get)! As it turned
out, this was happening because I used an enumerated type to
represent the sub-type of EGPRS Packet Channel Request.

  typedef struct
  {
    EGPRS_PacketChannelRequestType_t      Type; // <-- enum
    EGPRS_PacketChannelRequestContent_t	  Content;
  } EGPRS_PacketChannelRequest_t;

The problem is that length of an enumerated field, more precisely
the amount of bytes it takes in the memory, is compiler/machine
dependent. While the CSN.1 decoder assumes that the field holding
sequential number of the chosen element is one octet long, so its
address is getting casted to (guint8 *) and the value is written
to the first MSB.

  // csnStreamDecoder(), case CSN_CHOICE:
  pui8  = pui8DATA(data, pDescr->offset);
  *pui8 = i; // [ --> xx .. .. .. ]

Let's make sure that none of the existing RLC/MAC definitions is
using enumerated types, and add a warning comment to CSN_CHOICE.

Affected CSN.1 definitions (unit test output adjusted):

  - Additional_access_technologies_struct_t,
  - Channel_Request_Description_t.

Change-Id: I917a40647480c6f6f3b0e68674ce9894379a9e7f
2020-05-23 19:26:58 +07:00
Vadim Yanitskiy
0614e9333f csn1: fix csnStreamEncoder(): always check the choice index
It's so easy to pick an out of bounds value otherwise...

Change-Id: I12f5ab739b97f1f3b5d4bed1b5a4a661c879e89f
2020-05-23 18:00:53 +07:00
Vadim Yanitskiy
fac8332649 csn1: fix csnStreamEncoder(): also check length of the choice list
Similar checks are done in csnStreamDecoder(), so better check than sorry.

Change-Id: I441c716975905a37264efc8a76df92194f39c1fb
2020-05-23 18:00:53 +07:00
Vadim Yanitskiy
c9915660ff csn1: fix M_CHOICE: restirct maximum length of the choice list
The current implementation is not capable of handling more than
256 (UCHAR_MAX) selectors in the choice list. Let's document
this and add a guard check to the M_CHOICE handler.

Change-Id: I40c3c5b9be892804c6cd71cbb907af469ce5d769
2020-05-23 18:00:53 +07:00
Vadim Yanitskiy
6fd4733041 l1if: fix: s/pcu_rx_rach_ind_pdtch/pcu_rx_rach_ind_ptcch/g
This is not a functional change, just fixing misleading function
name. Access Bursts on PTCCH/U have nothing to do with PDTCH.

Change-Id: I4ab710ba026315301cc6970263967616401a9fc8
2020-05-22 21:09:22 +07:00
Oliver Smith
0d7bc876c0 Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
Change-Id: If17e82c5fe1fc49877a5a3d9ba250e86091e253c
2020-05-22 13:43:32 +02:00
Pau Espin Pedrol
0b0391ff66 gsmtap: Set signal level and SNR fields
lqual (containing C/I value) is passed instead of SNR, but let's have
that better than nothing.

Change-Id: Ibe9502d42c8bd1b984069e7fd805dde87ecbab0c
2020-05-20 14:20:56 +02:00
Pau Espin Pedrol
2ae8337669 Get rid of class GprsCodingScheme
We have same kind of object splitted into two layers, in coding_scheme
and gprs_coding_scheme. Let's merge them together and get rid of the
class, which is not really useful because it's only a set of functions
operating on one enum value.

This change also fixes gcc 10.1.0 error about memseting a complex type
in rlc.h init().

Change-Id: Ie9ce2144ba9e8dbba9704d4e0000a2929e3e41df
2020-05-20 11:07:07 +00:00
Oliver Smith
c10cb81593 contrib: integrate RPM spec
Remove OpenSUSE bug report link, set version to @VERSION@, make it build
with CentOS 8 etc.

Related: OS#4550
Change-Id: Idfe04c2e2609763387d1309f059c390b8e7ae938
2020-05-19 17:08:33 +02:00
Oliver Smith
d0156fd6a8 contrib: import RPM spec
Copy the RPM spec file from:
https://build.opensuse.org/project/show/home:mnhauke:osmocom:nightly

Related: OS#4550
Change-Id: I4da70814357a326842de52b33934819d3ea133d8
2020-05-19 17:08:33 +02:00
Pau Espin Pedrol
c33a024d4a tbf_ul: Fix UL ACK not sent to MS if intermediate UL block is lost
In normal conditions ACKing of UL blocks is only sent every
SEND_ACK_AFTER_FRAMES (20) frames. Which means if CV=0 is received (and
hence no more packets are received) less than 20 frames before a lost,
the PCU won't ask for a retransmission and wait there until some timer
destroys the TBF.

This issue is shown by TTCN3 test PCU_Tests.ttcn
TC_ul_intermediate_retrans.

Unit tests triggering this condition are adapted. Some similar tests are
not triggering it because BSN/CV relation being used is totally wrong
(like CV=0 being sent on a BSN with previous value than others).

Change-Id: I9b4ef7b7277efa645bdb5becf2e9f6b32c99a9b1
2020-05-19 09:30:23 +00:00
Pau Espin Pedrol
5bb87b83d1 rlc: Move prepare() function out of gprs_rlc_data struct
Newer gcc 10.1.0 is erroring due to memset being applied on a complex
type, so let's start by removing this only function outside of the
struct.

Change-Id: I20426557d9b3049ab275fadb92e10ea8a860a119
2020-05-18 11:07:03 +02:00
Pau Espin Pedrol
c68e97012c rlc: Drop unused function gprs_rlc_data::put_data
Change-Id: I10eb93a1aa6ac1eac15c8e64da300caf2e6ccc44
2020-05-18 11:07:03 +02:00
Pau Espin Pedrol
7b9e56a500 sysmo: femtobts.h: Avoid redefining global variables
Change-Id: I0f04726ae608f499c557cedffb4d86625bccbd5a
2020-05-18 11:07:03 +02:00
Pau Espin Pedrol
09afd6f230 pcu: tbf_ul: Clean up maybe_schedule_uplink_acknack()
Get rid of checking all conditions twice, and update one log message.

Change-Id: I95831991b01961e4b7faddb08d27133acb0ab4d4
2020-05-15 17:03:46 +02:00
Pau Espin Pedrol
97e88fd35f bts: Drop specific functions to add values to stats
Change-Id: I877a9c9a35b6c94c3dd6b1ab3019bc57f6c8568a
2020-05-14 11:19:05 +00:00
Pau Espin Pedrol
a107f8f496 bts: Drop specific functions to add values to counters
It's super annoying seeing lots of functions being called everywhere
only to find out they are only incrementing a counter. Let's drop all
those functions and increment the counter so people looking at code
doesn't see dozens of code paths evyerwhere.

Most of the commit was generated by following sh snippet:
"""
 #!/bin/bash
define_pattern="^CREATE_COUNT_ADD_INLINE"
generic_func="do_rate_ctr_add"
grep -r -l "${define_pattern}" . | xargs cat | grep "${define_pattern}("| tr -d ",;" | tr "()" " " | awk '{ print $2 " " $3 }' >/tmp/hello

while read -r func_name ctr_name
do
 #echo "$func_name -> $ctr_name";
files="$(grep -r -l "${func_name}(" .)"
for f in $files; do
echo "$f: $func_name -> $ctr_name";
sed -i "s#${func_name}(#${generic_func}(${ctr_name}, #g" $f
done;
done < /tmp/hello

grep -r -l "void ${generic_func}" | xargs sed -i "/void ${generic_func}(CTR/d"
grep -r -l "$define_pattern" | xargs sed -i "/$define_pattern/d"
"""

Change-Id: I966221d6f9fb9bb4f6068bf45ca2978008a0efed
2020-05-14 11:19:05 +00:00
Pau Espin Pedrol
2338e5318e bts: Drop specific functions to increase counters
It's super annoying seeing lots of functions being called everywhere
only to find out they are only incrementing a counter. Let's drop all
those functions and increment the counter so people looking at code
doesn't see dozens of code paths evyerwhere.

Most of the commit was generated by following sh snippet:
"""
 #!/bin/bash
grep -r -l ^CREATE_COUNT_INLINE . | xargs cat | grep "^CREATE_COUNT_INLINE("| tr -d ",;" | tr "()" " " | awk '{ print $2 " " $3 }' >/tmp/hello

while read -r func_name ctr_name
do
 #echo "$func_name -> $ctr_name"
files="$(grep -r -l "${func_name}()" .)"
for f in $files; do
echo "$f: $func_name -> $ctr_name";
sed -i "s#${func_name}()#do_rate_ctr_inc(${ctr_name})#g" $f
done;
done < /tmp/hello

grep -r -l "void do_rate_ctr_inc" | xargs sed -i "/void do_rate_ctr_inc(CTR/d"
grep -r -l "CREATE_COUNT_INLINE" | xargs sed -i "/^CREATE_COUNT_INLINE/d"
"""

Change-Id: I360e322a30edf639aefb3c0f0e4354d98c9035a3
2020-05-14 11:19:05 +00:00
Pau Espin Pedrol
5dc6e465cb Drop unneeded arg 'ta' in tbf_alloc_ul()
The function is simply setting the ta on the ms, so simply make sure ta
is set on callers before passing the ms object.

Change-Id: Iebb9c57f458690e045ddc45c800209ad8cf621e0
2020-05-14 11:19:05 +00:00
Pau Espin Pedrol
ce60011e77 pdch: rcv_resource_request(): Clarify tbf_free only needed if MS used to exist beforehand
Variable found is used to always call Guard() on MS to avoid possible
unexpected freeing regressions.

Change-Id: I62f24fe04ca10fca19bedda288fe3ed3ce75413f
2020-05-14 11:19:05 +00:00
Philipp Maier
de0e558baf gprs_debug: Use only LOGL_NOTICE as default loglevel
The default loglevels of some log categories are configured to
LOGL_INFO. This is still to verbose, lets use LOGL_NOTICE here.

Change-Id: Ibb1cd1a94fb4fdd0147e073f8c1c82562c2c14ef
Related: OS#2577
2020-05-13 16:41:55 +00:00
Pau Espin Pedrol
322456ed47 Expect ms object to exist before calling tbf_alloc_dl_tbf()
Same as previous commit, this time for the DL counterpart.

Change-Id: I87f6cdf8288a688466020bda0874e68b57aa71c4
2020-05-12 17:09:20 +00:00
Pau Espin Pedrol
17402a5902 Expect ms object to exist before calling tbf_alloc_ul_tbf()
It's really non-sense from architectural point of view to pass an
optional pointer to the MS holding the TBF and creating it otherwise.
TBFs shouldn't be creating MS they belong too.

This simple change requiring so many code line changes really exhibits
how badly entangled the object relationship is.

Another commit will follow doing the same for dl tbf.

Change-Id: I010aa5877902816ae246e09ad5ad87946f96855c
2020-05-12 17:09:20 +00:00
Vadim Yanitskiy
f094b46d1c fix egprs_mslot_class_from_ra(): multislot class may not be present
For more details, see 3GPP TS 44.060, table 11.2.5a.2.

Change-Id: Iba0466b29afd26cff317ed4fb6749f8a3079f16a
Signed-off-by: Vadim Yanitskiy <axilirator@gmail.com>
Related: OS#1548
2020-05-11 20:06:39 +00:00
Pau Espin Pedrol
db9ea55c6e Use OSMO_FD_* instead of deprecated BSC_FD_*
New define is available since libosmocore 1.1.0, and we already require
1.3.0, so no need to update dependenices.
Let's change it to avoid people re-using old BSC_FD_* symbols when
copy-pasting somewhere else.

Change-Id: Ida8fd3bd7347163567acde34ad67aefee913b0ea
2020-05-09 19:18:06 +02:00
Pau Espin Pedrol
e6bca376aa bts: Return uint8_t in egprs_mslot_class_from_ra()
MultislotClass is 5 bit long, so an uint8_t is enough.
In most places we are already storing multislot class as uint8_t.

Change-Id: I1dcaff9d69379453a0b794e5f36b820f5f78531f
2020-05-08 16:37:25 +02:00
Pau Espin Pedrol
98eb03c391 bts: Fix Decoding EGPRS MultislotClass from 11-bit EGPRS PACKET CHANNEL REQUEST
In osmo-pcu datatructures, the variables holding multislot classes
simply contain an integer referring to the multislot class number,
instead of coding from 3GPP TS 44.060 Table 11.2.5.3 and Table
11.2.5a.3.

So coding Multislot class 3 is stored as 0x03 in osmo-pcu variables,
while in 3GPP TS 44.060 coding it's coded as 0x02 (N-1).
This allows us using value 0x00 to designate a "yet unknown (EGPRS) Multislot
class".
Hence, we need to add 1 to the decoded value to match our data
structures.

Change-Id: Id3b121272bb7e84c0542ae9b4ce09598c6054edd
2020-05-08 16:35:51 +02:00
Pau Espin Pedrol
9ee90d2323 bts: Rename mslot_class_from_ra
This function is actually returning an EGPRS multislot class, so let's
update naming. The variable using the return value was already being
passed as egprs_ms_class to tbf_alloc_ul_tbf().

Change-Id: Idb51836c8c9dd4e865bf2cb0b0c24155662f2ae8
2020-05-08 16:34:50 +02:00
Pau Espin Pedrol
acd67cbdf1 tbf: Avoid crash: don't set TBF window size if setup failed
Should fix assertion triggered due to the tbf not set up properly
beforehand.

Fixes: OS#4524
Change-Id: I267b147520ef5a50f40ad4bc19e7b5fb3e708127
2020-05-08 13:50:54 +02:00
Pau Espin Pedrol
20331ae5f6 pdch: Avoid sending GSMTAP_CHANNEL_UNKOWN for rejected UL EGPRS data block
Even if we don't accept it, let's submit GSMTAP with correct channel.
We don't return error like in code below, because otherwise the generic
UNKNOWN gsmtap message will be sent.

Change-Id: I853679ce8907d46fcb84ae4127335c10623f09c9
2020-04-29 21:02:12 +02:00
Pau Espin Pedrol
3301cc900e pcu_l1_if: Don't use GSMTAP_CHANNEL_PACCH when sending unknown gsmtap blocks
It's actually counter-productive when analyzing wireshark traces, since
one may not spot a decoding issue and assume PACCH is sent on the wrong
TS.

Change-Id: I7a96148f1ca1ebfa88a3ff714ea3bb8798866046
2020-04-29 20:56:31 +02:00
Vadim Yanitskiy
3f27fb56e4 TBF/UL: fix rcv_data_block_acknowledged(): print the actual TLLI
Change-Id: I71b5c656d4b318d11bd5fe2b5d163c3a06e09a6a
2020-04-20 13:18:49 +07:00
Vadim Yanitskiy
72ec18ce5a sba: fix possible memleak in SBAController::alloc()
Change-Id: I417eda155cd5b1e46dd0b05db3f507abd79121d1
2020-04-20 11:25:05 +07:00
Harald Welte
a4e8c10a38 TODO: remove those that have obviously been implemented 5+ years ago
The TODO file hasn't seen any updates sicne 2014, while the code has
evolved.  I'm not sure about some of the topics, but at least several
can for sure be removed by now

Change-Id: I56581c9b2a08edb1b6d4dc53ad9eb6fdd1800a0d
2020-04-17 16:02:26 +00:00
Eric
2e90a1f015 configure.ac: fix libtool issue with clang and sanitizer
As pointed out at https://github.com/libexpat/libexpat/issues/312
libtool does not play nice with clang sanitizer builds at all.
For those builds LD shoud be set to clang too (and LDFLAGS needs the
sanitizer flags as well), because the clang compiler driver knows how
linking to the sanitizer libs works, but then at a later stage libtool
fails to actually produce the shared libraries and the build fails. This
is fixed by this patch.

Addtionally LD_LIBRARY_PATH has no effect on conftest runs during
configure time, so the rpath needs to be set to the asan library path to
ensure the configure run does not fail due to a missing asan library,
i.e.:

SANS='-fsanitize=memory -fsanitize-recover=all -shared-libsan'
export CC=clang-10
ASANPATH=$(dirname `$CC -print-file-name=libclang_rt.asan-x86_64.so`)
export LDFLAGS="-Wl,-rpath,$ASANPATH $SANS $LDFLAGS"

Change-Id: I999adf84a34c03765ce6c32ece0e61d0ac6e1c13
2020-04-11 01:19:46 +02:00
Vadim Yanitskiy
b30f28f38d l1if: fix pcu_rx_rach_ind(): use proper format string specifiers
Change-Id: If95362ef4cc203a60856d6b47d95d441813a5c19
2020-04-02 05:42:57 +07:00
Pau Espin Pedrol
c374ab00ac csn1: Remove code block from CSN_NEXT_EXIST_LH
It was removed in wireshark.git e8407dd6c1378427daee77e8de540d0b5f7a0b73
and it's not there anymore in current master.

Change-Id: I73f4eeca3fd4f00a5bc4f06ef7a9bb9b8a70e37b
2020-03-30 10:08:02 +00:00
Pau Espin Pedrol
c8280a538a csn1: Properly verify CSN_BITMAP length
Change-Id: I9f7672b534f9345caff99095504749eebad25adb
2020-03-30 10:08:02 +00:00
Pau Espin Pedrol
f5e275aec0 csn1: verify enough bits present to decode whole CSN_UINT_ARRAY
Change-Id: I4a762a8fec4153b53e10df1ec8ba3708c1f47649
2020-03-30 10:08:02 +00:00
Pau Espin Pedrol
70a211747b csn1: Fix Several typos & whitespace
Change-Id: Ibe31d52d4a5a4015196d73681082f68b99a80c77
2020-03-30 10:08:02 +00:00
Pau Espin Pedrol
99eb353337 rlcmac: add dissection of 2G->3G/4G PS handover
Port from wireshark.git 428ee66ae1c524b49f9043729b1f1e9b4f52f409, from
Pascal Quantin.

The original commit is also changing the RRC_Container field to
M_CALLBACK, but we leave them as M_VAR_ARRAY since the callback is
basically used to add more dissection information in wireshark.

Change-Id: I0f374e78300efddff00c4df26a401adcdee18a12
2020-03-30 10:08:02 +00:00
Pau Espin Pedrol
f3ac06bbaf rlcmac: support decode FDD_CELL_INFORMATION of "UTRAN FDD Description
Port of patch (+ later fixes squashed) of wireshark.git commit
dea5452b95dfaf18e38670a8e2b3b38f9175fdfd, from Lei Chen:
https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=6856

Squashed wireshark.git fix commits:
774be29de0b4d93d01aecb1518c41d7d551071a9
51c31cd7bd3d8fc196a9f90a8af466ad84e9e6a8
6aca10831f86c562970b13efa811f46e25ee3091
c1ceac58cdb77051e9bd14c1f6f7669cf5779a86

Change-Id: I08523bc1bbdffde479ef974b4c7b56cfa5639591
2020-03-30 10:08:02 +00:00
Pau Espin Pedrol
b2653fe619 Move gsm_rlcmac.cpp -> .c
Original file from wireshark.git (packet-gsm_csn1.c) is being built and
maintained as a C file. There's no real need for us to maintain it as a
C++, and doing so will make both files derive over time (as already
happened). Let's keep it as a C compiler (which btw seems to be more
strict) to make it easier to port patches back and forth wireshark.git.

Take the chance to move some declarations we added to csn1.h to be able
to build it out of wireshark. Let's keep those in a separate header file
to ease looking for differences.

Change-Id: I818a8ae947f002d35142f9f5473454cfd80e1830
2020-03-30 10:08:02 +00:00
Pau Espin Pedrol
799cf8221a gsm_rlcmac: Disable unused CSN1 descriptors
When switching to C compiler, it will warn/error. Use #if 0 as in the
original wireshark.git epan/dissectors/packet-gsm_rlcmac.c code.

Change-Id: If1be50947c02208f15892d99edeb394fb4f52b75
2020-03-30 10:08:02 +00:00
Vadim Yanitskiy
d2e50e7f21 fix: properly include pure C headers from C++ code
Header files included from libosmocore may potentially contain
some language constructions allowed in C but not in C++, such
as type casting. Let's add 'extern "C" { ... }' and be safe.

Change-Id: I7197f7b34f30b49d5397506ce9d67cbf0e2cc196
2020-03-29 01:46:36 +07:00
Pau Espin Pedrol
a0cbde700a tbf.cpp: Include c++ <new> header required for new operator's replacement type
Including the <new> header is required as explained by the c++ specs [1]

osmo-pcu/src/tbf.cpp: In function ‘gprs_rlcmac_ul_tbf* tbf_alloc_ul_tbf(gprs_rlcmac_bts*, GprsMs*, int8_t, uint8_t, uint8_t, bool)’:
osmo-pcu/src/tbf.cpp:1002:39: error: no matching function for call to ‘operator new(sizetype, gprs_rlcmac_ul_tbf*&)’
 1002 |  new (tbf) gprs_rlcmac_ul_tbf(bts->bts);
      |                                       ^

Most of the times this issue is not detected because other STL headers
are already including <new>.

[1] http://www.cplusplus.com/reference/new/operator%20new/

Change-Id: Ie5fb536ae29dcf40e2a0dbe67432bebd61b8c7aa
2020-03-27 15:06:41 +00:00
Vadim Yanitskiy
ce160147f4 pdch: cosmetic: use GSM_MI_TYPE_* constants from libosmocore
Change-Id: I1d85c8eded68bc8aa8e90c33b36d335fa775ded2
2020-03-27 14:55:22 +00:00
Vadim Yanitskiy
d71c566ee6 pdch: fix packet_paging_request(): properly print paging MI
This problem problem was discovered by the Undefined Behavior Sanitizer:

  pdch.cpp:210:4: runtime error: load of misaligned address
                  0x60c00002abf2 for type 'uint32_t',
                  which requires 4 byte alignment

Do not convert TMSI to number, use osmo_mi_name() from libosmocore.
Also use this function to print other MI types (IMSI or IMEISV).

Change-Id: Icf8836f216793e342b239c8e6645aac1e82bf324
2020-03-27 14:55:22 +00:00
Vadim Yanitskiy
0b25f693b3 BSSGP: fix: properly encode P-TMSI in RR Paging Request
The TMSI/P-TMSI IE in BSSGP PAGING-PS/CS comes without the MI type
header, that must be present in RR Paging Request. Prepend it.

TTCN-3 test case: I7fbec5b2c5c3943a7413417b623f55c135c152d7

Change-Id: I97fd5ffc15a4a58112d7c37c69b7ac42b0741a0e
2020-03-27 14:55:22 +00:00
Vadim Yanitskiy
f497412f20 BSSGP: cosmetic use OSMO_IMSI_BUF_SIZE from libosmocore
Change-Id: I2079118100a3422e9c2ee63a5ac74e3ee81080b3
2020-03-27 14:55:22 +00:00
Keith
2b6acd8873 Don't check ul_control_block before decoding into it.
This patch corrects an error introduced in
6fd8ffb6fe
That commit allowed us to send the data over GSMTAP even
if the Uplink Control Block had invalid content,
that is to say, if decode_gsm_rlcmac_uplink() returned error.

However the check for ul_control_block->u.MESSAGE_TYPE
was place before decode_gsm_rlcmac_uplink()

Change-Id: Ic47602e5c6a13571b92c0a939fc3514110b82444
2020-03-26 23:32:49 +00:00
Pau Espin Pedrol
e5e2f747c3 csn1.h: Fix trailing whitespace
Change-Id: If17d36378fabeb7d22a513b93b0ecfde899df520
2020-03-26 16:03:56 +01:00
Pau Espin Pedrol
24fa27453f gsm_rlcmac.cpp: Avoid declaring variable in for loop
That's only allowed under C++.

Change-Id: I0da8849a0fb7f9a7ee5e726edea87e91dc411ea8
2020-03-26 16:03:56 +01:00
Pau Espin Pedrol
980ef1e38b cosmetic: Do not indent header includes inside extern C block
Change-Id: I3091d917a13e45b3aef8e52a53dcafa308581652
2020-03-26 16:03:56 +01:00
Pau Espin Pedrol
007056e4ad gsm_rlcmac: Use 'struct bitvec' instead of 'bitvec'
The later is only accepted when compiling as c++.

Change-Id: Ib5004643d4eff3bc9d11b66ddaf262f1c0d2aef6
2020-03-26 16:03:27 +01:00
Pau Espin Pedrol
754b093d16 pcu_l1_if.cpp: Add missing header ctype.h
The file uses the isdigit() function.

Change-Id: Id06ea25969ad9b964b3207479604132d25160f24
2020-03-26 14:49:04 +01:00
Pau Espin Pedrol
de63548f04 rlcmac: Rename field to MS RA Cap2 in Additional_MS_Rad_Access_Cap_t
This fix was spotted by wireshark.git developers while reviewing port of
osmo-pcu commit e50ce6e45c.

Related: https://code.wireshark.org/review/c/36574/
Fixes: e50ce6e45c

Change-Id: Ic5fc3252f61b6a042d9c3def7a6a32b311dd9d9e
2020-03-25 20:26:07 +01:00
Pau Espin Pedrol
10ec506cc9 cosmetic: rlcmac: Fix comment typo and whitespace introduced recently
Change-Id: Ie56898e11e2c9426393af0f6558d340c454fe6c4
2020-03-25 14:32:30 +01:00
Pau Espin Pedrol
e50ce6e45c rlcmac: Introduce MS Radio Access Capabilities 2 to fix related spare bits
There's two variants for the Ms Radio Access Capabilities.
* The usual encoding with spare bits (usually to fill up to octet boundary)
as defined in TS 24.008 Table 10.5.146
And there's too:
* MS Radio Access Capabilities 2 IE from TS44.060 section 12.30, which is
the same but removing all spare bits, and which is used in messages like
Packet Resource Request and Additional MS RAC messages.

The later is used basically for messages having extra IEs after the MS
Radio Access capabilities IE, since they are encoded immediatelly
afterwards.

So this patch does:
* Adds the expected spare bits (M_PADDING) to MS_Radio_Access_capability_t
* Creates a new MS_Radio_Access_capability2_t without padding
* Updates code to use the new "2" version where needed.

Note RLCMACTest long de/encoding line logs change only because the name
of the struct changes (the "2" is added).

Change-Id: Ibd756f80a03452a651e2771dbc628d701e55ac4b
2020-03-23 19:02:01 +01:00
Pau Espin Pedrol
20848c3ae5 rlcmac: Log names of de/encoded rlcmac packet types
Change-Id: I6a6e79d7e12cd5e8e969bf0eaa30ddac6b0aa7d3
2020-03-23 19:02:01 +01:00
Pau Espin Pedrol
7faa5da209 rlcmac: Fix bug receiving RA cap
It seems the assumptions regarding maximum number of RA capabilitites
in one message were wrong. Doing some rough calculations, each RA
capabilitiy value (without extensions) can take around 20ish bits, which
means for a message containing up to 52 bytes that quite a lot of
different values could be theoretically fed in. Let's be safe and
increase the array size to be able to handle all different access
technologies listed in See TS 24.008 table 10.5.146 following
restrictions:
* "The MS Radio Access capability is a type 4 information element, with a maximum length of 52 octets."
* "Among the three Access Type Technologies GSM 900-P, GSM 900-E and GSM 900-R only one shall be present."
* "the mobile station should provide the relevant radio access
  capability for either GSM 1800 band OR GSM 1900 band, not both".

Wireshark requires similar fix (it's not important though because it
currently uses another ad-hoc decoder for RAcap).

Related: OS#4463
Change-Id: I5334eaacfbc238fae8bea50c9e9667c2117f81ff
2020-03-23 19:01:58 +01:00
Pau Espin Pedrol
efad80bfbf csn1: Validate recursive array max size during decoding
This way if CSN1 encoded bitstream contains more elements than what the
defintion expects it will fail instead of overflowing the decoded
buffer.

RA cap struct placed in unit test is taken from a real android phone
sending the value when attaching to the network. Then SGSN sends it back
and osmo-pcu would crash similar to unit test:
*** stack smashing detected ***: terminated
 Process terminating with default action of signal 6 (SIGABRT): dumping core
at 0x4C62CE5: raise (in /usr/lib/libc-2.31.so)
by 0x4C4C856: abort (in /usr/lib/libc-2.31.so)
by 0x4CA62AF: __libc_message (in /usr/lib/libc-2.31.so)
by 0x4D36069: __fortify_fail (in /usr/lib/libc-2.31.so)
by 0x4D36033: __stack_chk_fail (in /usr/lib/libc-2.31.so)
by 0x124706: testRAcap2(void*) (RLCMACTest.cpp:468)

Related: OS#4463
Change-Id: I9fe0e55e0a6a41ae2cc885fba490c1d4a186231e
2020-03-23 15:34:11 +01:00
Pau Espin Pedrol
81b40cbaf3 rlcmac: Don't pass array element to CSN1 descriptors
This way the macros can be used to access the arrays themselves and
calculate its static size to enable validation later.

In the case of Packet_Access_Reject_t, modify the description to use a
M_REC_TARRAY_1 object to get rid of access to 2nd element. The new
description is the correct one, since the first element is mandatory
according to TS 44.060 Table 11.2.1.

Change-Id: I6f10350d4734360c7a15a702c72b59efd84987ee
2020-03-23 15:29:48 +01:00
Pau Espin Pedrol
866becefff tests/RLCMACTest: Several fixes and improvements to RAcap tests
It was recently discovered that the Racap value used for testRAcap was
actually malformed (it was taken from a TTCN3 test). It had the presence
bit for "EGPRS multislot class" set but no struct was put after it.

So let's move that malformed value to a new test named
testMalformedRAcap(). Since it doesn't make sense trying to re-encode or
do similar things with an initially malformed value, let's drop all
those parts in this new test.

For the old testRAcap() test, use a new bitstream this time with the
"EGPRS multoslot class" struct set inside (class 3).

Change-Id: I1e7f8d8866695732ee24a79d8b54d660fd4f22d5
2020-03-21 01:24:28 +01:00
Pau Espin Pedrol
2f16924959 tests/RLCMACTest: free allocated bitvectors
Change-Id: I6d1e93cb1a07a7bf05483dbc877105a86a17829b
2020-03-20 23:59:44 +01:00
Harald Welte
570f9135cd csn1.c: Almost all of the logging is DEBUG, not NOTICE
low-level text decodes of CSN.1 messages certainly are not NOTICEable
events, but rather something used for debugging.

Right now we get various text CSN.1 log output of osmo-pcu in it's
default configuration.  Despite all log levels being relatively high
(NOTICE), we still see those messages as they simply are logged
at the wrong level.

Related: OS#2577
Change-Id: I7b42c9e21ad8d8a5b54e7a3b68490934ce3d3198
2020-03-19 15:09:45 +01:00
Pau Espin Pedrol
de7bb75ccf Use downlink BSSGP RA Cap IE
This commit is basically a revert of
f4bb42459c, which disabled the code. That
commit claimed the SGSN may have providen inacurate or wrong data at the
time, but then it should be fixed in the SGSN.

Related: OS#1525, OS#3499
Change-Id: Ie36ae23203110018d4b5ae47591e0a64989e23a0
2020-03-18 14:01:24 +01:00
Pau Espin Pedrol
1de6873810 Use clock_gettime(CLOCK_MONOTONIC) and timespec everywhere
We should really be using monotonic clock in all places that
gettimeofday is used right now. Since clock_gettime() uses timespec,
let's move all code to use timespecs instead to avoid having to convert
in several places between timespec and timeval.
Actually use osmo_clock_gettime() shim everywhere to be able to control
the time everywhere from unit tests.

Change-Id: Ie265d70f8ffa7dbf7efbef6030505d9fcb5dc338
2020-03-16 10:31:56 +00:00
Vadim Yanitskiy
29aeb901e4 csn1: fix: do not return 0 if no bits left in the buffer
Both csnStreamDecoder() and csnStreamEncoder() shall not return 0
prematurely if no more bits left in the input / output bit-vector.

Returning CSN_ERROR_NEED_MORE_BITS_TO_UNPACK might make more sense,
however we don't know in advance (i.e. without entering the loop)
whether it's an error or not. Some CSN.1 definitions have names
like 'M_*_OR_NULL', what basically means that they're optional
and can be ignored or omitted.

Most of the case statements do check whether the number of remaining
bits is enough to unpack / pack a value, so let's leave it up to
the current CSN_* handler (pointed by pDescr) if no bits left.

Return CSN_ERROR_NEED_MORE_BITS_TO_UNPACK only if the number of
remaining bits is negative as this is an error in any case.

Change-Id: Ie3a15e210624599e39b1e70c8d34efc10c552f6c
2020-03-11 19:55:55 +00:00
Vadim Yanitskiy
773cb74ec8 rlcmac: fix encode_gsm_*(): do not suppress encoding errors
Change-Id: Ieec8e6e0823c6f6985f9d343af6d503b8fe9e6ab
2020-03-11 19:55:55 +00:00
Pau Espin Pedrol
40d4e35fb3 tests/llc: Change unrealistic time jump to avoid runtime error under ARM
When running the test under a RaspberryPI4:
+../../../src/llc.cpp:216:29: runtime error: signed integer overflow: 864197544 * 1000 cannot be represented in type 'long int'

864197544 comes from comparing initial insertion time and dequeue time
	  (test does a big jump in time): 987654321 - 123456777

let's use more realistic time changes, since the current one account for
about 37 years.

Change-Id: I28abc9192e0e7c590bc1c3c88950627cf669ffaf
2020-03-11 13:06:15 +01:00
Vadim Yanitskiy
bbafcc1602 tests/rlcmac: also enable logging for DRLCMACDATA category
Change-Id: Idf0808461f7e7a1bce58d11a54238c215126451a
2020-03-07 16:13:26 +07:00
Vincent Helfre
1145fd263c gsm_rlcmac: improve dissection of MS RA Capability IE
Port from wireshark.git de028e81c53f9c45ccc5adb3bffd2f16ae2017bf

This commit breaks transcoding of the test vectors containing
the MS RA Capability IE due to the reasons explained in [1].

The more fields we add, the longer gets the output of the CSN.1
encoder. This is not critical, since we never need to encode
messages containing the MS RA Capability IE on practice.

[1] Ibb4cbd3f5865415fd547e95fc24ff31df1aed4c0

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: Ibb4cbd3f5865415fd547e95fc24ff31df1aed4c0
2020-03-07 05:04:40 +07:00
Vadim Yanitskiy
2679ec0a9f csn1: fix csnStreamDecoder(): skip bits unhandled by serialize()
This change fixes a bug that was reported by Keith Whyte and
confirmed in [1]. The problem is that a user-defined handler
in case of CSN_SERIALIZE may parse only a part of the given
bit-stream, leaving some bits unhandled. This is expected
because the sender (i.e. the MS) may use more recent RLC/MAC
message definitions containing new fields at the end.

Those bits that were left unhandled by serialize() shall not be
interpreted as continuation of the message, they shall be skipped.

Note that the encoded vector in the RLCMAC unit test still does
not match the original one. That's a known bug explained in [2].

[1] If5873355d52d7ddb06c2716154a88d34100f6ab5
[2] Ic46d6e56768f516203d27d8e7a5adb77afdf32b7

Change-Id: Id4cc042fed68fc54aca0355dcb986cab3f6b49ea
Related: OS#4338
2020-03-06 21:49:04 +00:00
Vadim Yanitskiy
f22163b1df tests/rlcmac: add a new test vector for Packet Resource Request
This test vector (from HTC Desire 628) demonstrates a bug in the
CSN.1 decoder. For some reason, OsmoPCU fails to decode it:

  DCSN1 ERROR csnStreamDecoder: error NEED_MORE BITS TO UNPACK (-5)
              at EGPRS_TimeslotLinkQualityMeasurements (idx 164)

while Wireshark dissects it without any errors.

Change-Id: If5873355d52d7ddb06c2716154a88d34100f6ab5
Reported: https://osmocom.org/issues/4338#note-15
Related: OS#4338
2020-03-06 21:49:04 +00:00
Keith
6fd8ffb6fe Send UL-CTRL Packet to GSMTAP even if we fail to decode.
Move the call to send_gsmtap() before the call to decode_gsm_rlcmac_uplink() as if
the latter returns error we return and never get to see the packet on the GSMTAP.

Change-Id: Ia6af9f40590f28fcae3fef50d9c601d8435412cd
2020-03-05 19:51:06 -06:00
Pau Espin Pedrol
0daf913e0c gsm_rlcmac: fix Packet_Resource_Request_t: s/Slot/I_LEVEL_TN/
This is how this field is named in Wireshark.

Change-Id: I140443c48af8e4bb1b6279e6de986879b7d9c276
2020-03-02 13:33:23 +00:00
Vadim Yanitskiy
b47e53b5fa tests/rlcmac: also verify encoding of MS RA Capability
The main idea of this change is to demonstrate a weakness of the
CSN.1 codec that most likely causes a unit test breakage in [1].

The problem seems to be that the transitional structures, where
the CSN.1 decoder stores the results, do not contain any details
about presence of the optional fields (such as M_UINT_OR_NULL).

In other words, it's impossible to know whether some optional
field is omitted in the encoded message (NULL), or is it just
set to 0. This means that the encoder will always include all
optional fields, even if they're not present in the original
message.

[1] Ibb4cbd3f5865415fd547e95fc24ff31df1aed4c0

Change-Id: Ic46d6e56768f516203d27d8e7a5adb77afdf32b7
2020-03-02 13:33:23 +00:00
Pau Espin Pedrol
5fc6e010a5 llc_queue::{dequeue,enqueue}() refactor
As seen in OS#4420, setting the MetaInfo.recv_time outside of
llc_queue before calling llc_queue::enqueue() and later on using that
value in llc_queue itself at dequeue time is not a good idea, since it
can provoke errors if the recv_time was not set correctly.
For instance, LlcTest was not setting the value for recv_time on some
test, which ended up with a huge millisec value when substracting now()
from it:
"""
llc.cpp:215:29: runtime error: signed integer overflow: 1582738663 * 1000 cannot be represented in type 'long int'
"""
This issue only appeared when started building on a raspberrypi4.

Let's better set/store the MetaInfo.recv_time internally during
llc_queue::enqueue(). Then, enqueue() only needs the
MetaInfo.expire_time, so let's change its arg list to only receive that
to avoid confusions.

Take the chance to move the llc_queue APIs to use osmo_gettimeofday,
since we need to fake the time now that the API itself sets that time.

Also take the chance during this refactor to disallow passing null
pointer by default since no user needs that.

Finally, update the LlcTest accordingly with all API/behavior changes.

Related: OS#4420
Change-Id: Ief6b1464dc779ff22adc2b02da7a006cd772ebce
2020-03-02 12:05:06 +01:00
Vadim Yanitskiy
55f06c3d77 tests/rlcmac: fix malformed MS RA capability in testRAcap()
Long story short: as it turns out the test vector '12a5146200'O
has been generated by TITAN, and it's malformed. The length
indicator it contains must be at least 29 bits, not 21. This
field is calculated by TITAN automatically, so I guess there
is a bug somewhere in its RAW encoder implementation.

It's funny that Wireshark decodes the old malformed vector without
any problems if it's encapsulated into the BSSGP DL-UNITDATA. The
reason for that is because BSSGP dissector does not actually use
the CSN.1 codec and relies on its own hand-written parser [1],
which does not respect the length constraints.

Furthermore, table 10.5.146/3GPP TS 24.008, describing the format
of MS Radio Access Capability IE, has the following comment:

  < Multislot capability struct > ::=
    { 0 | 1 < HSCSD multislot class : bit (5) > }
    ...
  -- error: struct too short, assume features do not exist

so ideally our CSN.1 decoder should be more tolerant to the old
malformed vector, but unfortunately error handling is not implemented.

[1] See de_gmm_ms_radio_acc_cap() in epan/dissectors/packet-gsm_a_gm.c.

Change-Id: I5f810397b8d09c18e069168023429f6a4d899c86
2020-02-19 00:07:56 +07:00
Vadim Yanitskiy
e60d9c7e9d gsm_rlcmac: fix misleading LOGP statement in decode_gsm_ra_cap()
Change-Id: I48fd701566e1364ce7fccaa3e3a1a0296b932988
2020-02-18 05:30:57 +07:00
Vadim Yanitskiy
1553049226 csn1: use proper format specifier for unsigned integers
Change-Id: I33f86b79e72394bdb7d99762f8ec21d80e06dc30
2020-02-17 19:40:15 +07:00
Vadim Yanitskiy
4b57b6da54 csn1: bitvec_get_uint() may return a negative, use %d
Change-Id: I3cfd66643ec140150a4089b0e1c493d911d3d7d4
2020-02-17 19:40:15 +07:00
Vadim Yanitskiy
d8e5e8bb3b csn1: fix csnStreamDecoder(): update bit_offset in CSN_EXIST{_LH}
Found while doing differential analysis (comparison against the
original implementation from Wireshark).

Change-Id: Ibd0b7400d78f7873c2a8d45267332f511b5c6fbb
2020-02-17 18:35:37 +07:00
Vadim Yanitskiy
e87066d01e csn1: fix csnStreamDecoder(): always keep remaining_bits_len updated
Found while doing differential analysis (comparison against the
original implementation from Wireshark).

Change-Id: I9f7fa9c3f2f4ff5213dded930cee7ec509b9d799
2020-02-17 18:35:37 +07:00
Vadim Yanitskiy
584daba8e9 csn1: fix csnStreamDecoder(): do not subtract no_of_bits twice
Found while doing differential analysis (comparison against the
original implementation from Wireshark).

Change-Id: Id2a4f03035cd8354d3fba0ad37571453d3986d21
2020-02-17 18:35:37 +07:00
Vadim Yanitskiy
39a65056da csn1: get rid of C++ specific code, compile with GCC
The implementation of CSN.1 codec was taken from Wireshark, where
it's implemented in pure C. For some reason it was mixed with C++
specific features, mostly using references in parameter
declaration. Not sure what are the benefits.

Change-Id: I56d8b7fbd2f9f4e0bdd6b09d0366fe7eb7aa327a
2020-02-17 02:31:15 +07:00
Vadim Yanitskiy
8a87f913bd tests/rlcmac: additionally match debug output of the CSN.1 codec
This would allow us to catch more bugs. Note that I had to remove
printing of pointer address to make the output deterministic.

Change-Id: I1a77441eb957353c919bc73f8e3a2e38f4a383a9
2020-02-17 02:30:58 +07:00
Vadim Yanitskiy
74bc150ab2 csn1: fix existNextElement(): use bitvec_get_bit_pos()
As was discovered recently (see OS#4388), bitvec_read_field()
would never return a negative value because its return type
is unsigned (uint64_t).

We don't really need to get more than one bit, so let's just
use the bitvec_get_bit_pos() instead.

Change-Id: I763a295cd955cd33f542292c85d97ff82f6b49bc
Related: OS#4388
2020-02-16 23:41:26 +07:00
Pascal Quantin
fa5f91c05f gsm_rlcmac.cpp: fix global-buffer-overflow error reported by ASAN
Port from wireshark.git f751918476bdde65f2289b86245a3c30dace6730.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: I70d4ff3e137b5fd13d367bd4ea6ab501e81e7a87
2020-02-15 09:28:27 +00:00
Pascal Quantin
fb65682d34 gsm_rlcmac.cpp: fix another global-buffer-overflow error reported by ASAN
Port from wireshark.git aa3bbe5aebdc180172e7956719b26199e4784fcc.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: I808ec66011cdfe8e1193298f7fb7e92d25b45be4
2020-02-14 19:42:33 +00:00
AndersBroman
2d075be3b8 gsm_rlcmac: Update : PACKET RESOURCE REQUEST to Release 14.0.0
Port from wireshark.git 07fc801684ebff7aff02505cdb2c120caea846e0.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: Iceb59c58406180bc57fe6eb27127b4d11a0a3df7
2020-02-14 19:38:48 +00:00
Vadim Yanitskiy
9b2f7c420e tests/rlcmac: mark Packet Polling Request as malformed
It contails no valid identity, and thus violates the specs.
Let's keep it 'as-is' to check that decoder actually fails.

Change-Id: I663edfdaac0c065e08ab7b6dc50d2f18e433433c
Related: OS#4392
2020-02-11 21:47:15 +07:00
Vadim Yanitskiy
5574a58cd6 csn1: fix csnStreamDecoder(): catch unknown CSN_CHOICE values
After the recent changes [1], it was noticed that one of the unit
tests fails. In particular, a decode-encode cycle of Packet
Polling Request produces a different vector:

  vector1 = 49 13 e0 08 50 88 40 13 a8 04 8b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
  vector2 = 49 13 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
  vector1 == vector2 : FALSE

As it turns out, the original (input) vector itself is malformed
because it contails no valid identity, and thus violates the
specs. The CSN.1 decoder from Pycrate [2] throws an exception
while trying to decode it. I believe we should do the same.

Let's stop decoding the bit stream and return an error in case
if neither of a given list of the choice items matched.

[1] Ia0f8cc224a4c38e80699f834fd83d4c0d99322ea
[2] https://github.com/P1sec/pycrate

Change-Id: I420144773ed5e80372534e0f18db5e74cdb2999d
Fixes: OS#4392
2020-02-11 05:58:44 +07:00
Vadim Yanitskiy
fee767f21c csn1: fix some mistaken CSN.1 error names
Change-Id: I3bd9954ee36212c94f0c4d91581da300c56fce60
2020-02-11 05:28:02 +07:00
Vadim Yanitskiy
9a530a2430 encoding: assert return value of bitvec_set_u64()
Change-Id: Ic0de3ae34f06e41aacacb917f5a0214623259bdc
Fixes: OS#182120
2020-02-10 14:52:33 +07:00
Vadim Yanitskiy
9cbfe11ee2 tbf: fix NULL pointer dereference in create_[ul|dl]_ass()
The problem is that bitvec_free() is not NULL-safe. Ideally we
need to fix it in libosmocore [1], but let's also fix it here,
so OsmoPCU can be safely used with older libosmocore versions.

[1] https://gerrit.osmocom.org/c/libosmocore/+/17114

Change-Id: I7647d17b3d03f8e193ef6e793a2d3c1967744eef
Fixes: CID#208181, CID#208179
2020-02-10 14:12:24 +07:00
Vadim Yanitskiy
c74e217799 tbf: cosmetic: fix spacing in gprs_rlcmac_tbf::create_ul_ass()
Change-Id: Ice4c4db20551753fa4219e7a216309229f7a2ab5
2020-02-09 05:40:51 +07:00
Pau Espin Pedrol
ac2b866426 Fix trailing newline mess with LOGP(C) in rlcmac/csn1
Output was incorrect before this patch. LOPC was being called without
having any initial LOGP, and trailing newline was usually missing at the
end.

Since csnDecoder/encoder functions are recursive, it's difficult to
handle logging state in a coherent way inside them. Let's better simply
control start/end of logging related topics in the callers of those
functions, and simply use LOGPC everywhere in csn1.cpp.

Change-Id: I50da7560939fac360b7545e2a6bfaf45ed0c4832
2020-02-08 11:03:13 +00:00
Vadim Yanitskiy
3ff1a3c6d5 pcu_sock: cosmetic: fix typo in a comment message
Change-Id: Ia7e91d803152ac3f2af88f4552fced27f59d8270
2020-02-08 14:36:01 +07:00
Vadim Yanitskiy
8832c2e5d4 pcu_sock: fix memleak, allocate pcu_sock_state on stack
It was noticed that OsmoPCU leaks memory when trying to reconnect
to the BTS. It could be easily fixed, but we don't really need to
allocate the PCU socket state on heap as we never have more than
one connection.

Change-Id: Iea8930f443caa16f522f7c5375e0004e4e2315cb
2020-02-08 14:36:01 +07:00
Vadim Yanitskiy
d83c8ffb6b VTY: install talloc context introspection commands
Change-Id: I2e1e26be1162b0fb695969e5ac265704adaf9d5c
2020-02-08 14:36:01 +07:00
Vadim Yanitskiy
1ec29c7324 VTY: get rid of pcu_vty_go_parent() / pcu_vty_is_config_node()
Since I2b32b4fe20732728db6e9cdac7e484d96ab86dc5, go_parent_cb()
is completely optional. It no longer has the task to determine
the correct parent node. The is_config_node() callback is no
longer needed too. Get rid of them.

Since Ic5e69a396df659933fd4d50298b9925e837a6861 we depend on 1.3.0.

Change-Id: Id7ce8c4e1ac43747ad40a06d01433c366da07b42
2020-02-08 14:36:01 +07:00
Vadim Yanitskiy
28b4d27209 csn1: fix csnStreamDecoder(): avoid conditional calls to bitvec_read_field()
As was discovered by pespin, changing logging level of DCSN1 makes
the CSN.1 decoder behave differently (see OS#4375). In particular,
this makes RLCMACTest (encode / decode test) fail.

I did a quick investigation and noticed that some of the logging
statements call bitvec_read_field(). By definition this function
moves the internal pointer (current bit position) of a given
vector and increments readIndex by a given amount of bits.

The problem is that LOGPC would not evaluate its format string if
the logging message is not going to be printed, e.g. if a given
logging level is lower than the current one, or in case if
logging is not enabled at all.

The first two conditional calls to bitvec_read_field() are related
to CSN_PADDING_BITS, so that's not critical because padding is
always in the end of messages. The later two are related to
CSN_RECURSIVE_ARRAY and CSN_RECURSIVE_TARRAY respectively.

Let's use bitvec_get_uint() instead to keep readIndex unchanged.

Change-Id: Ia331048db9f790ca407fd341ced01df12d10a233
Fixes: OS#4375
2020-02-06 16:19:47 +00:00
Pau Espin Pedrol
ea9de4ae25 rlcmac: Transform a few LOGPC messages to LOGP
Those messages are self contained and don't need LOGPC.

Change-Id: Iea79e030563cd29bfc9750ff5c3e398c590a7307
2020-02-05 17:27:48 +01:00
Pau Espin Pedrol
5e300ce565 Check return code of rlcmac decode/encode functions
Change-Id: Iabcb768bd714680aa768b35c786dea2015d1e451
2020-02-05 17:26:02 +01:00
Pau Espin Pedrol
47de23266d rlcmac: Return error code from csn1 encoder/decoder
Change-Id: I0c5e1b870608b0622b239791effd5c5878e230bc
2020-02-05 17:25:59 +01:00
Pau Espin Pedrol
d636f74923 csn1.cpp: Rework ProcessError() function to print errors
Same API is kept to more easily keep code compatibility with wireshark's
packet-csn1.c implementation.

Change-Id: I1ce2c52e2357841aa1f31babfdce9011435f866b
2020-02-05 11:44:46 +01:00
Pau Espin Pedrol
f960d5bcf4 cosmetic: csn1.cpp: Fix whitespace
Change-Id: I663c5c20a878b3643db6a8ddd58e29bc9fe93d80
2020-02-03 14:39:38 +00:00
Vadim Yanitskiy
3568fcfa7d gprs_bssgp_pcu: fixup: fix length check in gprs_bssgp_pcu_rx_dl_ud()
Change-Id: I7d49b27a615350a7707154aa3cc903db7c1df374
Fixes: I7f84bd776cc780a45880f136107f6e0bc56241d1
2020-02-03 16:39:12 +07:00
Vadim Yanitskiy
3898cd6843 gprs_bssgp_pcu: fix invalid use of non-static data member 'frame'
The 'gprs_llc' is defined as a pure C structure with C++ specific
extensions (methods), so it's rather a class. Accessing its field
'frame' statically causes Clang to throw a compilation error:

  gprs_bssgp_pcu.cpp:111:29: error: invalid use of non-static data member 'frame'
      if (len > sizeof(gprs_llc::frame))

Let's avoid this and use LLC_MAX_LEN as the size limitation.
God knows what to expect from such a mix of C++ and C...

Change-Id: I7f84bd776cc780a45880f136107f6e0bc56241d1
2020-01-29 06:17:50 +07:00
Vadim Yanitskiy
9f62b92258 tests/alloc: fix implicit conversion from 'double' to 'int8_t'
Looks pretty much like a typo. Both '-1' and '.' symbols are
neighbours in QWERTZ keyboard layout, so it must be -1.

Found by Clang [-Wliteral-conversion].

Change-Id: Id4eb2dcc3b44e18096c7b94efb7260e2400c596b
2020-01-29 06:17:50 +07:00
Vadim Yanitskiy
f6b83a24a3 encoding: fix log_alert_exit(): do not treat error as format string
This is rather a cosmetic change aimed to make ASAN / Coverity happy.
In general, we never pass any input from an untrusted source.

Change-Id: I26d654da4c3bf5fd86a298c3027fd9820c932308
2020-01-29 06:17:49 +07:00
Vadim Yanitskiy
4590b91728 gsm_timer: fix comparison of constant LONG_MAX with an integer
It does not make sense since INT_MAX is always less than LONG_MAX.
Found by Clang [-Wtautological-constant-out-of-range-compare].

Change-Id: I9934e05aa050bf93b3c795376f5dca3a848a7e11
2020-01-29 06:14:54 +07:00
Vincent Helfre
e525bf94ba gsm_rlcmac: add dissection of NAS container
Port from wireshark.git 575e4df4aa3392ffd09ca372859573f09f0a5c57

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: I2a05a057b6f441364502a96f9f34872c7e251a36
2020-01-28 18:16:24 +01:00
Pascal Quantin
e4a243c02b gsm_rlcmac.cpp: fix an out of bounds access
Port from wireshark.git a4a5adb68b898f770e2addf9168d796979ebe237.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: I23fb2199fc8f9cc3e5bd475e2558ee8d482df1e1
2020-01-28 18:08:29 +01:00
Pascal Quantin
463746917f gsm_rlcmac.cpp: Do not skip too many lines of the CSN_DESCR when the field is missing
Port from wireshark.git c4ead251da7199cfd746d378c51eb8c30d09a6ba.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: Ib9b8eafd69d3b45b0d631ba9635689807b472b73
2020-01-28 18:03:59 +01:00
Anders Broman
8ea3cbe6b2 gsm_rlcmac.cpp: hanged all M_BIT macros to M_UINT, as M_BIT does not use the referenced hf.
Port from wireshark.git e97273a35d101516decbc7d98fcc6c6b3f193962.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: Id20d31e9ebd851b45d5f3280f3e229d8d7ae2cea
2020-01-28 17:42:34 +01:00
Pau Espin Pedrol
034e32f0eb gsm_rlcmac.cpp: Fix trailing whitespace
Change-Id: I3b50cf386d417ba73b97f48b3000f69d9a54c8c9
2020-01-28 17:14:26 +01:00
Pascal Quantin
29248d6941 gsm_rlcmac: Enhance dissection of PSI1
Port of wireshark.git 7e9411fee3a101b53693210f7a38789fd4c70ba2.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: I89d488c1f349c556e40a9d13895b1309d5140212
2020-01-28 17:13:39 +01:00
Pascal Quantin
cc76d412fd gsm_rlcmac.h: Remove Uplink messages from the RlcMacDownlink_t structure
(as they are part of the RlcMacUplink_t structure that is also used to call csnStreamDissector function).

Port from wireshark.git commit 9f8b638cfa8a660fb64c54dcadb83e6747db0a15.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: If46f8cc3f21f527f911dcac6ff1b78f182104a00
2020-01-28 16:46:51 +01:00
Gerald Combs
bc4f3930a9 gsm_rlcmac.h: Make sure we have a corresponding 'u' member to RlcMacDownlink_t for every call
Port from wireshark.git commit 6c32ba5ff1a5f5ec2426d1d2c4f4f37fd136bab0.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: I989befc56fa37b8f982301f4f9aa4f4533e3e87a
2020-01-28 16:38:39 +01:00
Bill Meier
bcc6408cd1 gsm_rlcmac.h: #if 0 unused stuff
Port of wireshark.git 2ef0c615946cd290aa9463c637169da0a1ca7972.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: Ia2e80664d293a2a95372213b4164c3e72259e0bb
2020-01-28 16:17:41 +01:00
Alexis La Goutte
e36fb5b802 csn1: fix this statement may fall through [-Werror=implicit-fallthrough=] found by gcc7
Port of wireshark.git commit fd68c7dfc7d06ce7babe914f2575d9e4f35988ad.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: Ibaf47d7c4fdff326ac1dccf6fff77e2357e6a2bd
2020-01-28 13:45:03 +01:00
Pau Espin Pedrol
900c2e277a csn1: Drop format_p union from CSN_DESCR
Port of wireshark.git 8626bb4cbb4d9926f7b56663585d9ef66252f93f.
We don't really need the other fields added there, let's keep only the
value out of the union.

Change-Id: Ia8889252ee7518a919a15d749815c2803b4b23cd
2020-01-28 13:43:45 +01:00
Anders Broman
771da85a11 csn1: Try to fix cast discards '__attribute__((const))' qualifier from pointer target type
Port of wireshark.git 1ff6213c949b373bcb7de5c48a5a4f805093066f.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: Ie14c335a904a17333e98ef58bf5e40245444e956
2020-01-28 13:43:24 +01:00
Guy Harris
e26467c4ed csn1: Don't cast away constness
Port of wireshark.git commit 8e22ded7f8537e37e89ba558c83702d127443ae8.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: I100d5c43d8878e660035bf4a64718771f41a38a8
2020-01-28 13:42:29 +01:00
Anders Broman
60bf845f25 csn1: Fix warning with -Wmissing-prototypes
Port of wireshark.git 2e52e2ac997ca58caabee3270b5a6c3f96159ff0.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: Ic69a75ce3f01cea326139f678b963110e895c356
2020-01-28 13:41:21 +01:00
Pascal Quantin
c515551625 csn1: Fix an infinite loop in CSN.1 dissector when having more than 255 padding bits
Port of wireshark.git 8b5aa913711b32b1e1bc707919d2a98c1875d443.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: I7f6aecc2c0f300c1a77cd683652969d3f1aa5794
2020-01-28 13:40:14 +01:00
Pau Espin Pedrol
5b71697618 csn1: Fix pedantic compiler warnings in csn.1 dissectors
Port of wireshark.git commit 6aca10831f86c562970b13efa811f46e25ee3091.

    From Mike Morrin:
    Fix pedantic compiler warnings in csn.1 dissectors.

    There is some tricky casting going on in csn.1 structures.  To eliminate all
    the warnings, the function pointers needed to be moved out of the object
    pointer unions.  Fortunately macros (mostly) hide these changes from the
    protocol dissector tables.

    https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=7686

    svn path=/trunk/; revision=44899

Change-Id: Ia1a8c50c4b024ca6df4e3fbbf891cd33591ccc9b
2020-01-28 13:38:42 +01:00
Pau Espin Pedrol
7cce825fa4 csn1: Allow CHOICE elements to re-process the bits used for the choice
This is a port of wireshark.git commit
2f024256bf337400ef3a82fa75e6d48d5707e059.

From 78516187d821b8d19d16987b1d6bc855ee7cbe10 Mon Sep 17 00:00:00 2001
From: Sylvain Munaut <tnt@246tNt.com>
Date: Sat, 4 Feb 2012 10:00:22 +0100
Subject: [PATCH 4/6] packet-csn1: Allow CHOICE elements to re-process the bits used for the choice

We may want to display more detail, or the sub-element should be
displayed with its headers or whatever ...

Change-Id: I3a5a95d5f918b8f17a2400a6d0c4d855ecacea7e
2020-01-28 13:38:42 +01:00
Pau Espin Pedrol
98e4c53cad csn1: Extend CSN_SERIALIZE to allow 0 bit of length
Port of wireshark.git 2f024256bf337400ef3a82fa75e6d48d5707e059.

From c6ee558d3bb00bfd25cca7c534448bf60df3c7cf Mon Sep 17 00:00:00 2001
From: Sylvain Munaut <tnt@246tNt.com>
Date: Sat, 4 Feb 2012 10:24:01 +0100
Subject: [PATCH 6/6] packet-csn: Extend CSN_SERIALIZE to allow 0 bit of length

In some coding there is no 'length' field at the top of a serialized
block, or it's more complex than a single field, in which case we
have to rely on the serialize decoder to consume the correct number
of bits.

We extend the CSN_SERIALIZE processing so that if a '0 bit' length
field is specified, then the length is not displayed and the
consumed bits by the serialize function is taken as the length
at posteriori.

The processing keeps the same behavior for any length > 0.

Change-Id: I9fadc99218594447001f7bb9943f4514b9877799
2020-01-28 13:38:42 +01:00
Jeff Morriss
c0b4f4a633 csn1: shuffle decrements of remaining_bits_len
So that they always occur next to an increment of bit_offset.

Port from wireshark.git 1c81971d4292438ffdf83e9f9b9ab96c133c785b.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: I7474e9d632e068d6e33b0a502b81d4fff1f48802
2020-01-28 13:37:50 +01:00
Anders Broman
c0190c8a5a csn1: packet-csn1.c:179: warning: 'pui8' may be used uninitialized in this function
Port from wireshark.git commit fd9f182f4b13a3d81b1b5c797a6e4b9d6d327fdd.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: I37f5f3732f92cd7340af8ac1e04383f3e45e7636
2020-01-28 13:34:36 +01:00
Anders Broman
72c102acf8 csn1: Update M_NULL CSN_DESCR to match wireshark
Port from iwireshark.git commit cc6d4341e65ef2e8d8488fe0ac0f236ece0dd844.
It looks like it makes no difference to us now, but other EGPRS messages
may use it in the future.

Ported-by: Pau Espin Pedrol <pespin@sysmocom.de>
Change-Id: I34039370c292e62790a38abb59f55c69fffa88e8
2020-01-28 13:34:08 +01:00
Pau Espin Pedrol
cdbc5dbd1c tests/rlcmac: Add test to showcase that decode_gsm_ra_cap() fails
Currently code using that function in osmo-pcu is disabled, allegadly
because SGSN was sending incorrect values, but it looks more like a CSN1
issue.

Related: OS#1525, OS#3499
Change-Id: I92c86397f988afaa791871d823a45fa85054f3bb
2020-01-28 13:29:42 +01:00
Pau Espin Pedrol
ea39fade07 tests/rlcmac: Don't check stderr output
Current stderr output is empty anyway, and not checking it allows
enavling different log levels to easily debug issues.

Change-Id: I5b12e919e08a6eeaad31a459e5a15fdee4d76a61
2020-01-24 13:01:23 +01:00
Pau Espin Pedrol
87bfbe4fe2 tests/rlcmac: Use osmo_hexdump to print buffers
Old method takes lots of lines of codes and prints inn unconfortable way
because left-trailing zeros are dropped, making it difficult to split in
bytes.

Change-Id: I56c24f934824e4e52a91a7273aec384b2e15aa67
2020-01-24 12:42:03 +01:00
Pau Espin Pedrol
5cb002f0ef tests/rlcmac: Fix missing commas with unexpected results
Change-Id: Ia0f8cc224a4c38e80699f834fd83d4c0d99322ea
2020-01-24 12:42:00 +01:00
Pau Espin Pedrol
99c437b7c7 tests/rlcmac: Memzero decoded struct
Otherwise final output is undefined.

Change-Id: I9b501b8a99473b4d79279f8a3a9854e0b2eb3284
2020-01-24 12:34:50 +01:00
Pau Espin Pedrol
54681c3ffe tests/rlcmac: print test name at the start
Change-Id: Ib8f0fcbd6bb68d77727c021f0d90d5248e895772
2020-01-23 21:59:23 +01:00
Vadim Yanitskiy
bd0b0b3242 pcu_l1_if.cpp: fix NULL-pointer dereference in imsi2paging_group()
Passing NULL to strlen() would lead to a segmentation fault.

Change-Id: I838e3a21a3b25c2bc8260f67d156c6cc284f4456
Fixes: CID#207484
2020-01-16 00:25:54 +07:00
Pau Espin Pedrol
771de1f439 Support PAGING-CS and PAGING-PS on on PTP-BVCI
Related: OS#2403
Change-Id: I5c52b5af740460c48bb3ba858243b1d20e624268
2020-01-06 10:26:46 +00:00
Pau Espin Pedrol
65a0d1d19b Support Gb PAGING-CS
The paging is sent over PACCH towards MS with an active TBF.

Related: OS#2406
Change-Id: I9501e02e1d7f6944497e724dbccb9a19c3f5221f
2020-01-06 10:26:46 +00:00
Pau Espin Pedrol
5530f12f71 Allow Gb PAGING-PS without P-TMSI
P-TMSI is optional IE, but IE is mandatory and hence always available.
Since the encoding is actually a Mobile Identity, the IMSI is used in
case P-TMSI is not available.

Change-Id: I4dbf8db04e81f98352a42ce34a5d91326be9bfd1
2020-01-06 10:26:46 +00:00
Pau Espin Pedrol
d7c3265223 Pass paging group instead of imsi where later is not needed
Change-Id: Id0663a81f439f2d0b893b0d34f85a6db1927ef8e
2020-01-06 10:26:46 +00:00
Pau Espin Pedrol
b507e428e8 Bump version: 0.7.0.62-fbfa-dirty → 0.8.0
Change-Id: Ic5e69a396df659933fd4d50298b9925e837a6861
2020-01-03 19:40:02 +01:00
Pau Espin Pedrol
fbfab297ee Split identity_lv param into mi+mi_len
It's not really needed to have those together in some function calls,
and makes it more difficult to follow the code. Furthermore, new callers
not having content already aligned (len+value) will be using these
functions in forthcoming commits.

Change-Id: Ifb9d3997bfb74b35366c3d1bc51ce458f19abf16
2020-01-01 16:10:15 +00:00
Pau Espin Pedrol
db12f254ce Log BVCI PTP value upon msg recv
Change-Id: I47c5902112d568cd5a48e003010d8085b02d64e8
2020-01-01 16:10:05 +00:00
Pau Espin Pedrol
506eb23dc5 fix typo in log message
Change-Id: Ib6fc4625242d855193b62b561624b23b265648b9
2019-12-31 17:01:34 +00:00
Pau Espin Pedrol
585cfb28b8 Fix trailing whitespace
Change-Id: I0515b5c8f6744fa501de88fa7808b7fc91981f0e
2019-12-10 20:02:21 +01:00
Pau Espin Pedrol
3f064f516d prs_bssgp_pcu.cpp: Mark priv funcs as static and remove trailing whitespace
Change-Id: I93b7ee33cc33c773675c85ace7b8f1afa86fbf06
2019-12-10 17:09:51 +01:00
Pau Espin Pedrol
32499b614b pcu_l1_if: Check pag_req id_lv len fits buffer
Related: OS#4316
Change-Id: I803e1d2577a0d210e74feb5ca4c216375a5024ea
2019-12-09 13:55:14 +01:00
Pau Espin Pedrol
30f6617c79 tbf_dl.cpp: Fix typo in log line
Change-Id: I9fdea4246c95897f3e72604981597db828a219a3
2019-12-04 18:11:52 +01:00
Pau Espin Pedrol
d0fc9e80fe Remove dash from name used in VTY cmd prompt
Others projects don't contain a dash in there, and it seems to cause
problems with TTCN3 VTY expectations.

Change-Id: I3430abb5fc622dec293457466e760de95fa3a05c
2019-12-02 11:14:26 +00:00
Harald Welte
3a61b920f2 manual: Add missing documentation for '-i' command line option
Change-Id: Iec0f2ecd610e63baba46a63d3e1a0979a9d8c5a8
2019-12-01 14:32:54 +00:00
Harald Welte
f6d282822e manual: Fix documentation missing "-D" command line option
Change-Id: I2fd0b6f52ddc6931f413921b45d9756cb1fe456d
2019-12-01 14:32:54 +00:00
Harald Welte
c925ccccfc manual: Fix copy+paste error
Change-Id: I27744628edeca6895f50f84c6f2f04c2e6e2de0b
2019-12-01 14:32:54 +00:00
Vadim Yanitskiy
657a4c0ccd VTY: cosmetic: use osmo_talloc_replace_string()
Change-Id: Id09c7d24b48ddecfa96404c3e75330465a11f830
2019-11-30 20:17:25 +07:00
Vadim Yanitskiy
fc75cc0ecf VTY: add warning about changing PCU socket path at run-time
Change-Id: I7cee2d782bd3dfc2cc8d2febc16dca905dcc294e
2019-11-30 20:17:21 +07:00
Pau Espin Pedrol
1e6eb30f51 Clarify (M)CS related VTY attributes
Some are used to control (M)CS values for downlink while some do it for
uplink. Let's make clear which one is used for what. Take the chance to
document the fields a bit better than they were.

Some more information about the origin of cs_downgrade_threshold can be
found in the commit introducing it: 70b96aa232.

Related: OS#4286
Change-Id: I4e890e924b094a1937fbd3794de96704cf0421a8
2019-11-28 17:11:56 +01:00
Pau Espin Pedrol
1d8497ba6a doc: vty: Update osmo-pcu_vty_reference.xml
Change-Id: I287893cabf0468f0c110eb751eb887f58ff238c0
2019-11-28 17:09:09 +01:00
Vadim Yanitskiy
ffebd24456 PTCCH: properly handle RACH.ind for PCU_IF_SAPI_PTCCH
Change-Id: I482d60a46b9d253dfe0b16140eac9fea6420b30c
Related: OS#1545
2019-11-23 17:42:45 +07:00
Vadim Yanitskiy
17954da56c pcuif_proto.h: extend RACH.ind with TRX / TS numbers
Since there can be multiple PDCH channels configured on different
timeslots, different TRXes, and BTSes, the PTCCH/U handling code
in OsmoPCU needs to know the exact origin of a given RACH.ind.

Otherwise, it is not known which subscriber originated a given
PTCCH/U indication, and hence it is impossible to send PTCCH/D
Timing Advance notification properly.

Fortunately, we can extend the RACH.ind message without even
bumping the protocol version, because every single PDU has a
fixed size defined by the largest message - INFO.ind. In case
if the actual message payload is smaller, the rest is filled
with a constant padding byte (0x00).

Older versions of OsmoPCU will consider the new fields as padding,
while the messages from older OsmoBTS versions will always have
both fields set to 0x00. Since C0/TS0 cannot be configured to
PDCH, this can be easily detected on the other end.

Change-Id: If209001885ffb14b64a8e808df3700d85a4b2ef9
Related: OS#1545
2019-11-17 02:58:35 +07:00
Vadim Yanitskiy
78f58618f3 PTCCH: properly handle RTS.req for PCU_IF_SAPI_PTCCH
Change-Id: Ib204acce1a7e33f6651b9da2a7b4a9b9ae461093
Related: OS#1545
2019-11-17 02:58:31 +07:00
Vadim Yanitskiy
bd0dac3783 PTCCH: implement basic message codec and API
Change-Id: Id79e95aafdde4a71977c64385fce48b729a51ca9
Related: OS#1545
2019-11-17 02:35:18 +07:00
Vadim Yanitskiy
0bf622e057 gprs_bssgp_destroy(): fix memleak and NULL-pointer dereference
So far there was a memory leak, because free()ing 'the_pcu.bctx'
would cause ASAN to complain. And that's reasonable, because it
needs to be freed properly. Moreover, 'the_pcu.bctx' may simply
be uninitialized in some cases, e.g. when OsmoPCU is terminated
before connecting to the SGSN.

Let's use the new bssgp_bvc_ctx_free() from libosmogb.

Change-Id: I274e79e1746c7678b81720ec11e8a564befe38ba
Depends: Ia78979379dbdccd6e4628c16f00d0c06d9212172
2019-11-10 09:04:21 +00:00
Vadim Yanitskiy
fd734de4d1 GprsMs::update_cs_ul(): clarify the meaning of old_link_qual
Change-Id: Iad703a573621c64613b9b8c229079dc63fcaeb9e
2019-11-08 06:20:55 +07:00
Vadim Yanitskiy
87b6e7dbed tests/tbf: suspend warnings about the link quality measurements
Share a single instance of 'pcu_l1_meas' between all unit tests,
set initial measurement values in main(). This way we can get
rid of the following warnings:

  Unable to update UL (M)CS CS-X because we don't have link quality measurements.

Change-Id: I1c82076df6cd0833d243e1e6afb140bae3bd2ec9
Fixes: OS#3828
2019-11-08 04:44:07 +07:00
Vadim Yanitskiy
ce27d1e126 BSSGP: properly print BVCI for signalling messages (BVCI=0)
Change-Id: I4ac0f48d2e62cd0545e8a1e1b26c9e43ef5e8dde
2019-11-08 04:18:46 +07:00
Vadim Yanitskiy
ef444142c8 BSSGP: do not reject SUSPEND ACK / NACK messages
Both BSSGP SUSPEND ACK and NACK messages use BVCI=0 (signaling),
which always exists. Claiming that BVCI=0 is unknown is wrong.

Instead of adding both BSSGP_PDUT_SUSPEND_{ACK,NACK} to the 'if'
statement, let's rather avoid rejection for all BVCI=0 messages,
as there may be other unlisted message types.

Change-Id: I780657c1e8f67e0bef0e92a31db7ba61b57d7ec4
Related: OS#4111
2019-11-08 04:11:20 +07:00
Pau Espin Pedrol
05bca3524a Fix assertion hit upon CCCH Paging Request
Recent commit added an assertion to check for buffer boundaries and it
actually gets hit.
One of the 2 code paths calling pcu_l1if_tx_pch() was passing a buffer
of 23 bytes while one of maximum 22 is expected (because plen is not set
in the buffer but set inside pcu_l1if_tx_pch()).
So it seems before the assert, that code path was actually writing 1
byte outside the boundaries of data buffer, since bitvec_pack() uses
data_len field of bitvec.

Related: OS#4228
Fixes: 8dc09e73d0
Change-Id: I84c5dfd4d5580e9d4c00ed21887cb51bd9abbd2e
2019-10-16 14:36:28 +02:00
Alexander Couzens
b3b0c49d1c encoding: fix space, tabs
Change-Id: I80ac88f50bfedfd2b86d548883313b5a187b1e8f
2019-10-09 17:14:17 +00:00
Vadim Yanitskiy
cef2f843b4 VTY: fix command 'show tbf all': properly filter TBFs
For a long time the VTY command to show all active TBFs was broken.
The TBF filtering (by allocation origin) logic allows one to show
TBFs allocated on CCCH, PACCH, or on both of them. In the latter
case we have been checking whether a TBF was allocated on both
logical channels at the same time.

Let's fix this by passing a flag-mask instead of boolean arguments.
To be able to use GPRS_RLCMAC_FLAG_* definitions from "tbf.h", let's
exclude them from "#ifdef __cplusplus ... #endif" block.

Change-Id: I1c9f401368af880a97d32905c4cce0da481ffc21
2019-10-09 22:00:54 +07:00
Vadim Yanitskiy
afbf189ef2 VTY: refactor pcu_vty_show_ms_all(): use show_ms()
Change-Id: I72aa1a1de22602a3ad2a4d19604ae0935c88c750
2019-10-09 20:26:47 +07:00
Pau Espin Pedrol
d752d7cebe pcu_l1_if.cpp: Replace value 23 with libosmocore's GSM_MACBLOCK_LEN
Change-Id: Ieec3dd028fffa1a735afaaf3f93da0a1202d122a
2019-10-07 21:04:19 +02:00
Pau Espin Pedrol
8dc09e73d0 pcu_l1_if.cpp: Imm Assign PCH: clarify size of different items
Change-Id: I32876858e3e93951e965b0fc7875c95c1f36f3ac
2019-10-07 21:04:19 +02:00
Pau Espin Pedrol
f681f07cd0 pcu_l1_if.cpp: Drop unneeded byte in Imm Ass PCH buffer
paging group is 3 bytes and imm assign with plen prepended is 23 bytes,
so there's 1 extra byte not needed and makes code confusing.

Change-Id: Id7835e5aa1506505ff54e019b38f30111f79b5dc
2019-10-07 21:04:19 +02:00
Pau Espin Pedrol
2ccb6aef89 pcu_l1_if.cpp: Fix GSMTAP Imm Assign PCH wrong encoding
Wireshark expects to receive the plen in order to decode it.

Fixes: 58543709e4
Change-Id: I91d1354689300b949760cdbaee03294eab958e12
2019-10-07 18:26:10 +02:00
Pau Espin Pedrol
58543709e4 Log AGCH and PCH blocks using GSMTAP
Change-Id: I4d62f98801af1b0a290d3dd35bd213ccf3151035
2019-10-01 10:38:55 +02:00
Pau Espin Pedrol
1ec211f57f Log RACH Requests using GSMTAP
Change-Id: Ib686a49e8c630808c30bede5810cd65fc045954a
2019-09-30 20:15:54 +02:00
Pau Espin Pedrol
1879442443 vty: Fix osmo_tdef timers not listed in write config
Change-Id: I5c7ae18919e4b016505aa01eea6694d8a3f5df5f
2019-09-26 18:25:35 +02:00
Pau Espin Pedrol
f4c77e686a tbf_dl.cpp: Remove dup call to tbf_update_ms_class() in state GPRS_RLCMAC_WAIT_RELEASE
tbf_update_ms_class() is already called two lines above in the common
path.

Fixes: 409efa1ec8
Change-Id: Icbe3805c72a5c77366215be55128b586e5a00fb7
2019-09-26 14:43:22 +02:00
Pau Espin Pedrol
ad586a9fe4 tbf_dl: Setup m_llc_timer in constructor using osmocom API
Change-Id: I3e761b319326e33ab1d56c4fb30cafe3b0f96c29
2019-09-26 14:43:22 +02:00
Pau Espin Pedrol
bddf1ad7f6 Move tbf_{dl,ul} child constructors to respective .cpp files
Fixes: 9d1cdb1f69
Change-Id: Id258589d46de42ad4e27889bc396f930b7f94b79
2019-09-26 14:43:22 +02:00
Pau Espin Pedrol
9d1cdb1f69 Move out tbf subclasses from tbf.h to their own headers
It's a good start towards clearing current mess between parent and the 2
children classes.

Change-Id: Ibc22ea2e02609af7ee058b8bc15df2115d4c6f60
2019-09-25 17:50:06 +02:00
Pau Espin Pedrol
488aa29083 cosmetic: fix whitespace
Change-Id: I45bbe4d3c69d573aaff010d16f338c7ec3eaf08a
2019-09-25 17:50:06 +02:00
Pau Espin Pedrol
e13cdc503e pdch.cpp: Use pcu_l1_meas previously filled by lower layers
Otherwise, a new meas object is allocated in the stack in upper layers
which doesn't contain the link_qual information (have_link_qual=0),
outputting following error:

osmo-pcu/src/gprs_ms.cpp:644 Unable to update UL (M)CS CS-2 because we don't have link quality measurements.

Change-Id: I1980ca325c8d65f3f6310fa697dd810eec7ab077
2019-09-25 13:52:31 +02:00
Pau Espin Pedrol
812d466bbd pdch.cpp: Refactor bitvec param passing in rcv_control_block
Move code in rcv_block_gprs() only needed for rcv_control_block() into
the later. This way rcv_block_gprs() is simplified and shows similar
code paths with regards to rcv_data_block().
It can now be seen that the main difference between both is the meas
param no being passed in the control case.

Change-Id: I2a0133463edced93c72ccc743a0cf00d1d6922cf
2019-09-25 13:35:13 +02:00
Oliver Smith
04797b1e35 configure.ac: set C and C++ dialects
Make sure that the compiler always assumes the same C dialect, to
prevent unexpected compiler errors when building with older compilers
(on other Linux distrubtions like in OBS, or in docker).

Use gnu89 and gnu++03, because that is what the code currently compiles
with.

Related: https://lists.osmocom.org/pipermail/openbsc/2019-September/013030.html
Related: OS#3598
Change-Id: Ia57ba101627e3cc0babeca82631e207a3e2e0960
2019-09-18 14:28:43 +02:00
Oliver Smith
2beb1b85e0 tests/app_info: fix compiling with older g++
Do not use C++11 extended initializers to prevent the following error.

AppInfoTest.cpp:109:54: error: extended initializer lists only available with -std=c++11 or -std=gnu++11
  pcu_prim.u.app_info_req = {0, 15, {0xff, 0x00, 0xff}};

I ran into this when modifying the gerrit build verification job to
build with docker (which still uses GCC-4.9).

Related: OS#4204
Change-Id: I307cd87af88e86804a90d6466e9cc3909bfe701f
2019-09-18 13:58:42 +02:00
Pau Espin Pedrol
2b5c629055 Use osmo_tdef to implement dl-tbf-idle-time
Change-Id: I5e4f0d2f90e643600b7752525d6c2830856c9d3b
2019-09-17 11:11:04 +02:00
Pau Espin Pedrol
63700ead34 Use osmo_tdef to implement ms-idle-time
This commit would also remove the option from config_write_pcu() since
it's automatically filled in by osmo_tdef, but there was actually a bug
because that param was never printed when saving the config...

Change-Id: Id8e70b0f44ef2f7e20ecdb3fd8ca93ae2a05b9a3
2019-09-17 11:05:45 +02:00
Pau Espin Pedrol
474dc77894 tests: TbfTest: Unify stderr and stdout to ease debugging
osmo-pcu code is really verbose, and since log lines printing start and
end of tests are sent to a different file, it's really difficult to
understand which test outputs what.

Change-Id: I3e887158e2c9585c360d44f12f995f55861170f2
2019-09-16 14:21:21 +00:00
Pau Espin Pedrol
38cfa734f4 Use osmo_tdef to implement T3190
Change-Id: I0c767c526398d98ca47ef98fdaccfc23af11fb0d
2019-09-16 14:21:21 +00:00
Pau Espin Pedrol
5211d9deca Use osmo_tdef for BSSGP T1 and T2
Change-Id: I477e5b702c8b956136d93fc1cee01991233e381f
2019-09-16 14:21:21 +00:00
Oliver Smith
cfb6321b88 Forward ETWS Primary Notification to MS
Receive an Application Information Request from the BTS via PCU
interface. Construct a Packet Application Information message from it
(3GPP TS 44.060 11.2.47) and send it to all MS with active TBF.

The TTCN-3 test infrastructure to test this feature is not quite ready
yet, so I've added C unit tests instead.

Related: OS#4048
Change-Id: Ie35959f833f46bde5f2126314b6f96763f863b36
2019-09-14 15:28:43 +00:00
Pau Espin Pedrol
5360ef5447 bts.cpp: Fix osmo_tdef initialization on older g++ compilers
Fixing errrors spotted:
bts.cpp:78:1: error: uninitialized const member 'osmo_tdef::T'
 };
 ^
bts.cpp:78:1: error: uninitialized const member 'osmo_tdef::default_val'
bts.cpp:78:1: error: uninitialized const member 'osmo_tdef::unit'
bts.cpp:84:1: error: uninitialized const member 'osmo_tdef::T'
 };
 ^
bts.cpp:84:1: error: uninitialized const member 'osmo_tdef::default_val'
bts.cpp:84:1: error: uninitialized const member 'osmo_tdef::unit'

Change-Id: I2dfecf22516f52cc19e0a0442e70dbc4dbc61336
2019-09-13 12:42:38 +02:00
Alexander Couzens
e46d8dcaab tbf_dl: add comments to the scheduler
Change-Id: Ib037f9fda30472313c7a82effb1e925c6abebbe5
2019-09-12 16:49:21 +00:00
Pau Espin Pedrol
28f160e76c Introduce osmo_tdef infra and timer VTY commands
This will allow for configuration of some of the timers by the user,
and allow him to inspect current values being used.
It will be also useful for TTCN3 tests which may want to test some of
the timers without having to wait for lots of time.

Timers are splitted into 2 groups: BTS controlled ones and PCU controlled
ones. The BTS controlled ones are read-only by the user (hence no
"timer" VTY command is provided to change them).

TbfTest.err output changes due to timers being set up correctly as a
consequence of changes. Other application such as pcu_emu.cpp and
pcu_main.cpp had to previosuly set the initial values by hand (and did
so), but apparently TbfTest.c was missing that part, which is now fixed
for free.

Depends: libosmocore.git Id56a1226d724a374f04231df85fe5b49ffd2c43c
Change-Id: I5cfb9ef01706124be262d4536617b9edb4601dd5
2019-09-12 14:17:07 +02:00
Oliver Smith
45fdc44d68 tbf_dl: make preemptive retransmission optional
Since [1], OsmoPCU already starts to retransmit downlink blocks before
the MS has had a chance to receive them and/or send the related
acknowledgement in uplink. Make this optional with the new VTY option
"no dl-tbf-preemptive-retransmission".

[1] e25b5b91f6 ("tbf: Only create dummy frames if necessary")
Related: OS#2408
Change-Id: Id08aed513d4033aa0d4324c6ce07cbb2852f2f92
2019-09-11 06:16:29 +00:00
Oliver Smith
37ae22ab13 doc: update generated VTY reference
Change-Id: I6243a2856df9db0f06e704e51e87113f9b57bb36
2019-09-11 06:16:29 +00:00
Pau Espin Pedrol
a8892a69b3 Use proper API osmo_timer_setup() to set up timer struct
Change-Id: Idd2b0c5247870bee3b3c3e460a6731ee50a47404
2019-09-05 16:21:49 +02:00
Pau Espin Pedrol
ef1fe58e19 cosmetic: tbf: Rename T and N arrays
Those namings my collide with usual osmocom "T" variable name associated
to a timer number, which will be added in following patches.

Change-Id: Ic2b5068a4882e4a043bf81496be30a378fdb9a09
2019-09-05 14:40:26 +02:00
Vadim Yanitskiy
df0fd8ba27 osmobts_sock.cpp: do not print the same debug message twice
The following message is printed by the pcu_tx_txt_ind():

  DL1IF INFO pcu_l1_if.cpp:113 Sending XXX TXT as PCU_VERSION to BTS

There is no need to print it twice:

  DL1IF INFO osmobts_sock.cpp:74 Sending version XXX to BTS.
  DL1IF INFO pcu_l1_if.cpp:113 Sending XXX TXT as PCU_VERSION to BTS

Change-Id: Ic2793f20cf9df2fa08c45070a8f81ef1c08b925a
2019-08-27 11:06:11 +02:00
Vadim Yanitskiy
9e5ef54594 osmobts_sock.cpp: pcu_sock_read(): further simplify the code
Change-Id: Ie7c0ca8baf0ae5beadda60bda0bc76a44664d439
2019-08-27 11:06:00 +02:00
Vadim Yanitskiy
c7849c2dfe osmobts_sock.cpp: pcu_sock_read(): use stack buffer, not heap
We don't really need to use the message buffer (on heap), because
it's never getting passed to pcu_rx(). Let's use a buffer on stack.

Change-Id: I4cb8ca80513df7b71e1438da2e82799be6be1fa0
2019-08-23 18:03:37 +02:00
Vadim Yanitskiy
aef6bf43e5 osmobts_sock.cpp: pcu_sock_cb(): use libosmocore's socket API
Change-Id: I7f5c7f5744ab14f36f46cf7941e91352eca8d2b9
2019-08-23 18:03:37 +02:00
Alexander Couzens
2585451408 tests: test encoding of egprs ul ack/nacks
Test the encoding with uncompressed and compressed acknowledgements.

Change-Id: I35d6b5e312faeb116ddda6b33c550840da1496fe
2019-08-12 08:40:04 +00:00
Pau Espin Pedrol
19b15a5b93 Bump version: 0.6.0.88-3bcc → 0.7.0
Change-Id: I98719f38c490b70983f2dca24ef743abc00b7d28
2019-08-07 21:09:53 +02:00
Pau Espin Pedrol
3bcc7ab418 Require newer libosmocore to avoid compile failures
Older commit started using OSMO_IMSI_BUF_SIZE, only available in
libosmocore 1.1.0 onwards, but forgot to increase the values in
configure.ac.

Fixes: 2c076bcb4d
Change-Id: Iaf5f276a092c695a5f077551d9784652c2289424
2019-08-07 21:07:49 +02:00
Pau Espin Pedrol
cd2ac56bd4 Remove undefined param passed to {logging,osmo_stats}_vty_add_cmds
Since March 15th 2017, libosmocore API logging_vty_add_cmds() had its
parameter removed (c65c5b4ea075ef6cef11fff9442ae0b15c1d6af7). However,
definition in C file doesn't contain "(void)", which means number of
parameters is undefined and thus compiler doesn't complain. Let's remove
parameters from all callers before enforcing "(void)" on it.
API osmo_stats_vty_add_cmds never had a param list but has seem problem
(no "void"), so some users decided to pass a parameter to it.

Related: OS#4138
Change-Id: Ic1ac815eafab49577ff883a5d700ecca5936d216
2019-08-05 14:30:47 +02:00
Vadim Yanitskiy
2c076bcb4d gprs_bssgp_pcu_rx_dl_ud(): use OSMO_IMSI_BUF_SIZE
Change-Id: Ia1da9f005b7f801872c542d31cc8eabd859d997a
2019-07-24 20:39:57 +00:00
Vadim Yanitskiy
6170ac041f gprs_bssgp_pcu.cpp: check return code of gsm48_mi_to_string()
Change-Id: Id1ad279ce9bef38eb3d11ac62337276207e8d8bd
2019-07-24 20:39:57 +00:00
Vadim Yanitskiy
aad87b66e8 gprs_bssgp_pcu_rx_dl_ud(): fix: BSSGP_IE_IMSI is optional
Change-Id: I940d220a399166122f33e67a222dd572085e1401
2019-07-24 20:39:57 +00:00
Eric Wild
ab8b01effd ubsan: fix shift
Ubsan complains about shifts into the sign bit due to automatic int
promotion, so cast explicitly.

Change-Id: I6387c7313832f6c7c920e1016b74562b66d6b68e
Related: OS#4029
2019-07-24 19:28:38 +00:00
Harald Welte
7b7f2048b8 bssgp: Fix dead code: PDUT_STATUS can never reach this part
Change-Id: Iae4332cd3b87f37164655d3df16554de4876159d
Closes: CID#188855
2019-07-21 09:26:56 +02:00
Thorsten Alteholz
8bb7904458 fix spelling errors detected by lintian
Change-Id: I381618eb55cc513cfa9c2e384c27cead0935c8bf
2019-07-17 10:56:19 +00:00
Max
f3038e7b2a Use libosmocore for IMSI parsing
Change-Id: Iec5c65776fc54b2f9e5dd55c711ace2471662db1
2019-07-16 04:19:21 +00:00
Vadim Yanitskiy
f17dfc062a src/pcu_l1_if.cpp: fix: properly pass measurements from PCUIF
The recent versions of OsmoBTS do provide the following measurements:

  - RSSI (Received Signal Strength Indication),
  - ToA (Timing of Arrival),
  - BER (Bit Error Rate),

as well as C/I (Carrier-to-Interference ratio) since [1] (OS#4006).

[1] https://gerrit.osmocom.org/r/Ia58043bd2381a4d34d604522e02899ae64ee0d26

Change-Id: I0fd6c35e8cf0b1314f4e3c336b233b5f7e42dfc6
Related: OS#1855
2019-07-16 04:11:20 +00:00
Alexander Couzens
210ccf4a1d Encoding: ACK/NACK: always encode with length field present
In most cases the length field was present and this field takes 7
bits of the maximum available 110 rest bits.
The length field was only removed when encoding huge bitmaps usually
only happen on lossy connections with packet lost.
However the cases without length field were encoded incorrect,
because all remaining bits must be used by the uncompressed bitmaps,
but the PCU violates this by encoding always the "release 5" bit.
Rather than fixing the encoding without length field, simply remove it
and always encode with length field. This also reduces the code
complexity.

Change-Id: I7bc2e18d647b72b8f17ba7a5c9c5e421d88275fb
2019-07-11 18:38:45 +02:00
Oliver Smith
5c3a9880ca contrib/jenkins.sh: run "make maintainer-clean"
Related: OS#3047
Change-Id: I733df8f8bfaf448a6507c9c9d75d2f076fedb342
2019-07-11 03:38:37 +00:00
Alexander Couzens
e4e70d052e Encoding: use uint16_t when interacting with the window object
The ESN, SSN and uncompress bitmap len are uint16_t. The Window is using uint16_t in
function arguments and return values. Don't do so many integer conversions.

Change-Id: If62fa09d7bfa8e91ce707824f7019edb1b83da9e
2019-07-11 03:23:52 +00:00
Alexander Couzens
fba931bab6 bts.cpp: ensure left-shift operation does not exceed uint32_t
Found by Asan
Relates: OS#4029
Change-Id: I21640e40e689016d6fb80a8db4257b22e85b303b
2019-07-11 03:22:15 +00:00
Alexander Couzens
243a204021 Encoding: write_packet_ack_nack_desc_egprs: don't use a reference for rest_bits
The rest_bits are never read after calling this function nor are rest_bits
updated properly.

Change-Id: Ic350b0365b125638a6c752f692bef981ad6b9d89
2019-07-11 00:27:15 +02:00
Alexander Couzens
2d24eba903 decompress_crbb: add length argument for search_runlen
search_runlen() must know the exact size in bits when parsing
the bits otherwise it read over the buffer.
Fixes testcase #7 which was wrongly decoded.

Change-Id: Ie34a0651e7e7efea4e9ecff1e3a467588113cf47
2019-06-24 13:51:06 +00:00
Alexander Couzens
3a499f3cb2 Encoding: drop struct gprs_rlcmac_bts* from all functions
The bts is not used at all.

Change-Id: Ia07755e825913a16352ab13f6cf55f2918de8681
2019-06-24 13:48:43 +00:00
Alexander Couzens
0d482c5c97 rlc: replace int with uint16_t
The i value will only count forward and is limited to 11 bit. The integer is also
converted when returning to uint16_t

Change-Id: Ib8a9081bbcb8b4344498254c58941002d17f9381
2019-06-24 13:48:09 +00:00
Keith
1f0ca54bba Cosmetic: Osmcoom -> Osmocom
Change-Id: I02c6b2655df54ca40717ce7609013d0bc54eabdf
2019-06-24 10:59:24 +02:00
Alexander Couzens
7fe3895b46 tests/BitcompTest: fix wording in log message
The testcase is showing the compressed rbb.

Change-Id: Ie7a2b31e305dffb2776698d1bc8e80d6a435c135
2019-06-22 15:52:09 +02:00
Daniel Willmann
79c6c4db3b manuals: Update VTY documentation
Related: OS#1700
Change-Id: I4a5da9b2b1caa625754383b0f9a180e7503bc60e
2019-06-19 12:43:02 +00:00
Daniel Willmann
76791d20f5 manuals: Add script to regenerate vty/counter documentation
Change-Id: Iff570e85828c87d69a7a49a00d9459094077ca30
Related: OS#1700
2019-06-19 12:43:02 +00:00
Alexander Couzens
6d73205280 egprs_rlc_compression: fix white spaces
Change-Id: I43a5acc2dda4ba567ada9846880e31c85bc98394
2019-06-17 02:04:09 +02:00
Alexander Couzens
b7439f28a1 encoding: use /* */ for comments instead of #if 0 #endif
Change-Id: Ifff9526b15bfda7a0f85c92bcb0d3fabab61ca32
2019-06-17 02:03:18 +02:00
Alexander Couzens
3f640773f3 encoding: correct encoding of CRBB in ACK/NACK when not byte aligned
The last bits of the CRBB (compressed receive block bitmap) was incorrect
encoded when the CRBB is not byte aligned.

Related: OS#3728
Change-Id: I7261aa71b37d7ead52992f8db462f72a3804988e
2019-06-13 21:26:12 +00:00
Alexander Couzens
5c3783b7e8 gprs_bssgp_pcu: explicit allocate & initialize bssgp_nsi instance
The instance bssgp_nsi is a global instance to be used by all
NS related functions. Previous the PCU allocated and initialized
the bssgp_nsi instance when (re-)connecting and freeing on disconnect.
The problem of the implicit initialisation is gprs_ns_vty_init(bssgp_nsi).
All vty init functions must be called before the configuration is read,
otherwise a previous vty written configuration is invalid.
Furthermore the vty modifications to the `ns` object were lost when the PCU has to
reconnect to the SGSN.

Fixes: OS#4024
Change-Id: I2aa53ea54e9352577f6280ad7b9d1d9da9f57eaf
2019-06-07 01:17:53 +02:00
Oliver Smith
a558ad4269 debian: create -doc subpackage with pdf manuals
I have verified, that the resulting debian packages build in my own OBS
namespace (see the -doc packages):
https://download.opensuse.org/repositories/home:/osmith42/Debian_9.0/all/
https://build.opensuse.org/project/show/home:osmith42

Depends: Ib7251cca9116151e473798879375cd5eb48ff3ad (osmo-ci)
Related: OS#3899
Change-Id: I9f2e7cfd93ee0b13d064c606a20378c1ea01400e
2019-05-29 12:14:18 +02:00
Alexander Couzens
eb64d43922 gprs_bssgp_pcu: make gprs_bssgp_ns_cb public
rename the function sgsn_ns_cb -> gprs_bssgp_ns_cb.
To allow writing and reading the same configuration, the pcu needs to register
all vty commands before reading the configuration. This callback
is required to register NS based vty commands

Related: OS#4024
Change-Id: I440c0df2e32fe22bf43288c00bb4aa3a0c6a3a51
2019-05-25 05:56:32 +02:00
Max
41d6b35670 Add test for MS mode and (M)CS settings
Right now set_mode() on GprsMs behaves in pretty counter-intuitive way:
* it's possible to set current DL MCS higher than max value
* EGPRS and EGPRS_GMSK have the same max DL MCS
* setting EGPRS* mode drops current/max MCS values to unknown

Let's capture this in a unit-test before attempting any further
modifications.

Change-Id: Ibf917f4b49d927a21cbd467775806fa6ea06a6a6
2019-04-11 07:29:53 +00:00
Rafael Diniz
0e6e45a65f Fix help message formatting of osmo-pcu.
Change-Id: If4ecf9be5a0739bb54aedb077eda51ad091b4c3f
2019-04-08 19:28:02 +00:00
Max
34513feff9 cosmetic: use const pointer for bts_data
It's used several time for logging so let's call it once to make code
easier to follow.

Change-Id: Icfd9e5603a5d8701f487f17e9c0335d458e9e80b
2019-04-08 07:36:11 +00:00
Max
902e3e58db Update MCS selection for retransmission
In 3GPP TS 44.060 the selection of MCS for retransmissions is defined as
separate tables (8.1.1.1 and 8.1.1.2) depending on the value of
resegmentation bit (which is opposite to the way EGPRS_ARQ are defined
in the source code). Let's follow the same idea and explicitly check for
resegmentation bit value and use separate tables. This also makes it
easier to add proper support for special cases (MCS-6-9 and MCS-5-7) and
padding in future independently for different ARQ types. The code is
also moved to c to avoid unnecessary conversions to and from cpp class.

Change-Id: Ia73baeefee7a58834f0fc50e3b8bf8d5e3eb7815
2019-04-08 07:35:19 +00:00
Max
12a0987b36 vty: add commands to show TBF of a certain kind
Add vty commands to show only TBFs allocated via PACCH or CCCH.

Change-Id: I80f8df4fe663a0346f4289a4220b761e39726312
Related: OS#1759
2019-04-08 07:35:00 +00:00
Max
0e6ac799f7 TS alloc: expand tests log
* restructure code for easier reading
* use consistent formatting for output
* log essential allocation parameters on failure

Change-Id: I4b78951a79ddbc0745b39d091080a4e0e247d3c5
Related: OS#2282
2019-03-28 08:58:41 +00:00
Daniel Willmann
8ab35b14e9 jenkins.sh: Add oc2g build support
Related: OS#3749, SYS#4524
Change-Id: I014e5e59bc5e904a616ddf50ebfb8247f0d428cf
2019-03-27 14:26:22 +01:00
Daniel Willmann
32518cce5a oc2g: Change log type (Litecell15->Oc2g)
Change-Id: I95ced5da1c89dae5a16963b10b005747277f320b
2019-03-27 14:23:17 +01:00
Daniel Willmann
f57dccbbfb oc2g: Remove custom alarms
Don't try to send custom alarms that weren't included in upstream.

Change-Id: I51de826aa732a3875d75396b46b7d2821ef7417c
2019-03-27 14:21:15 +01:00
Minh-Quang Nguyen
ba66ea4406 OC-2G: Always use positive TA information provided in PH-RA-IND
From-Commit: 960aa3d1b0b1
From-Remote: https://gitlab.com/nrw_noa/osmo-pcu
Change-Id: Ia526f712b95eb7bba99252f2a348d9fb5d2f3838
2019-03-27 14:20:21 +01:00
Minh-Quang Nguyen
2ba608415b OC-2G: Fix TA adjustment
Problem:
 TA provided from L1 PH-DATA-IND is a relative amount of TA adjustment to actual TA
 being used for given TBF. The current TA update algorithm in PCU simply applies the relative
 amount of TA to given TBF but does not take into account of current TA.
 As a result, the PCU will request wrong TA jump for given TBF if the MS is moving away from
 BTS more than 2 km.

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

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

From-Commit: 139ad3f42193
From-Remote: https://gitlab.com/nrw_noa/osmo-pcu
Change-Id: I7665586dd5722bbe04632ee5673d3033bc082324
2019-03-27 14:19:40 +01:00
Minh-Quang Nguyen
4a1796fe76 OC-2G: Fix missing header
From-Commit: a9eefb54c62a
From-Remote: https://gitlab.com/nrw_noa/osmo-pcu
Change-Id: Ida0592c9da74588a57d9d2d5be40fcf79edcb596
2019-03-27 14:18:30 +01:00
Jean-Francois Dionne
c1e44908fe Initial commit for OC-2G support.
From-Commit: b77fd00608dd
From-Remote: https://gitlab.com/nrw_noa/osmo-pcu
Change-Id: I7cd89a549c9463e81893ca7dd925299f728e4453
2019-03-27 14:16:41 +01:00
Max
3fa235fe01 Update IA Rest Octets encoding
Write initial bits of 3GPP TS 44.018 §10.5.2.16 IA Rest Octets the same
way as  write_ia_rest_*() routines do.

This should also fix the issue addressed in
I75dd5bebc74eea85edf9582607c774d0bba0d2a6 initially by properly encoding
L/H bits.

Change-Id: I7ed5270bf95c3f6e9e026ff447eef8539f6f0314
2019-03-27 12:32:04 +00:00
Max
d4a39291e0 TBF-DL: cosmetic update for helper routines
* use enum values where appropriate
* reformat to proper code style to improve readability

Change-Id: If1d2bc69b0d43fc520e579457007704b7975117e
2019-03-27 12:32:04 +00:00
Max
fb59a93425 TBF: update MCS counters
* use enum CodingScheme directly instead of converting it to class and
  back
* drop useless mode check
* log errorneous update attempt

Change-Id: I763136c2f356d63aa3d28d09c57fd5faf5336258
2019-03-27 12:32:04 +00:00
Max
e742cc0997 Use Timing Advance Index in UL assignments
Write TAI (if available) when generating Rest Octets for UL
Assignment. This should not affect actual PCU behavior because TAI is
not yet supported by upper layers but we have to adjust corresponding
tests anyway.

That's updated version of reverted commit.

Change-Id: I69407793bdb863be5fc42adadf75842d22f27335
Related: OS#3014
2019-03-27 12:32:04 +00:00
Max
3eb47363ad Rewrite Packet Uplink IA Rest Octets for SBA
Use bitvec_set_*() directly without external write pointer tracking to
simplify the code. This is part of IA Rest Octets (3GPP TS 44.018
§10.5.2.16) which is the last part of the message so it should not
interfere with the rest of encoding functions.

The difference in the expected test output is due to proper handling of
TAI which should not be transmitted for SBA according to the Note in
Table 10.5.2.16.1 in 3GPP TS 44.018.

The change was manually tested against real mobile phone using options
'gprs mode gprs' in osmo-bsc.cfg and 'two-phase-access' in osmo-pcu.cfg
to make sure appropriate code path is actually triggered.

That's partially based on reverted commit 93d947f5e8.

Change-Id: I97d53c27c1ca9e032d431b3aa7f915027d63ddc0
Related: OS#3014
2019-03-27 12:32:04 +00:00
Max
0367ddd62b Rewrite Packet Uplink IA Rest Octets for MBA
Use bitvec_set_*() directly without external write pointer tracking to
simplify the code. This is part of IA Rest Octets (3GPP TS 44.018
§10.5.2.16) which is the last part of the message so it should not
interfere with the rest of encoding functions.

That's partially based on reverted commit 93d947f5e8.

Change-Id: Ibe294b26ac374b9264a734db9663cacc105a4474
Related: OS#3014
2019-03-27 12:32:04 +00:00
Max
6e96dd4665 Fix Channel Coding Command for MCS
Previously result of ".to_num() - 1" was used without any checks which
means that in case of to_num() returning zero we would effectively try
to encode (uint8_t)(-1).

Let's fix this by using proper mcs_chan_code() function which returns
Channel Coding Command for MCS without the need to further correct it
and adjust expected tests output accordingly.

Change-Id: I868062a81fffe6714a811c032215f25a79259905
2019-03-27 12:32:04 +00:00
Max
898dddb1d1 MCS: add Channel Coding Command encoder
Add function to encode MCS value as proper EDGE or GPRS Channel Coding
value according to 3GPP TS 44.060 and corresponding helpers.

Use it for everything except IA Rest Octet encoding which is done in a
follow-up patches to make sure that we distinguish between
encoding-related changes to test output and unrelated changes.

Change-Id: I127fb29f5aaf77a7f6c4c565dfeb3b711af9845d
2019-03-27 12:32:04 +00:00
Harald Welte
48b1e7a86f gprs_debug: Use named initializers and explicit array indicies
This is a much safe way, it allows for modifications of the debug
subsystem enum member values without breakage.  Also, the syntax
introduced here is what we do in all other Osmocom CNI projects.

Change-Id: I2be88586ca44b0b8361f96cf3c034c8459244c2c
2019-03-27 07:39:02 +00:00
Harald Welte
3447c4a8a4 Forward GPRS SUSPEND REQ from BTS to SGSN using BSSGP
As specified in 3GPP TS 03.60 Section 16.2.1 and 44.018 Section 3.4.15,
a Class B MS is sending a "RR GPRS SUSPEND REQ" via a DCCH to the BTS if
it wants to suspend GPRS services.  As of
Change-Id I3c1af662c8f0d3d22da200638480f6ef05c3ed1f, OsmoBTS forwards
this via the PCU socket, so we need to pick it up and send it via BSSGP
to the SGSN.

Change-Id: I7b4beb413a6f974373a404b5a11c44d86ba695d3
Closes: OS#2249
2019-03-27 07:39:02 +00:00
Harald Welte
1473b377c2 pcu_l1_if: Fix erroneous endian-swapping of the CellID
In Change-Id I787fed84a7b613158a5618dd5cffafe4e4927234 in February 2018
we accidentially introduced a change that would erroneously swap
the endianness of the CellID on the way between PCUIF socket and
BSGSP.  This meant that all OsmoPCU based BTSs would report the
wrong CellId to the SGSN.

Closes: OS#3854
Change-Id: I2f6cc930c5dbf8dac386b24b0756df2efe8199e4
2019-03-27 07:39:02 +00:00
Max
8a8e0fb267 MCS: add mcs_is_*() helpers
In preparation for Channel Coding Command encoder in follow-up patches
let's add necessary helpers. Those are similar to previously used
helpers from GprsCodingScheme class but without CamelCase and with less
typo chances between Gprs and Egprs cases.

Change-Id: I6699cbc8d7ae766fa4d2b3d37e5f9ff1cf158b7e
2019-03-26 11:19:30 +01:00
Daniel Willmann
fb3fd09353 Include pdch.h in bts.h even if we're not compiling C++
bts.h needs pdch.h whether we're compiling C or C++ code so move it out
of the #ifdef.

make[1]: Entering directory '/home/daniel/scm/osmo/oc2g/osmo-pcu-oc2g/src'
  CC       osmo-bts-oc2g/oc2g_l1_if.o
In file included from osmo-bts-oc2g/oc2g_l1_if.c:39:
./bts.h:74:26: error: array type has incomplete element type ‘struct gprs_rlcmac_pdch’
  struct gprs_rlcmac_pdch pdch[8];
                          ^~~~

Change-Id: Ib39e4424f73c677b34f921917440f211e400e14f
2019-03-26 09:29:17 +00:00
Max
a4de02db5d MCS: move Mode enum outside of class definition
Move Mode (EDGE/GPRS) definition and related functions outside of
GprsCodingScheme class. This allows us to use standard libosmocore
value_string functions.

Change-Id: I3baaac7f1ca3f5b88917a23c1679d63847455f47
2019-03-24 18:54:52 +01:00
Max
02fbfc15c7 Fix TA index encoder
The TAI is described as { 0 | 1 < TIMING_ADVANCE_INDEX : bit (4) > } in
3GPP TS 44.018 §10.5.2.16.1 so it should be encoded with if-else.

Change-Id: I54482790e1cf3cb13a635a99a481250576deabaf
2019-03-19 18:27:49 +01:00
Max
a0353547b1 TBF-DL: log MCS as string
Log MCS name instead of numeric value.

Change-Id: I3e1925a010a6def5fd14da63b73e0b75feddfafc
2019-03-19 18:27:49 +01:00
Max
136ebccc5e MCS: use value_string for conversion
Change-Id: I212ebb892ab162821633974d5a6c7e315d308370
2019-03-19 18:27:49 +01:00
Max
51754b6f35 MCS: move HeaderType enum outside of class definition
Move functions which compute number of blocks or bits depending on
header type and corresponding enum outside of GprsCodingScheme
class. This will allows us to use standard libosmocore value_sting
functions in upcoming patches for IA Rest Octet encoding/decoding.

Change-Id: Id0873f85e1f16a72e17e7fbc4ad76b194917067f
2019-03-19 18:27:06 +01:00
Max
d5ffeb5e63 Explicitly clean up BTS singleton
Add method to explicitly cleanup BTS singleton similar to GprsMsStorage
class and use it from main(). The destructor becomes trivial wrapper
around cleanup() method.

This prevents annoying SIGABRT on exit of OsmoPCU caused by
rate_ctr_group_free() being called after talloc_free() which removes the
context in which counter group is allocated.

Change-Id: I796d56a7de3f3a1f9d59708995c8e3e9b05a2747
2019-03-19 15:42:10 +00:00
Max
f4d3973688 MS store: move test helper to unit test
It's confusing to have test-specific helper with the same name as tested
function directly inside the GprsMsStorage class. Let's convert it into
static function and move to the unit test.

Change-Id: Ia2a5b90779051af894fe15d957c1d26f0a142f33
2019-03-19 15:05:51 +00:00
Oliver Smith
0fb91b736c tests: use -no-install libtool flag to avoid ./lt-* scripts
This ensures that the rpath of the generated binaries is set to use only
the just-compiled so-files and not any system-wide installed libraries
while avoiding the ugly shell script wrapper.

Change-Id: I2915f0de87598f9f23efc2b9a8f4a6bdf4966efb
2019-03-19 13:46:08 +01:00
Max
a3ff316c5f Use unique NSEI/BVCI/NSVCI in TBF tests
This works around the issue with colliding rate counter group index
highlighted by 86e35e4887 by using
different NSEI/BVCI/NSVCI parameters for different TBF tests.

Change-Id: Id7d2d1a48308a6b1fb17768aee0e79adbdee56ee
Related: OS#3827
2019-03-14 09:30:20 +00:00
Max
ed6e4acdf6 Debian: bump copyright year
Change-Id: If61a510fbbcd549dc7bdf6e38643a00d242be875
2019-03-13 17:12:57 +01:00
Max
0620656288 MCS: remove unused function
Change-Id: I32ab5ac36a0db90f2bea670b7684784b83a90b6b
2019-03-13 17:11:01 +01:00
Max
e43ef21d35 Make get_retx_mcs() into regular function
Moving from header-defined inline function allows us to hide
egprs_mcs_retx_tbl definition and simplify further changes.

Change-Id: I95258d1558a3b918ae83f1a69e7c3de2b97e5627
2019-03-12 15:35:40 +01:00
Max
bea2edbc46 MCS: move Coding Scheme enum outside of class definition
Move generic MCS enum to C header file to simplify further modifications
to GprsCodingScheme class in follow-up patches. This also allows us
to use standard libosmocore value_sting functions in upcoming patches
for IA Rest Octet encoding/decoding.

Related: OS#3014
Change-Id: I993b49d9a82b8c7ad677d52d11003794aeabe117
2019-03-12 15:35:40 +01:00
JF Dionne
1beed38b54 encoding: Fixes TMSI vs MI bit selection in repeated page info
Change-Id: Iddb00b9133f523f4ba09c8f1fc5694e62dc46fbf
2019-03-11 13:36:44 +00:00
Max
fa3085b45e Log (M)CS UL update errors
Previously some of the errors in update_cs_ul() call were silently
ignored. Let's log all those as errors with appropriate message.

Note: test output needs updating because we do not (yet) set proper meas
struct in TBF tests. That's likely wrong but it's better to update tests
in a separate commit.

Change-Id: I4084fb281dd9dad04a2a3a68cac2a8f7b462548e
2019-03-07 17:05:19 +00:00
Max
86e35e4887 Enable LGLOBAL logging for TBF tests
This exposes the bug in BSSGP rate counter group allocation in the
current tests version which should be fixed in a separate commit.

Change-Id: I6317eccfb1408c5c9d07110a8b059f5ceb5d2afc
2019-03-07 12:20:42 +01:00
Max
8119ecd2db Tighten lqual table limits check
Previously MAX_GPRS_CS was used for both EDGE and GPRS which means that
we waste extra memory in GPRS case. It also leads to misleading
name. Let's fix this by introducing separate definitions for GPRS and
EDGE cases and use them as appropriate in limit checks.

Change-Id: I3ae1ee64ec8e80247b8fe669cc79505b4dadf58f
2019-03-06 20:52:15 +00:00
Max
d3a0d91a38 Use msgb_eq_data_print() in tests
This allows to see exact byte which differs with expected output in case
of test failure.

Change-Id: If1285649b27843d68dfaa6f6dd3b80deee9aa148
2019-03-06 20:51:57 +00:00
Max
01f9bc7bf1 EDGE tests: remove no-op check
The headerTypeControl() function always return constant and is only used
inside OSMO_ASSERT() which compares it to the very same constant which
makes it no-op. Let's remove this clutter.

Change-Id: Ie0f81fe05a2b3f432de7d1f3446e8115d7524ff4
2019-03-05 12:10:52 +01:00
Max
9feaddc390 MCS: remove dead code
As a preparation for (M)CS fixes in follow-up patches, remove unused
operators from GprsCodingScheme class.

Change-Id: Ieef3b095a6732300e5efa395b989843112b9ca78
2019-03-04 16:25:12 +00:00
Max
360e021d0a EDGE tests: reduce code duplication
* move duplicated code into helper function
* use proper parameter types

Change-Id: I8a9528032629e5df629996da5cd8b808efede017
2019-02-26 23:01:02 +00:00
Max
807dde070f MCS: internalize 'family' parameter
There's no need to expose it in header file as it's only used internally
for consistency checks.

Change-Id: Ic705615cd2a8ca077efef9ea62a2a676f70b4aed
2019-02-26 19:59:46 +01:00
Harald Welte
57d3515abd Optionally Use the NS Sub-Network-Service (SNS) on Gb
This change add support for the recently-introduced GPRS Gb interface
auto-configuration using the NS IP Sub-Network Service (SNS) procedures.

It requires a Change-Id I84786c3b43a8ae34ef3b3ba84b33c90042d234ea of
libosmocore.

Related: OS#3372
Depends: I84786c3b43a8ae34ef3b3ba84b33c90042d234ea (libosmcore)
Change-Id: I256b40ac592d3b6e75dd581bf7b9512f69b11e83
2019-02-26 13:23:24 +01:00
Max
2a47c73217 Rewrite EGPRS Packet Uplink IA Rest Octets for SBA
Use bitvec_set_*() directly without external write pointer tracking to
simplify the code. This is part of IA Rest Octets (3GPP TS 44.018
§10.5.2.16) which is the last part of the message so it should not
interfere with the rest of encoding functions.

That's partially based on reverted commit 529ce88545.

Change-Id: I143b3dd02aa54b9ce206d9e780a5554f6d9fd118
Related: OS#3014
2019-02-26 11:20:15 +00:00
Harald Welte
b26854c276 Mark gprs_ns_reconnect() as static (not used outside of C file)
Change-Id: I95138adedacdc2d953284cff57f79ecb33616f0f
2019-02-26 11:11:51 +00:00
Max
bd5647ee92 Rewrite EGPRS Packet Uplink IA Rest Octets for MBA
Use bitvec_set_*() directly without external write pointer tracking to
simplify the code. This is part of IA Rest Octets (3GPP TS 44.018
§10.5.2.16) which is the last part of the message so it should not
interfere with the rest of encoding functions.

That's partially based on reverted commit 529ce88545.

Change-Id: I19cc4226e7e831e7d7f70212b2078f5589a87ff0
Related: OS#3014
2019-02-19 18:58:04 +01:00
Max
23c0e018e4 Rewrite Packet Downlink Assignment
Use bitvec_set_*() directly without external write pointer tracking to
simplify the code. This is part of IA Rest Octets (3GPP TS 44.018
§10.5.2.16) which is the last part of the message so it should not
interfere with the rest of encoding functions.

That's updated version of commit with the same topic reverted earlier.

Change-Id: Ie180733d2584ebb16fb80b84526d0dbc70e3d441
Related: OS#3014
2019-02-19 18:58:04 +01:00
Max
0160a29b6c Restructure IA Rest Octets encoders
In preparation for upcoming patches with 11 bit RACH and TA support,
let's restructure existing encoders to simplify further modifications:

* move consistency checks to top-level Imm. Ass. encoder
* use consistent formatting
* constify pointers where appropriate
* split SBA and MBA encoders into separate functions

Those changes also make it obvious which parameters are necessary for
Rest Octets in each specific case (DL, UL-SBA, UL-MBA, UL-SBA-EGPRS,
UL-MBA-EGPRS).

There're no functional code changes so there's no need to adjust tests.

Change-Id: I0ad1bc786c3a8055ea9666f64ae82c512bd01603
Related: OS#1548
2019-02-19 18:58:04 +01:00
Max
fc8afc2f33 Clarify write_immediate_assignment() signature
* remove unused variable
* use bool for boolean types
* add clarification comments

Change-Id: I363445063e2d873d9194b2a5924b9e59b8b7ea53
2019-02-19 18:58:04 +01:00
Max
81b58ccd94 Add encoding tests for Immediate Assignment
Change-Id: I63f4654b23c7c4f063f6b3254d77157fac798586
2019-02-19 18:58:04 +01:00
Max
7426c5f957 Add define for dummy burst string
Change-Id: I464920b3d6d47bb1c797a4ce06230f005a2e06a0
2019-02-19 18:58:04 +01:00
Max
0c55bf19a5 Move C include to proper place
Change-Id: Id58d1820b94d54ce73ed40edb7747ef975890a7b
2019-02-19 18:58:04 +01:00
Max
1f2e69fd35 Don't install pcuif_proto.h header
Both OsmoBTS and OsmoBSC use their own copies of this header nowadays so
we can simplify our installation slightly by making it local only.

Change-Id: I4a87395d4ab7212fe2fc055dae0a737e10d20c69
2019-02-14 16:35:36 +01:00
Rafael Diniz
f0af1b051a Added support for daemonize to osmo-pcu.
Change-Id: Ia889544e0a350b6bab55da4e4201a617e0241ea2
2019-01-29 12:32:58 -02:00
Harald Welte
99278b1050 Bump version: 0.5.1.38-5b52 → 0.6.0
Change-Id: Ibd7ac106f1e08319a592cf3246fc9fb6e298d226
2019-01-21 19:03:52 +01:00
Oliver Smith
5b521891ba contrib: fix makedistcheck with disabled systemd
EXTRA_DIST files need to be distributed, no matter if the systemd option
is configured or not.

Change-Id: I356aa59ab152ace89b247823a2c0517814a69ecb
2018-12-06 13:54:54 +01:00
Oliver Smith
64bc9400d4 contrib/jenkins.sh: build and publish manuals
Add new environment variables WITH_MANUALS and PUBLISH to control if
the manuals should be built and uploaded. Describe all environment vars
on top of the file.

When WITH_MANUALS is set, install osmo-gsm-manuals like any other
dependency and add --enable-manuals to the configure flags (for "make"
and "make distcheck"). Add the bin subdir of the installed files to
PATH, so osmo-gsm-manuals-check-depends can be used by ./configure.

Related: OS#3385
Change-Id: Ia5b112fc1663b78800d3c2c4ff2a0771cf5af11b
2018-12-05 13:47:26 +01:00
Oliver Smith
61469d1d86 Fix DISTCHECK_CONFIGURE_FLAGS override
Set AM_DISTCHECK_CONFIGURE_FLAGS in Makefile.am instead of
DISTCHECK_CONFIGURE_FLAGS. This is the recommended way from the
automake manual, as otherwise the flag can't be changed by the user
anymore.

Related: OS#3718
Change-Id: I10d1eef9838c0b843a3a4103b7b03e8e9457b69e
2018-12-04 15:31:28 +01:00
Oliver Smith
47aab58ec3 build manuals moved here from osmo-gsm-manuals.git
Moved to doc/manuals/, with full commit history, in preceding merge commit.
Now incorporate in the build system.

Build with:

$ autoreconf -fi
$ ./configure --enable-manuals
$ make

Shared files from osmo-gsm-manuals.git are found automatically if
- the repository is checked out in ../osmo-gsm-manuals; or
- if it osmo-gsm-manuals was installed with "make install"; or
- OSMO_GSM_MANUALS_DIR is set.

Related: OS#3385
Change-Id: I7270652de393a98748c0cdc51e626c17ab8f44c2
2018-11-27 18:30:56 +01:00
Neels Hofmeyr
dfb08d31fe Merge history from osmo-gsm-manuals.git
Change-Id: I33ce528bb58a8a730a15cc8a7a74852a14f42921
2018-11-27 18:30:39 +01:00
Daniel Willmann
96481c8d3f Change OpenBSC mentions to OsmoBSC where applicable
Change-Id: I4cc6874302b6089a54d44b09f08660a25e46d4dc
2018-11-27 18:30:23 +01:00
Harald Welte
2eeddee132 vty-ref: Update URI of docbook 5.0 schema
... to match the /etc/xml/catalog file on debian (no "www" in hostname)

Change-Id: Id9f3579c7f2bc3af13fe30b5268f249b6f59ed0d
2018-11-27 18:30:23 +01:00
Alexander Couzens
57d91ea785 OsmoPCU: add rate counter documentation
Change-Id: Ieb4e1dab415a70ded5c65c21752dca497856e96f
2018-11-27 18:30:23 +01:00
Neels Hofmeyr
d2829b89c1 refactor Makefile build rules, don't use the FORCE
The initial goal was to make sure we don't have overall FORCE rules causing
unnecessary rebuilds -- annoying while writing documentation. As I looked
through possible dependencies, I finally understood what's going on here.

Remove code dup and nicely sort which belongs where in build/Makefile.*.inc. In
each, describe in a top comment how to use it, and also unify how they are
used:

- Rename Makefile.inc to Makefile.docbook.inc and refactor
- Add Makefile.vty-reference.inc
- Add Makefile.common.inc

Make sure that we accurately pick up all dependencies.

Drop use of the macro called 'command', that silenced the actual command lines
invoked and replaced them with short strings: it obscures what is actually
going on and makes the Makefiles hard to read and understand.

Each manual's makefile is greatly reduced to few definitions and a Makefile
include, e.g. one for asciidoc, one for VTY reference.

Move common/bsc_vty_additions.xml to OsmoBSC/vty/libbsc_vty_additions.xml, link
from OsmoNITB. It applies only to OsmoBSC and OsmoNITB.

Add a script that combines a VTY reference file with *all* additions files
found in a manual's vty/ dir. Call this from Makefile.vty-reference.inc.

Change-Id: I9758e04162a480e28c7dc83475b514cf7fd25ec0
2018-11-27 18:30:23 +01:00
Pau Espin Pedrol
f605f3d891 Allow easily disabling GFDL references
All parts referencing GFDL can be easily disabled by removing the
'gfdl-enabled' attribute from the document.

Change-Id: I2489726ad2e90301bceadfada926e31ae0f85986
2018-11-27 18:30:23 +01:00
Philipp
452b533114 configuration: fixing typos
configuration.adoc has some minor typos in it, this commit fixes
that.

Change-Id: Id84238b89e5deeac99c043b79b26c7e7b8b8534b
2018-11-27 18:30:23 +01:00
Neels Hofmeyr
5c8f1ccff0 fix 'make clean': shell glob, ignore failure
Unfortunately a glob like osmo-x__*.{svg,png} doesn't work, so have the
suffixes in separate globs.

Add dashes to indicate that failure should be ignored.

Change-Id: I6bc4d9ea72b43a573acbc860c23397f748de2c7b
2018-11-27 18:30:23 +01:00
Neels Hofmeyr
6bc5c2aac6 add 'make check' target
Generate *.check files from asciidoc output and grep for WARNINGs.
Add *.check files to gitignore and to 'make clean'.

Change-Id: Ibccc83a3415930a528f2e8e4e4dda3b81c6d0b64
2018-11-27 18:30:23 +01:00
Neels Hofmeyr
5828b60af5 make clean: also remove generated image files
Change-Id: I80798e79b4ccee64f26f58f9754de02b2958e33e
2018-11-27 18:30:23 +01:00
Jonathan Brielmaier
58721eb350 fix various typos across all manuals 2018-11-27 18:30:23 +01:00
Harald Welte
6fb29639df gb/NS: Clarify the language regarding the UDP port numbers / socket 2018-11-27 18:30:23 +01:00
Harald Welte
032f94b099 consistently use '3GPP TS' not sometimes 3GPP TS and sometimes TS. 2018-11-27 18:30:23 +01:00
Harald Welte
32b58e6418 gb: Some language improvements, formatting changes 2018-11-27 18:30:23 +01:00
Harald Welte
a27873f1e2 Gb: Various spelling fixes 2018-11-27 18:30:23 +01:00
Harald Welte
3a89f21540 Gb message sequence chart: Add notion of PCU unix domain socket 2018-11-27 18:30:23 +01:00
Harald Welte
d930b4d5f4 Gb message sequence chart: flip sides (SGSN should be right)
... also, re-word some of th labels for more clarity
2018-11-27 18:30:23 +01:00
Max
8f934f55d7 OsmoPCU: add MSC chart 2018-11-27 18:30:23 +01:00
Max
b1776b65b3 OsmoPCU: expand BSSGP documentation 2018-11-27 18:30:23 +01:00
Max
4e2a1e3e98 OsmoPCU: expand NS documentation
Add table with NS messages.
Add corresponding sections.
Clarify spec numbers.
2018-11-27 18:30:23 +01:00
Max
c182b98998 OsmoPCU: fix Gb documentation front page
Add date and commit refs.
Add relevant standards.
Fix email typo.
2018-11-27 18:30:23 +01:00
Harald Welte
b682cd6a51 Initial place-holder for the new Gb/IP interface documentation 2018-11-27 18:30:23 +01:00
Harald Welte
4095f31fea Add link to Asciidoc source code of manual 2018-11-27 18:30:23 +01:00
Harald Welte
4ff37feb4c initial checkin of manuals to public repo
The manuals existed in different form for several years in an internal
sysmocom repository.  However, since they had just recently been
converted from docboox-xml to asciidoc and all files have been
re-shuffled for enabling the public release, there's not much point in
keeping the history with git-filter-branch.
2018-11-27 18:30:23 +01:00
Neels Hofmeyr
b9ba5ad239 Importing history from osmo-gsm-manuals.git
Change-Id: I23320be18612dd1eb800d3b16594166f33b3d984
2018-11-27 18:30:08 +01:00
Max
4214c4ae53 deb: add missing copyright file
File is imported as-is from current .deb package

Change-Id: Ib05480c0eea91bfb55bfc7ab446ea60932096d3d
2018-11-07 14:20:27 +01:00
Harald Welte
099f4f2169 gprs_rlcmac_received_lost(): Fix regression / uninitialized now_tv
In Change-Id I7d22e7b5902c230efeae66eb20c17026a4037887 we
introduced the use of timespecsub(). Unfortuantely, we also
accidentially removed the call to osmo_clock_gettime() along
with it, leaving now_tv completely uninitialized.

Change-Id: Ieced0c62700b2fe4ab0208258183154cc701490b
Related: OS#3225
Fixes: Coverity CID#188872
2018-10-21 11:50:10 +02:00
Stefan Sperling
173d7fdbb9 check for overlong unix socket paths
In pcu_l1if_open(), use osmo_strlcpy() instead of strncpy() and check for
overflow. This catches overlong and non-NUL-terminated socket paths.

Change-Id: I825190cbb34d052b797e9fb5208884d6f5992839
Related: OS#2673
2018-09-20 18:31:36 +02:00
Pau Espin Pedrol
076122f592 Install osmo-pcu.cfg to docdir/examples
Change-Id: I42938d9abd17575c2e0ce69cc20d44a131f26b6a
2018-09-13 11:36:01 +02:00
Pau Espin Pedrol
ac5e3e222a Move examples/ to doc/examples/
Change-Id: I1e163e10f8a2e22b9ebdcb2d0f13f6ad07c84efe
2018-09-13 11:36:01 +02:00
Pau Espin Pedrol
516697e29a Install systemd services with autotools
Change-Id: Ie4c7e81495181059d1dff1c194d52d11fb72ed03
2018-09-10 16:09:54 +02:00
Pau Espin Pedrol
3e9c071aa6 configure.ac: Set CXXFLAGS during --enable-sanitize
For some unknown reason ld was failing to find some asan symbols until I
enabled asan too in CXXFLAGS.

Change-Id: I695314b284277674dc336b40765313a37d238d6e
2018-09-10 14:52:21 +02:00
Pau Espin Pedrol
e63c72acfb Cleanup of systemd service files
Let's use a symlink in debian/ as we do in other projects,
and merge the two service files since anyway they call the same binary.

Change-Id: Ibd82ec12cbeb73a27ca5860266587efb58be14ab
2018-09-06 15:06:22 +02:00
Harald Welte
54af2dba78 debian/rules: Don't overwrite .tarball-version
The .tarball-version file should contain the *source version* uniquely
identifying the git commit, and not the Debian package name.

With https://gerrit.osmocom.org/#/c/osmo-ci/+/10343/ there is a correct
.tarball-version file in the .tar.xz of the nightly source packages.

Change-Id: I093c9c1943e5f09d8f91f94af438f697a93e7127
Related: OS#3449
2018-08-06 11:18:24 +02:00
175 changed files with 536798 additions and 223218 deletions

15
.gitignore vendored
View File

@@ -20,6 +20,7 @@ install-sh
missing
*libtool
ltmain.sh
*~
core
core.*
@@ -32,6 +33,7 @@ src/osmo-pcu-remote
# tests
.dirstamp
__pycache__/
tests/atconfig
tests/package.m4
tests/*/*Test
@@ -53,3 +55,16 @@ debian/osmo-pcu/
debian/tmp/
osmo-pcu.pc
# manuals
doc/manuals/*.html
doc/manuals/*.svg
doc/manuals/*.pdf
doc/manuals/*__*.png
doc/manuals/*.check
doc/manuals/generated/
doc/manuals/osmomsc-usermanual.xml
doc/manuals/common
doc/manuals/build
contrib/osmo-pcu.spec

View File

@@ -1,9 +1,14 @@
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
SUBDIRS = include src examples tests
EXTRA_DIST = osmoappdesc.py README.md
SUBDIRS = include src doc tests contrib
EXTRA_DIST = \
README.md \
contrib/osmo-pcu.spec.in \
debian \
osmoappdesc.py \
$(NULL)
AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
@RELMAKE@
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = osmo-pcu.pc

View File

@@ -82,3 +82,4 @@ Current limitations
* No half-duplex class support (only semi-duplex)
* No TA loop
* No power loop
* Multi-BTS support not tested

8
TODO
View File

@@ -1,13 +1,5 @@
* 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.

9
TODO-RELEASE Normal file
View File

@@ -0,0 +1,9 @@
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
# In short:
# LIBVERSION=c:r:a
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
# If any interfaces have been added since the last public release: c:r:a + 1.
# If any interfaces have been removed or changed since the last public release: c:r:0.
#library what description / commit summary line

View File

@@ -9,6 +9,9 @@ AC_CONFIG_AUX_DIR([.])
AM_INIT_AUTOMAKE([dist-bzip2])
AC_CONFIG_TESTDIR(tests)
CXXFLAGS="$CXXFLAGS -std=gnu++03"
CFLAGS="$CFLAGS -std=gnu11"
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -23,6 +26,11 @@ AC_PROG_CXX
AC_PROG_INSTALL
LT_INIT
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
AS_CASE(["$LD"],[*clang*],
[AS_CASE(["${host_os}"],
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
@@ -41,6 +49,7 @@ AC_ARG_ENABLE(sanitize,
if test x"$sanitize" = x"yes"
then
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
CXXFLAGS="$CXXFLAGS -fsanitize=address -fsanitize=undefined"
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
LDFLAGS="$LDFLAGS -fsanitize=address -fsanitize=undefined"
fi
@@ -73,10 +82,11 @@ then
fi
dnl checks for libraries
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.12.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.12.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.12.0)
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.12.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 1.6.0)
AC_MSG_CHECKING([whether to enable direct DSP access for PDCH of sysmocom-bts])
AC_ARG_ENABLE(sysmocom-dsp,
@@ -138,6 +148,25 @@ if test "$enable_litecell15" = "yes"; then
CPPFLAGS="$oldCPPFLAGS"
fi
AC_MSG_CHECKING([whether to enable direct PHY access for PDCH of NuRAN Wireless OC-2G BTS])
AC_ARG_ENABLE(oc2gbts-phy,
AC_HELP_STRING([--enable-oc2gbts-phy],
[enable code for OC-2G PHY [default=no]]),
[enable_oc2gbts_phy="$enableval"],[enable_oc2gbts_phy="no"])
AC_ARG_WITH([oc2g], [AS_HELP_STRING([--with-oc2g=INCLUDE_DIR], [Location of the OC-2G API header files])],
[oc2g_incdir="$withval"],[oc2g_incdir="$incdir"])
AC_SUBST([OC2G_INCDIR], $oc2g_incdir)
AC_MSG_RESULT([$enable_oc2gbts_phy])
AM_CONDITIONAL(ENABLE_OC2GBTS_PHY, test "x$enable_oc2gbts_phy" = "xyes")
if test "$enable_oc2g" = "yes"; then
oldCPPFLAGS=$CPPFLAGS
CPPFLAGS="$CPPFLAGS -I$OC2G_INCDIR -I$srcdir/include $LIBOSMOCORE_CFLAGS"
AC_CHECK_HEADER([nrw/oc2g/oc2g.h],[],
[AC_MSG_ERROR([nrw/oc2g/oc2g.h can not be found in $oc2g_incdir])],
[#include <nrw/oc2g/oc2g.h>])
CPPFLAGS=$oldCPPFLAGS
fi
AC_ARG_ENABLE([vty_tests],
AC_HELP_STRING([--enable-vty-tests],
[Include the VTY tests in make check [default=no]]),
@@ -156,13 +185,77 @@ AM_CONDITIONAL(ENABLE_VTY_TESTS, test "x$enable_vty_tests" = "xyes")
STD_DEFINES_AND_INCLUDES="-Wall"
AC_SUBST(STD_DEFINES_AND_INCLUDES)
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
# Generate manuals
AC_ARG_ENABLE(manuals,
[AS_HELP_STRING(
[--enable-manuals],
[Generate manual PDFs [default=no]],
)],
[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"])
AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"])
AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals"
fallback])
if test x"$osmo_ac_build_manuals" = x"yes"
then
# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback)
if test -n "$OSMO_GSM_MANUALS_DIR"; then
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)"
else
OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)"
if test -n "$OSMO_GSM_MANUALS_DIR"; then
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)"
else
OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals"
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)"
fi
fi
if ! test -d "$OSMO_GSM_MANUALS_DIR"; then
AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.")
fi
# Find and run check-depends
CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh"
if ! test -x "$CHECK_DEPENDS"; then
CHECK_DEPENDS="osmo-gsm-manuals-check-depends"
fi
if ! $CHECK_DEPENDS; then
AC_MSG_ERROR("missing dependencies for --enable-manuals")
fi
# Put in Makefile with absolute path
OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")"
AC_SUBST([OSMO_GSM_MANUALS_DIR])
fi
# https://www.freedesktop.org/software/systemd/man/daemon.html
AC_ARG_WITH([systemdsystemunitdir],
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
[with_systemdsystemunitdir=auto])
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
AS_IF([test "x$def_systemdsystemunitdir" = "x"],
[AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
[AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
with_systemdsystemunitdir=no],
[with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
[AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"])
AC_MSG_RESULT([LDFLAGS="$LDFLAGS"])
AC_OUTPUT(
osmo-pcu.pc
include/Makefile
src/Makefile
examples/Makefile
doc/Makefile
doc/examples/Makefile
tests/Makefile
doc/manuals/Makefile
contrib/Makefile
contrib/systemd/Makefile
contrib/osmo-pcu.spec
Makefile)

1
contrib/Makefile.am Normal file
View File

@@ -0,0 +1 @@
SUBDIRS = systemd

View File

@@ -1,5 +1,12 @@
#!/bin/sh
# jenkins build helper script for osmo-pcu. This is how we build on jenkins.osmocom.org
#
# environment variables:
# * with_dsp: the DSP to configure ("sysmo", "lc15" or "none")
# * with_vty: enable VTY tests if set to "True"
# * WITH_MANUALS: build manual PDFs if set to "1"
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
#
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
@@ -41,6 +48,12 @@ elif [ "$with_dsp" = lc15 ]; then
osmo-layer1-headers.sh lc15 "$FIRMWARE_VERSION"
cd "$base"
elif [ "$with_dsp" = oc2g ]; then
PCU_CONFIG="$PCU_CONFIG --enable-oc2gbts-phy --with-oc2g=$deps/layer1-headers/inc"
cd "$deps"
osmo-layer1-headers.sh oc2g "$FIRMWARE_VERSION"
cd "$base"
elif [ -z "$with_dsp" -o "$with_dsp" = none ]; then
echo "Direct DSP access disabled, sanitizer enabled"
PCU_CONFIG="$PCU_CONFIG --enable-werror --enable-sanitize"
@@ -65,6 +78,11 @@ osmo-build-dep.sh libosmocore "" --disable-doxygen
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib"
export PATH="$inst/bin:$PATH"
if [ "$WITH_MANUALS" = "1" ]; then
PCU_CONFIG="$PCU_CONFIG --enable-manuals"
fi
set +x
echo
@@ -77,8 +95,13 @@ set -x
autoreconf --install --force
./configure $PCU_CONFIG
$MAKE $PARALLEL_MAKE
DISTCHECK_CONFIGURE_FLAGS="$PCU_CONFIG" AM_DISTCHECK_CONFIGURE_FLAGS="$PCU_CONFIG" \
$MAKE distcheck \
DISTCHECK_CONFIGURE_FLAGS="$PCU_CONFIG" \
$MAKE $PARALLEL_MAKE distcheck \
|| cat-testlogs.sh
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
make -C "$base/doc/manuals" publish
fi
$MAKE $PARALLEL_MAKE maintainer-clean
osmo-clean-workspace.sh

83
contrib/osmo-pcu.spec.in Normal file
View File

@@ -0,0 +1,83 @@
#
# spec file for package osmo-pcu
#
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
Name: osmo-pcu
Version: @VERSION@
Release: 0
Summary: Osmocom GPRS Packet Control Unit (PCU)
License: GPL-2.0-only
Group: Productivity/Telephony/Servers
URL: https://osmocom.org/projects/osmopcu
Source: %{name}-%{version}.tar.xz
BuildRequires: autoconf
BuildRequires: automake
BuildRequires: gcc-c++
BuildRequires: libtool
BuildRequires: pkgconfig >= 0.20
%if 0%{?suse_version}
BuildRequires: systemd-rpm-macros
%endif
BuildRequires: pkgconfig(libosmocore) >= 1.6.0
BuildRequires: pkgconfig(libosmogb) >= 1.6.0
BuildRequires: pkgconfig(libosmogsm) >= 1.6.0
BuildRequires: pkgconfig(libosmovty) >= 1.6.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.6.0
%{?systemd_requires}
%description
Osmocom PCU code (RLC/MAC/PCU) for OpenBTS and OsmoBTS.
%prep
%setup -q
%build
echo "%{version}" >.tarball-version
autoreconf -fi
%configure \
--enable-shared \
--disable-static \
--docdir=%{_docdir}/%{name} \
--with-systemdsystemunitdir=%{_unitdir}
make %{?_smp_mflags}
%install
%make_install
%if 0%{?suse_version}
%preun
%service_del_preun %{name}.service
%postun
%service_del_postun %{name}.service
%pre
%service_add_pre %{name}.service
%post
%service_add_post %{name}.service
%endif
%check
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%files
%license COPYING
%doc README.md
%doc %{_docdir}/%{name}/examples
%{_bindir}/osmo-pcu
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-pcu.cfg
%{_unitdir}/%{name}.service
%changelog

View File

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

View File

@@ -0,0 +1,6 @@
EXTRA_DIST = osmo-pcu.service
if HAVE_SYSTEMD
systemdsystemunit_DATA = \
osmo-pcu.service
endif

View File

@@ -1,5 +1,5 @@
[Unit]
Description=sysmocom sysmoPCU
Description=Osmocom osmo-pcu
[Service]
Type=simple
@@ -14,4 +14,3 @@ CPUSchedulingPriority=1
[Install]
WantedBy=multi-user.target
Alias=sysmopcu.service

991
debian/changelog vendored
View File

@@ -1,3 +1,994 @@
osmo-pcu (1.0.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* Support uplink multi-slot allocations
* tbf: Log timeslot allocation failure
* bts: Count TBF TS allocation failure
* pdch: Standarize and improve logging
* tbf: Improve logging when TBF being allocated or no TBF avail
* Remove uneeded ms param from alloc_algorithm_func_t func
* bts: Add new stats to detect TBF allocation failure reasons
* llc: use memset to fill llc dummy frame padding
* tbd_dl: Don't re-initialize class field twice
* tbf: log keep_open condition status
* tbf_dl: Fix m_last_dl_drained_fn not set under some conditions
* tbf_dl: fix FBI not set upon X2031 = 0
* ms: clarify delayed MS release process related code and logging
* gprs_ms: Use standarized logging on more messages
* sched: sched_select_ctrl_msg(): Clean up param list and improve logging
* sched: sched_select_downlink(): Clean up param list and improve logging
* TODO-RELEASE: document requirement of master libosmocore
* tbf: Log N310* counter increments
* pdch: Silently ignore DATA.ind with len=0
* direct_phy: Support submitting DATA.ind with len=0 to upper layers
* pcu_utils.h: Fix trailing whitespace
* Track TDMA clock with DATA.ind instead of TIME.ind
* Introduce init() APIs for PDCH and TRX objects
* tests: rlcmac: Fix C vs C++ linkage of extern symbol
* pdch.h: Drop uneeded include bts.h
* Improve DATA.ind logging
* Improve logging in DATA.req and ACT.req
* tbf: Fix wrong variable printed in log
* pdch: Log FN when decoding UL Ctrl block
* Add new PDCH UL Controller, drop SBAllocator class
* Replace PollController with newly added PDCH UL Controller
* sched: Use new PDCH UL Controller
* bts: Detect FN jumps
* cosmetic: tests/Makefile.am: Split content into several lines
* tests: Introduce unit tests for PDCH UL Controller
* tests: ulc: Show current bug with FN wrap around
* ulc: Fix FN store order upon wrap around
* sysmo: fix wrong FN jumps in rx RA.ind
* direct_phy: Fix condition dropping rx DATA.ind payload in in
* Fix: left shift cannot be repesented in type int
* sched: Fix scheduling UL TBF not matching conditions
* sched: Simplify usf selection code
* Set matching USF if available when polling a UL TBF
* pdch: Add mising pdch_ulc_release_node in Rx Cell Change Notif
* pdch_ulc: Create helper API pdch_ulc_release_node
* Track scheduled UL blocks through USF
* Properly implement N3101
* sba: Document AGCH_START_OFFSET after some experimental tests
* pdch_ulc: Optimize rbtree FN search
* Pick unreserved UL FN when allocating an SBA
* pdch_ulc: Support picking RRBP other than N+13
* Drop unused function tbf_check()
* pdch_ulc: Store TBF poll reason
* tbf: Get rid of unneeded poll_scheduled()
* tbf: Allow multiple concurrent polls
* Remove unneeded poll_state check
* tbf: get rid of poll_state completely
* Get rid of param 'poll' with constant value
* tbf: Get rid of attribute poll_fn
* tbf: Get rid of attribute poll_ts
* RIM: Improve logging
* sba: Drop unused function find_sba_rts
* pdch: rcv_resource_request: Improve robustness
* pdch: tbf_by_tfi(): Allow returning TBFs in state RELEASING
* Stop abusing T3169
* Make use of T3142 received from BTS
* Use negative numbers for non-spec osmo-specific timers
* ul_tbf: Clean up handle_tbf_reject()
* Make WaitIndication T3172 configurable
* sched: Simplify else-if condition
* Clarify, document Assignment related timers
* doc/tbf.txt: Update and improve some information
* bts: constify arg in func bts_ms_store()
* sched: Rename func to describe its used only for RLCMAC CTRL blocks
* rim: Constify param in func
* Simplify helper function tbf_select_slot_set()
* alloc_algorithm_b: Rearrange variable initialization
* Rename function s/tbf_alloc_ul/tbf_alloc_ul_pacch/
* Split ul_tbf alloc on CCCH into new function
* Implement T3141
* tbf_ul: Use is_tlli_valid() API
* Tx ul ack/nack: Avoid sending invalid/unknown TLLI
* encoding: Use gsm48_ta_is_valid() API
* encoding: Encode TA in UL ACK/NACK if available
* sched: Clean up helper function and improve logging
* Drop existing tbf->ms() check condition
* ul_tbf: Simplify function rcv_data_block_acknowledged
* ul_tbf: Fix accessing zeroed block when checking if transfer is complete
* sched: Clean up param passing and improve logging
* pdch: Use llist_first_entry() API
* RIM: Refactor Rx path to decode stack in proper order
* Clean false positive in newer GCC version checking guard of else clause
* Use LOGPDCH macro in bts_add_paging()
* Optimize PAGING-CS PDCH set selection when target MS is known
* bts: Use ms_store when calculating set of target PDCHs for Pkt Paging Request
* tbf: Log error path in setup() failing to assign control TS
* Move TBF list from BTS to the TRX structure
* MsTest: Set up tbf talloc destructor
* tbf: Move existing tbf_state implementation to osmo_fsm
* cosmetic: Fix typo s/TIMSI/TMSI/
* gsm_rlcmac.c: Fix arg list of 2 callbacks
* csn1: Implement CSN_CALLBACK type in encoder
* bts: Fix typo in field name
* Use new stat item/ctr getter APIs
* pdch: Log pdch_ulc reason upon rx of pkt ctrl ack
* pcuif: Support receiving System Information 2
* pdch: Fix null MS access gprs_rlcmac_pdch::rcv_control_ack
* pcuif_proto.h: Add new container message
* Support proto IPAC_PROTO_EXT_PCU BSC<->PCU
* pdch: Fix heap-use-after-free in pdch->ulc
* Make gcc 11.1.0 false positivies happy
* tbf: Drop impossible paths in create_dl_ass()
* tests/tbf: Fix null pointer access if slowly stepping with gdb
* Revert "coverity: fix null deref from recent UL TBF leak fix"
* Revert "fix: handle NULL return of as_dl_tbf() and as_ul_tbf()"
* Revert "Revert "Stop abusing T3169""
* Move NULL and ASSIGN tbf_state transition to tbf_fsm
* Move FLOW tbf_state transition to tbf_fsm.
* tests: tbf: Fix dl_tbf polled for data without being in FLOW state
* Move FINISHED tbf_state transition to tbf_fsm
* Move WAIT_RELEASE tbf_state transition to tbf_fsm
* Move RELEASING tbf_state transition to tbf_fsm
* Move T3169 and T3195 to tbf_fsm
* Drop duplicate log line
* Put dl_tbf::cleanup into destructor
* Drop logging last mas report before freeing TBF
* Remove duplicate call to gprs_rlcmac_lost_rep
* Move rate_ctr free to tbf subclass destructor
* Get rid of tbf_dl:abort()
* tbf_free: Get rid of uneeded tbf_state transition
* Replace ul_ass_state with osmocom FSM
* tbf: Reimplement rlcmac_diag() and make it available from C
* tbf: Drop unuseful flag GPRS_RLCMAC_FLAG_TO_UL_ASS
* replace dl_ass_state with osmocom FSM
* tbf: Drop unuseful flag GPRS_RLCMAC_FLAG_TO_DL_ASS
* tbf: Drop unuseful flag GPRS_RLCMAC_FLAG_UL_DATA
* Move timer X2001 to tbf_fsm
* Get rid of lots of code only used by tests
* tbf: Merge handle_ack_nack() into rcvd_dl_ack()
* Fix typos in comments documenting fsm st chg macro
* tbf: Use type bool for upgrade_to_multislot
* Move timer X2002 to tbf_fsm
* tbf_dl: Clarify requirements for DL ACK/NACK
* tbf.h: Improve documentation on several flags
* Move tbf ul_ack_state to osmocom FSM
* Simplify tbf::set_polling()
* tbf: Move T3193 to tbf_state FSM
* fix typo 's/dowlink/downlink/g'
* cosmetic: Fix typo in comment
* sched: energy saving: Avoid Tx dummy blocks on empty PDCH TS
* Fix crash with dyn TS when using direct pcu
* Use LOGPDCH macro to standarize log line
* cosmetic: sysmo: Drop unneded comment line
* bts: Use public getter instead of class member
* sched: Lower log level of RTS on disabled pdch
* pdch: Make sure pending ImmAssRej scheduled for disabled pdch are dropped
* Support Neighbor Address Resolution over PCUIF IPA multiplex
* nacc_fsm: Move logic checking if SI is being waited for to a func helper
* scheduler: Skip Tx DL idle blocks in TRX0 when not in DIRECT_PHY mode
* PTCCH: skip Tx DL idle blocks when possible
* tbf_ul_ass_fsm.c: Fix missing state transition in FSM description
* tbf: poll_timeout(): Validate expected poll reason
* nacc: Introduce helper function nacc_fsm_exp_ctrl_ack()
* tbf: refactor poll_timeout() with a switch statement
* tests: TbfTest: Fix wrong behavior in test_tbf_dl_reuse()
* pdch: refactor rcv_control_ack() with a switch statement
* cosmetic: Fix missing space
* assert if tbf pointer for POLL event is NULL
* tbf_fsm: Ignore event DL_ACKNACK_MISS in state RELEASING
* tests: RLCMACTest: Add one more sample RA capabilities to suite
* rlcmac: Fix CSN1 definition for DownlinkDualCarrierCapability_r7_t in MS RA cap
* tbf: Assert if FSM allocation fails
* sched: Rename function
* pdch: Only release ULC entry if rx ul block matches the expected one
* pdch: Validate poll reason matches in rcv_control_(egprs)_dl_ack_nack()
* pdch: PktResReq: Avoid releasing ULC entry if expecting something else on UL
* Handle Final UL ACK/NACK Confirmation in tbf_fsm
* tbf_fsm: rename state NULL -> NEW
* pdch_ulc: Log POLL reason upon timeout
* tbf_dl_ass_fsm: Move block msg generation conditions to rts() function
* tbf_ul_ass_fsm: Fix use of incorrect log macro
* tbf_fsm: Handle MAX_N3105 in state ASSIGN
* tbf: Use define to flag control_ts unset special value
* tbf: Document temporary change of control_ts and move code assigning it back to FSM
* Return void in tbf_assign_control_ts()
* pdch: rcv_data_block: Avoid releasing ULC entry if expecting something else on UL
* tbf: Avoid keeping poll nodes in pdch_ulc of temporary control_ts used during PACCH assignment
* tbf_ul_ass_fsm: Avoid retrying Pkt Ul Ass if tbf is not in state ASSIGN
* Abort scheduling of pending Pkt Ul Ass if tbf goes into RELEASE step
* tbf: Drop pending polls during free also on states != ASSIGN
* pdch: Simplify code path allocating UL TBF
* pdch: Log reason of expected POLL when receiving unexpected UL data
* bts_rcv_rach(): Gather pointers to data objects early and use them later
* bts_rcv_rach(): Split code paths for Ass and Ass Rej
* Get rid of tbf tsc field
* ts_alloc: Rename s/tbf_/tbf/
* tbf: Update FSM names when TFI change during tbf_update()
* tbf_fsm: Add assert verifying X2002 only triggers for DL TBF
* tbf: Assert if update() is called on UL TBF
* tbf: update(): return negative val on error
* tbf: Drop unneeded braces in one line condition
* cosmetic: Fix typo in comment
* pcuif: Submit data_req with len=0 as idle frames
* Split csn1.c into common, enc and dec files
* csn1: Add unit test showing RadioAccess Capability decoding failure
* csn1: Avoid failing if optional DownlinkDualCarrierCapability_r7 is missing
* csn1: Avoid storing existence bit as true if content was actually NULL
* csn1_dec.c: Fix stored bit in CSN_NEXT_EXIST_LH
* bts: Fix misleading log line in bts_rcv_rach()
* tbf_ul: Document context where tbf_alloc_ul_ccch() is used
* bts: Rename 11bit RACH request counter
* vty: Avoid crash in tbf_print_vty_info with null ptr ctrg
* vty: Log tbf_state when showing a TBF
* vty: show tbf: Drop unneeded check for non-null ms
* bts: Introduce new RACH req counters for one/two phase access
* bts: Improve logging to clarify RACH req is for 2 phase access
* bts: Count RACH Request with unexpected content
* tbf: Increase log level of line about unable to allocate poll for TBF
* pdch: Improve log line and increase log level
* tbf_ul: Set first_(common_)ts in handle_tbf_reject
* tbf: Set m_created_ts in constructor
* tbf: Mark initial first_(common_)ts with special value
* tbf: Set tfi to initial special value
* bts: Add counters for successful 1,2 phase pkt access
* tbf_ul: Update FSM names for dummy reject TBFs
* cosmetic: Add parenthesis around expression to clarify it
* pdch::rcv_resource_request(): Use local var to store bts pointer
* tbf_ul: Improve documentation of tbf_alloc_ul_pacch()
* Add counter for successful contention resolution procedures
* doc: Update counters_generated.adoc using osmo_vty_interact.py
* bts_pch_timer: Fix timer working only for MI type IMSI
* tests/alloc: Extend test_bts_pch_timer() to validate MI type TMSI
* bts_pch_timer: Avoid resend Paging Request over PCUIF if T3113 is armed
* pdch: Log line detaching TBF at start of the function
* pdch: Log TS enable/disable transitions
* pdch: Log DL TBF originating the new UL TBF
* ts_alloc: Simplify tfi_find_free logic
* ts_alloc: rename function to clraify what it does
* ts_alloc: rename variable to clarify meaning
* pdch: Update ms_reserved_slots in GprsMS when TS becomes disabled
* pdch: Drop previous UL TBF from MS who sent PktResReq through SBA
* pdch: Increase log level of line informing about TS control change
* tbf_ul_ass_fsm: Log both TBFs if old TBF is handling assignment for new one
* cosmetic: gprs_pcu.h: Fix typo in comment
* tbf_dl_ass_fsm: Log both TBFs if old TBF is handling assignment for new one
* tbf_dl_ass_fsm: Fix missing transition to NONE if DL TBF is nonexistent
* tbf_{dl,ul}_ass_fsm.c: use proper macro to log tbf
* pcu_main: Mark -r cmdline param as deprecated
* vty: Introduce command 'gsmtap-remote-host' and 'gsmtap-category enable-all'
* Fix MS ending up with assigned imsi 000
* bts: Add counter availablePDCHAllocatedTime
* tbf_dl_ass_fsm: Drop unsued X2000 timer callback
* T_defs_pcu: Set default val for X2000 to 0 ms
* Move T3172 T_defs_bts->T_defs_pcu to have it configurable in VTY
[ Alexander Couzens ]
* gprs_bssgp_pcu: rework BSSGP Reset messages to support SGSN originated BSSGP-RESET
* gprs_bssgp_pcu: ensure only known BVCI can be resetted by the SGSN
* gprs_bssgp_pcu: add comments to the pcu states
[ Harald Welte ]
* pdch_ul_controller: Fix compiler warning on gcc-10.2
* manual: remove revhistory, as we don't maintain it manually anyyway
* manual: Update copyright years
* vty: Add configuration for Gb DSCP and socket priority
* manual: Include QoS chapter and add osmo-pcu specific example
[ Vadim Yanitskiy ]
* gprs_rlcmac_sched: fix incorrect length for CTR_RLC_DL_BYTES
* PCUIF protocol: add message definition for interference report
* pcu_l1_if: ignore PDCH interference reports, do not log errors
[ Oliver Smith ]
* test: add 'make update_exp' target
* Add counters: pcu.sgsn.N.rx_paging_{cs,ps}
* Add counters: pcu.bts.N.pch.requests
* Add counters: pcu.bts.N.pch.requests.timeout
* bts: delete pch_timer list in destructor
* tests: make update_exp: build check_PROGRAMS first
* debian/control: remove dh-systemd build-depend
* Add stats: pcu.bts.N.pdch.available/occupied
* Add stats: pcu.bts.N.pdch.occupied.gprs/egprs
* pdch: has_gprs_only_tb_attached: use m_num_tbfs
[ Neels Hofmeyr ]
* T_defs_bts: remove unit from doc strings
* Revert "Stop abusing T3169"
* fix: handle NULL return of as_dl_tbf() and as_ul_tbf()
* coverity: fix null deref from recent UL TBF leak fix
[ Daniel Willmann ]
* gprs_bssgp_pcu: Fix crash when configuring an existing ns bind
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 16:47:29 +0100
osmo-pcu (0.9.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* Pass paging group instead of imsi where later is not needed
* Allow Gb PAGING-PS without P-TMSI
* Support Gb PAGING-CS
* Support PAGING-CS and PAGING-PS on on PTP-BVCI
* tests/rlcmac: print test name at the start
* tests/rlcmac: Memzero decoded struct
* tests/rlcmac: Fix missing commas with unexpected results
* tests/rlcmac: Use osmo_hexdump to print buffers
* tests/rlcmac: Don't check stderr output
* tests/rlcmac: Add test to showcase that decode_gsm_ra_cap() fails
* csn1: Extend CSN_SERIALIZE to allow 0 bit of length
* csn1: Allow CHOICE elements to re-process the bits used for the choice
* csn1: Fix pedantic compiler warnings in csn.1 dissectors
* csn1: Drop format_p union from CSN_DESCR
* gsm_rlcmac.cpp: Fix trailing whitespace
* cosmetic: csn1.cpp: Fix whitespace
* csn1.cpp: Rework ProcessError() function to print errors
* rlcmac: Return error code from csn1 encoder/decoder
* Check return code of rlcmac decode/encode functions
* rlcmac: Transform a few LOGPC messages to LOGP
* Fix trailing newline mess with LOGP(C) in rlcmac/csn1
* llc_queue::{dequeue,enqueue}() refactor
* gsm_rlcmac: fix Packet_Resource_Request_t: s/Slot/I_LEVEL_TN/
* tests/llc: Change unrealistic time jump to avoid runtime error under ARM
* Use clock_gettime(CLOCK_MONOTONIC) and timespec everywhere
* Use downlink BSSGP RA Cap IE
* tests/RLCMACTest: free allocated bitvectors
* tests/RLCMACTest: Several fixes and improvements to RAcap tests
* rlcmac: Don't pass array element to CSN1 descriptors
* csn1: Validate recursive array max size during decoding
* rlcmac: Fix bug receiving RA cap
* rlcmac: Log names of de/encoded rlcmac packet types
* rlcmac: Introduce MS Radio Access Capabilities 2 to fix related spare bits
* cosmetic: rlcmac: Fix comment typo and whitespace introduced recently
* rlcmac: Rename field to MS RA Cap2 in Additional_MS_Rad_Access_Cap_t
* pcu_l1_if.cpp: Add missing header ctype.h
* gsm_rlcmac: Use 'struct bitvec' instead of 'bitvec'
* cosmetic: Do not indent header includes inside extern C block
* gsm_rlcmac.cpp: Avoid declaring variable in for loop
* csn1.h: Fix trailing whitespace
* tbf.cpp: Include c++ <new> header required for new operator's replacement type
* gsm_rlcmac: Disable unused CSN1 descriptors
* Move gsm_rlcmac.cpp -> .c
* rlcmac: support decode FDD_CELL_INFORMATION of "UTRAN FDD Description
* rlcmac: add dissection of 2G->3G/4G PS handover
* csn1: Fix Several typos & whitespace
* csn1: verify enough bits present to decode whole CSN_UINT_ARRAY
* csn1: Properly verify CSN_BITMAP length
* csn1: Remove code block from CSN_NEXT_EXIST_LH
* pcu_l1_if: Don't use GSMTAP_CHANNEL_PACCH when sending unknown gsmtap blocks
* pdch: Avoid sending GSMTAP_CHANNEL_UNKOWN for rejected UL EGPRS data block
* tbf: Avoid crash: don't set TBF window size if setup failed
* bts: Rename mslot_class_from_ra
* bts: Fix Decoding EGPRS MultislotClass from 11-bit EGPRS PACKET CHANNEL REQUEST
* bts: Return uint8_t in egprs_mslot_class_from_ra()
* Use OSMO_FD_* instead of deprecated BSC_FD_*
* Expect ms object to exist before calling tbf_alloc_ul_tbf()
* Expect ms object to exist before calling tbf_alloc_dl_tbf()
* pdch: rcv_resource_request(): Clarify tbf_free only needed if MS used to exist beforehand
* Drop unneeded arg 'ta' in tbf_alloc_ul()
* bts: Drop specific functions to increase counters
* bts: Drop specific functions to add values to counters
* bts: Drop specific functions to add values to stats
* pcu: tbf_ul: Clean up maybe_schedule_uplink_acknack()
* sysmo: femtobts.h: Avoid redefining global variables
* rlc: Drop unused function gprs_rlc_data::put_data
* rlc: Move prepare() function out of gprs_rlc_data struct
* tbf_ul: Fix UL ACK not sent to MS if intermediate UL block is lost
* Get rid of class GprsCodingScheme
* gsmtap: Set signal level and SNR fields
* gprs_ms: Small clean ups in IMSI storage related code
* gprs_ms: Transfer known EGPRS MS class when mergling old MS
* tbf: Drop unneeded method set_tlli_from_ul
* pdch.cpp: Fix logging line format in rcv_block wrong length
* Set correct GSMTAP channel type for PDTCH messages returning error
* decoding.cpp: Improve logging in malformed UL data parsing
* tbf_dl: uint8_t is enough to store a TA value
* encoding: Encode TA as unsigned and check validty against GSM48_TA_INVALID
* encoding.cpp: Fix missing spacing in function param
* pdch.cpp: Avoid dropping existing DL TBF during rcv_resource_request
* pdch.cpp: Avoid resetting (egprs_)ms_class to unknown if not found in MS RadioAccCap
* pdch.cpp: Fix wrong annoying log line about non-scheduled ResourceReq received
* pdch.cpp: Store TLLI promptly on newly created TLLI in rcv_resource_request
* Fix typo in log message
* pdch: Drop unneeded notice log message in rcv pkt meas report
* Introduce log macro helper LOGPMS
* configure.ac: Fix trailing whitespace
* doc: Update VTY reference xml file
* Support setting rt-prio and cpu-affinity mask through VTY
* pdch: rcv pkt meas rep: Allocate MS object early in path and use it
* Fix recent typo preventing MS from registering
* gitignore: Add __pychache__ dir
* tbf: Don't log rlcmac_diag() output in separate lines
* gprs_ms_storage.h: Set pointer to NULL instead of 0
* Free all MS TBFs when receiving GPRS Suspension Request
* cosmetic: fix indentation alignment
* vty: Add 'show bts pdch' command
* cosmetic: Fix indentation of for loops
* cosmetic: Fix typo in comment
* Fix crash accessing NULL tbf->pdch[first_ts]
* contrib/jenkins: Enable parallel make in make distcheck
* Improve debug logging for alloc algos
* Fix several calls to LOGPAL
* Move gprs_rlcmac_ul_tbf::window to correct file
* Move constructor gprs_rlcmac_dl_tbf::BandWidth to correct file
* tbf: Make window() available to tbf base class
* tbf: Implement enable_egprs() once
* tbf: Set MS during constructor time
* Move ul_tbf allocation code to correct file
* Move dl_tbf allocation code to correct file
* tbf: Drop unused function disable_egprs()
* tests: ms: Pass correct pointer in constructor instead of NULL
* tbf: Clean up gprs_rlcmac_dl_tbf::handle()
* alloc_algo_b: Select TRX with least assigned TFIs during TBF alloc
* bts: define egprs_enabled as bool
* cosmetic: Fix ws between if keyword and parenthesis
* tbf_dl: Update (egprs_)ms_class for already known MS
* cosmetic: tests: pcu_emu: fix trailing whitespace
* gprs_ms: Use proper function to get CS
* Move BTS initial values inside bts.cpp
* pcuif: Improve BTS-supported CS/MCS handling
* Move EGPRS MS mode set to gprs_ms.cpp
* Take into account BTS supported (M)CS values when retrieving the maximum
* Enable egprs support through PCUIF from BTS/BSC
* pdch: Process received CS1-4 data blocks regardless of egprs_enabled
* tbf_dl: Don't fake EGPRS MS class when no related info is available
* tbf_ul: Allow non-egprs phones if EGPRS is enabled
* Get rid of bts->egprs_enabled
* Fix configuration of initial_(m)cs
* Fix mcs_is_valid(): UNKNOWN value is not a valid (M)CS
* gprs_ms: Avoid enabling EGPRS if no MCS are supported
* tbf_ul: Log mismatching TLLI on log message
* Fix ctr reports: Remove ctr description from already removed counter
* encoding: Fix duplicate word in log str
* sched: Fix sending GSMTAP DL data blocks with unset USF
* sched: Use correct GMSTAP category for EGPRS DL data blocks
* Support multiplexing of GPRS and EGPRS TBFs in one PDCH
* pdch: packet_paging_request: Put back non-fitting paging entry where where it was
* pdch: Log hexdump of decde failure for dl rlcmac block
* csn1: Fix readIndex pointer change in CSN_VARIABLE_ARRAY
* csn1: Log CSN_VARIABLE_ARRAY values as hex
* main: generate coredump and exit upon SIGABRT received
* tbf: Log previous TS when changing Control TS
* Implement downgrade to DL MCS1-4 when USF for GPRS_only MS
* Dl TBF: Get rid of LLC UI dummy blocks following other data
* rlcmac: Fix typo in MT_PACKET_CELL_CHANGE_NOTIFICATION value_string
* gprs_rlcmac_sched: Use helper structure to store several tbf pointer params
* sched: Convert code handling next_list array to be size independant
* Convert GprsMS and helpers classes to C
* tbf: Fix wrong verb used in log message
* .gitignore: ignore files ending with ~
* doc: Improve CS/MCS GPRS/EGPRS considerations in User Manual
* tbf: remove 'software error' logs from tbf_free
* ms: Replace struct var with rate_ctr
* AllocTest: Avoid queuing tons of to-be-freed ms
* gprs_ms: Mark ms_ctrg_desc static
* Workaround ASan false positive runtime errors under some platforms
* Split PCU global PCU object from BTS object
* Move T_defs_pcu from BTS to PCU object
* Move force_two_phase field from BTS to PCU
* Move alpha,gamma fields from BTS to PCU
* Move dl_tbf_preemptive_retransmission field from BTS to PCU
* Move dl_arq_type field from BTS to PCU
* Move cs_adj* fields from BTS to PCU
* Move cs_downgrade_threshold field from BTS to PCU
* Move (m)cs_lqual_ranges fields from BTS to PCU
* Move ns_dialect field from BTS to PCU
* Move fc_* fields from BTS to PCU
* tests/tbf: Allocate PCU per test instead of globally
* Move ws_* fields from BTS to PCU
* Move llc_* fields from BTS to PCU
* Fix configuration mess of initial_cs/mcs between PCUIF and VTY
* Unify BTS into a C usable structure
* Get rid of bts singletons
* Rename 'bts_data' leftovers to 'bts'
* bts: combine bts_{init,cleanup} into consturctor/destructor methods
* Get rid of unused gsm_timer.{cpp,h}
* Convert gprs_bssgp_pcu.cpp to C
* Move tbf::free_all static methods to proper object files
* Convert osmo_bts_sock.cpp to C
* Allow multiple bts objects in PCU
* bts: Store RAC+CI from info_ind
* Get rid of singleton gprs_bssgp_pcu_current_bctx()
* Initial handling support for RIM messages
* gprs_pcu: Use libosmocore osmo_cgi_ps_cmp API
* ms: Drop always-false check
* sched: Check if egprs is enabled in TBF rather than MS being egprs capable
* tbf: Drop always-true condition checking for MS
* encoding: fix typos in comment
* ms: Set proper initial MCS values setting mode EGPRS_GMSK
* ms: Properly handle EGPRS_GMSK mode in ms_max_cs_dl/ul()
* Fix Dl EGPRS data blocks being generated occasionally on GPRS TBFs
* sched: Avoid picking TBF with nacked dl blocks when GMSK is required
* tbf: Make tbf_ms() param const
* Introduce NACC support
* NACC: Fix crash freeing struct if CTRL conn was refused during alloc
* NACC: delay CTRL conn socket init until it's needed
* NACC: allow setting keep time for entries in neigh and si cache
* NACC: Configure neighbor and SI resolution timeout values
* NACC: Send only Pkt Cell Chg Continue if SI retrieve fails
* doc: Mark PCU node red in network node diagram
* doc: Introduce section documenting NACC support
* nacc: Improve log line failing to establish CTRL neigh conn
* Update TS 04.60 references to new TS 44.060
* Drop comment about an already implemented TODO
* Move src/tbf.txt to doc/
* encoding: Fix comment description of S/P field
* tbf: Reuse stored result in variable in check_polling()
* tbf: Constify some methods
* nacc: Fix typo in function name
* nacc: Implement Pkt Cell Change Continue retransmission
* nacc: Avoid RIM procedures targeting cells under same PCU
* rlc.h: Fix struct bit fields on big endian systems
* cosmetic: fix typo in comment
* nacc_fsm: Move code filling struct to helper function
* nacc_fsm: Remove NACC_EV_RX_SI from in_event_mask of some states
* nacc_fsm: Support receiving Pkt Cell Change Notify in state WAIT_RESOLVE_RAC_CI
* nacc_fsm: nacc_fsm: Support receiving Pkt Cell Change Notify in state WAIT_REQUEST_SI
* nacc_fsm: Support receiving Pkt Cell Chg Notif while in some advanced states
* nacc_fsm: Improve log when sending RIM RAN-INFO to gather SI from remote cell
* vty: Write 'neighbor resolution' config to file
* cosmetic: fix line indentation
* sched: Avoid selecting TBF to tx NACC Dl msg if no TFI is assigned
* tests: Explicitly drop category from log
* tests: Replace deprecated API log_set_print_filename
* Use NULL as default value for pointer type
* find_multi_slots: Avoid calling mslot_class_get_tx() on each iteration
* find_multi_slots: Avoid multiple calls to mslot_class_get_rx()
* find_multi_slots: Mark mslot_class properties const
* find_multi_slots: Avoid multiple calls to mslot_class_get_type()
* Use ALPHA value received in SI13 from PCUIF
[ Vadim Yanitskiy ]
* pcu_l1_if.cpp: fix NULL-pointer dereference in imsi2paging_group()
* gsm_timer: fix comparison of constant LONG_MAX with an integer
* encoding: fix log_alert_exit(): do not treat error as format string
* tests/alloc: fix implicit conversion from 'double' to 'int8_t'
* gprs_bssgp_pcu: fix invalid use of non-static data member 'frame'
* gprs_bssgp_pcu: fixup: fix length check in gprs_bssgp_pcu_rx_dl_ud()
* csn1: fix csnStreamDecoder(): avoid conditional calls to bitvec_read_field()
* VTY: get rid of pcu_vty_go_parent() / pcu_vty_is_config_node()
* VTY: install talloc context introspection commands
* pcu_sock: fix memleak, allocate pcu_sock_state on stack
* pcu_sock: cosmetic: fix typo in a comment message
* tbf: cosmetic: fix spacing in gprs_rlcmac_tbf::create_ul_ass()
* tbf: fix NULL pointer dereference in create_[ul|dl]_ass()
* encoding: assert return value of bitvec_set_u64()
* csn1: fix some mistaken CSN.1 error names
* csn1: fix csnStreamDecoder(): catch unknown CSN_CHOICE values
* tests/rlcmac: mark Packet Polling Request as malformed
* csn1: fix existNextElement(): use bitvec_get_bit_pos()
* tests/rlcmac: additionally match debug output of the CSN.1 codec
* csn1: get rid of C++ specific code, compile with GCC
* csn1: fix csnStreamDecoder(): do not subtract no_of_bits twice
* csn1: fix csnStreamDecoder(): always keep remaining_bits_len updated
* csn1: fix csnStreamDecoder(): update bit_offset in CSN_EXIST{_LH}
* csn1: bitvec_get_uint() may return a negative, use %d
* csn1: use proper format specifier for unsigned integers
* gsm_rlcmac: fix misleading LOGP statement in decode_gsm_ra_cap()
* tests/rlcmac: fix malformed MS RA capability in testRAcap()
* tests/rlcmac: also verify encoding of MS RA Capability
* tests/rlcmac: add a new test vector for Packet Resource Request
* csn1: fix csnStreamDecoder(): skip bits unhandled by serialize()
* tests/rlcmac: also enable logging for DRLCMACDATA category
* rlcmac: fix encode_gsm_*(): do not suppress encoding errors
* csn1: fix: do not return 0 if no bits left in the buffer
* BSSGP: cosmetic use OSMO_IMSI_BUF_SIZE from libosmocore
* BSSGP: fix: properly encode P-TMSI in RR Paging Request
* pdch: fix packet_paging_request(): properly print paging MI
* pdch: cosmetic: use GSM_MI_TYPE_* constants from libosmocore
* fix: properly include pure C headers from C++ code
* l1if: fix pcu_rx_rach_ind(): use proper format string specifiers
* sba: fix possible memleak in SBAController::alloc()
* TBF/UL: fix rcv_data_block_acknowledged(): print the actual TLLI
* fix egprs_mslot_class_from_ra(): multislot class may not be present
* l1if: fix: s/pcu_rx_rach_ind_pdtch/pcu_rx_rach_ind_ptcch/g
* csn1: fix M_CHOICE: restirct maximum length of the choice list
* csn1: fix csnStreamEncoder(): also check length of the choice list
* csn1: fix csnStreamEncoder(): always check the choice index
* csn1: fix: never use enumerated types in codec structures
* encoding: cosmetic: use RLC_MODE_ACKNOWLEDGED where possible
* RLC/MAC: implement decoding of EGPRS Packet Channel Request
* encoding: fix write_ia_rest_egprs_uplink_sba(): add missing CHECK(rc)
* doc/manuals: fix typo in overview.adoc: s/Omsocom/Osmocom/g
* bts: refactor handling and parsing of RACH.ind
* BTS::parse_rach_ind(): properly handle EGPRS Packet Channel Request
* bts: add send_gsmtap_rach(), also send PTCCH/U over GSMTAP
* bts: fix send_gsmtap_rach(): properly pack 11 bit RA
* bts: cosmetic: use DUMMY_VEC for padding where possible
* encoding: drop log_alert_exit(), use OSMO_ASSERT() instead
* encoding: assert() presence of Downlink TBF
* direct-phy: fix handle_ph_ra_ind(): handle PH-RA.ind on PRACH SAPI
* debian/control: change maintainer to the Osmocom team / mailing list
* pcu_l1_if: use proper format specifier for PCUIF version
* pcu_l1_if: constify the argument of pcu_rx_info_ind()
* pcu_l1_if: cosmetic: rename both 'trx'/'ts' to 'trx_nr'/'ts_nr'
* pcu_l1_if: cosmetic: move struct 'gprs_rlcmac_pdch' into the for loop
* pcu_l1_if: cosmetic: correct error message in pcu_rx_info_ind()
* gsm_rlcmac: use consistent naming for [Extended] Packet Timing Advance
* tbf: cosmetic: use GSM_MACBLOCK_LEN where possible
* tbf: allocate the bitvec on stack in create_{dl,ul}_ass()
* encoding: constify 'tbf' in UL/DL assignment functions
* encoding: do not encode out of range Timing Advance values
* encoding: fix RRBP field in write_packet_uplink_assignment()
* encoding: use bool for use_egprs in write_packet_uplink_assignment()
* encoding: pass pdch slot directly to encoding functions
* encoding: clarify docstring for write_packet_downlink_assignment()
* encoding: use CSN.1 codec to generate Packet Uplink Assignment
* encoding: implement handing of hopping parameters
* encoding: fix gen_freq_params(): do not check pdch twice
* pcuif_proto: version 10: add frequency hopping parameters
* pcu_l1_if: cosmetic: use ARRAY_SIZE() in pcu_rx_info_ind()
* pcu_l1_if: correct logging level in pcu_rx_info_ind()
* pcu_l1_if: cosmetic: make {local,remote}_sockaddr scoped variables
* pcu_l1_if: use proper format string specifiers: %d -> %u
* pcu_l1_if: print NSVC address in more common format
* gprs_bssgp_pcu: make osmo_sockaddr local/sgsn arguments const
* gprs_bssgp_pcu: fix possible memleak in gprs_nsvc_create_and_connect()
* struct gprs_rlcmac_bts: remove unused 'nsei' field
* gprs_bssgp_pcu: fix: do not crash on receipt of subsequent INFO.ind
* doc/manuals: (re-)generate XML VTY reference automatically
* fix tbf_select_slot_set(): use LOGP() instead of LOGPC()
* main: remove line breaks in print_help(), increase spacing
* main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
* BSSGP: use tlvp_val8() in gprs_bssgp_pcu_rx_paging_cs()
* BSSGP: constify argument 'tp' of gprs_bssgp_pcu_rx_paging_{cs,ps}
* TLLI 0x00000000 is a valid TLLI, use 0xffffffff instead
* gprs_rlcmac_sched: fix incorrect SBA frame number assignment
* bts: fix uninitialized memaccess in BTS::send_gsmtap_rach()
* bts: fix uninitialized memaccess in BTS::send_gsmtap()
* tests/rlcmac: add more test vectors for Packet Resource Request
* contrib/osmo-pcu.spec.in: require libosmo* version 1.4.0
* contrib/osmo-pcu.spec.in: add missing libosmoctrl dependency
* vty: register libosmocore's FSM introspection commands
[ Anders Broman ]
* csn1: Update M_NULL CSN_DESCR to match wireshark
* csn1: packet-csn1.c:179: warning: 'pui8' may be used uninitialized in this function
* csn1: Fix warning with -Wmissing-prototypes
* csn1: Try to fix cast discards '__attribute__((const))' qualifier from pointer target type
* gsm_rlcmac.cpp: hanged all M_BIT macros to M_UINT, as M_BIT does not use the referenced hf.
[ Jeff Morriss ]
* csn1: shuffle decrements of remaining_bits_len
[ Pascal Quantin ]
* csn1: Fix an infinite loop in CSN.1 dissector when having more than 255 padding bits
* gsm_rlcmac.h: Remove Uplink messages from the RlcMacDownlink_t structure
* gsm_rlcmac: Enhance dissection of PSI1
* gsm_rlcmac.cpp: Do not skip too many lines of the CSN_DESCR when the field is missing
* gsm_rlcmac.cpp: fix an out of bounds access
* gsm_rlcmac.cpp: fix another global-buffer-overflow error reported by ASAN
* gsm_rlcmac.cpp: fix global-buffer-overflow error reported by ASAN
[ Guy Harris ]
* csn1: Don't cast away constness
[ Alexis La Goutte ]
* csn1: fix this statement may fall through [-Werror=implicit-fallthrough=] found by gcc7
[ Bill Meier ]
* gsm_rlcmac.h: #if 0 unused stuff
[ Gerald Combs ]
* gsm_rlcmac.h: Make sure we have a corresponding 'u' member to RlcMacDownlink_t for every call
[ Vincent Helfre ]
* gsm_rlcmac: add dissection of NAS container
* gsm_rlcmac: improve dissection of MS RA Capability IE
[ AndersBroman ]
* gsm_rlcmac: Update : PACKET RESOURCE REQUEST to Release 14.0.0
[ Keith ]
* Send UL-CTRL Packet to GSMTAP even if we fail to decode.
* Don't check ul_control_block before decoding into it.
[ Harald Welte ]
* csn1.c: Almost all of the logging is DEBUG, not NOTICE
* TODO: remove those that have obviously been implemented 5+ years ago
* bts.cpp: Increase constructor priority
* Use osmo_fd_setup() whenever applicable
* Use osmo_fd_*_{disable,enable}
* gb manual: 08.16 -> 48.016 / 08.18 -> 48.018
* gb manual: NS is implemented in libosmogb, not libosmocore
* manuals/gb/ns.adoc: Update documentation regarding SNS capability
* migrate to DLBSSGP as log sub-system for BSSGP
[ Eric ]
* configure.ac: fix libtool issue with clang and sanitizer
* tbf: add virtual destructor
[ Philipp Maier ]
* gprs_debug: Use only LOGL_NOTICE as default loglevel
* vty: add attributes to VTY commands indicating when they apply
* pcu_main: add commandline option --vty-ref-xml
* gprs_bssgp_rim: add serving BSS NACC application
[ Oliver Smith ]
* contrib: import RPM spec
* contrib: integrate RPM spec
* Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
* contrib/jenkins: don't build osmo-gsm-manuals
* configure.ac: set -std=gnu11
[ Neels Hofmeyr ]
* use new osmo_mobile_identity api (avoid deprecation)
* paging: pass struct osmo_mobile_identity, not encoded IE bytes
[ Alexander Couzens ]
* pcuif_proto: version 0xa: add support for IPv6 NSVCs
* Revert "pcuif_proto: version 0xa: add support for IPv6 NSVCs"
* pcuif_proto: version 10: add support for IPv6 NSVCs
* Use the new NS2 lib
* Rework NS configuration over the info indication
* pcu_l1_if: fix misaligned assignment of remote address
* NS2: follow the change of ownership
* gprs_bssgp_pcu: follow ns2 library changes
* NS2: rework handling of unknown primitive
* ns2: follow ns2 dialect changes
* ns2: follow changes to add a unique name to all binds
* ns2: follow ns2 sns api changes
* gprs_ns2: set default dialect to ipaccess
* gprs_rlcmac_sched: don't leak a sched_dummy()
* gprs_rlc_ts_alloc: ensure no rolling slots are allocated
* follow gprs_ns2 API enum changes
* gprs_ns2: migrate to the new vty syntax
* gprs_bssgp: rework and rename ns_create_nsvc -> ns_configure_nse
* gprs_bssgp: rename gprs_ns_config -> gprs_ns_update_config
* gprs_bssgp: use gprs_ns2_sns_add_bind() to allow the NSE to use the binds for IP-SNS configuration
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 14:41:00 +0100
osmo-pcu (0.8.0) unstable; urgency=medium
[ Alexander Couzens ]
* tests: test encoding of egprs ul ack/nacks
* tbf_dl: add comments to the scheduler
* encoding: fix space, tabs
[ Vadim Yanitskiy ]
* osmobts_sock.cpp: pcu_sock_cb(): use libosmocore's socket API
* osmobts_sock.cpp: pcu_sock_read(): use stack buffer, not heap
* osmobts_sock.cpp: pcu_sock_read(): further simplify the code
* osmobts_sock.cpp: do not print the same debug message twice
* VTY: refactor pcu_vty_show_ms_all(): use show_ms()
* VTY: fix command 'show tbf all': properly filter TBFs
* BSSGP: do not reject SUSPEND ACK / NACK messages
* BSSGP: properly print BVCI for signalling messages (BVCI=0)
* tests/tbf: suspend warnings about the link quality measurements
* GprsMs::update_cs_ul(): clarify the meaning of old_link_qual
* gprs_bssgp_destroy(): fix memleak and NULL-pointer dereference
* PTCCH: implement basic message codec and API
* PTCCH: properly handle RTS.req for PCU_IF_SAPI_PTCCH
* pcuif_proto.h: extend RACH.ind with TRX / TS numbers
* PTCCH: properly handle RACH.ind for PCU_IF_SAPI_PTCCH
* VTY: add warning about changing PCU socket path at run-time
* VTY: cosmetic: use osmo_talloc_replace_string()
[ Pau Espin Pedrol ]
* cosmetic: tbf: Rename T and N arrays
* Use proper API osmo_timer_setup() to set up timer struct
* Introduce osmo_tdef infra and timer VTY commands
* bts.cpp: Fix osmo_tdef initialization on older g++ compilers
* Use osmo_tdef for BSSGP T1 and T2
* Use osmo_tdef to implement T3190
* tests: TbfTest: Unify stderr and stdout to ease debugging
* Use osmo_tdef to implement ms-idle-time
* Use osmo_tdef to implement dl-tbf-idle-time
* pdch.cpp: Refactor bitvec param passing in rcv_control_block
* pdch.cpp: Use pcu_l1_meas previously filled by lower layers
* cosmetic: fix whitespace
* Move out tbf subclasses from tbf.h to their own headers
* Move tbf_{dl,ul} child constructors to respective .cpp files
* tbf_dl: Setup m_llc_timer in constructor using osmocom API
* tbf_dl.cpp: Remove dup call to tbf_update_ms_class() in state GPRS_RLCMAC_WAIT_RELEASE
* vty: Fix osmo_tdef timers not listed in write config
* Log RACH Requests using GSMTAP
* Log AGCH and PCH blocks using GSMTAP
* pcu_l1_if.cpp: Fix GSMTAP Imm Assign PCH wrong encoding
* pcu_l1_if.cpp: Drop unneeded byte in Imm Ass PCH buffer
* pcu_l1_if.cpp: Imm Assign PCH: clarify size of different items
* pcu_l1_if.cpp: Replace value 23 with libosmocore's GSM_MACBLOCK_LEN
* Fix assertion hit upon CCCH Paging Request
* doc: vty: Update osmo-pcu_vty_reference.xml
* Clarify (M)CS related VTY attributes
* Remove dash from name used in VTY cmd prompt
* tbf_dl.cpp: Fix typo in log line
* pcu_l1_if: Check pag_req id_lv len fits buffer
* prs_bssgp_pcu.cpp: Mark priv funcs as static and remove trailing whitespace
* Fix trailing whitespace
* fix typo in log message
* Log BVCI PTP value upon msg recv
* Split identity_lv param into mi+mi_len
[ Oliver Smith ]
* doc: update generated VTY reference
* tbf_dl: make preemptive retransmission optional
* Forward ETWS Primary Notification to MS
* tests/app_info: fix compiling with older g++
* configure.ac: set C and C++ dialects
[ Harald Welte ]
* manual: Fix copy+paste error
* manual: Fix documentation missing "-D" command line option
* manual: Add missing documentation for '-i' command line option
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 03 Jan 2020 19:40:02 +0100
osmo-pcu (0.7.0) unstable; urgency=medium
[ Rafael Diniz ]
* Added support for daemonize to osmo-pcu.
* Fix help message formatting of osmo-pcu.
[ Max ]
* Don't install pcuif_proto.h header
* Move C include to proper place
* Add define for dummy burst string
* Add encoding tests for Immediate Assignment
* Clarify write_immediate_assignment() signature
* Restructure IA Rest Octets encoders
* Rewrite Packet Downlink Assignment
* Rewrite EGPRS Packet Uplink IA Rest Octets for MBA
* Rewrite EGPRS Packet Uplink IA Rest Octets for SBA
* MCS: internalize 'family' parameter
* EDGE tests: reduce code duplication
* MCS: remove dead code
* EDGE tests: remove no-op check
* Use msgb_eq_data_print() in tests
* Tighten lqual table limits check
* Enable LGLOBAL logging for TBF tests
* Log (M)CS UL update errors
* MCS: move Coding Scheme enum outside of class definition
* Make get_retx_mcs() into regular function
* MCS: remove unused function
* Debian: bump copyright year
* Use unique NSEI/BVCI/NSVCI in TBF tests
* MS store: move test helper to unit test
* Explicitly clean up BTS singleton
* MCS: move HeaderType enum outside of class definition
* MCS: use value_string for conversion
* TBF-DL: log MCS as string
* Fix TA index encoder
* MCS: move Mode enum outside of class definition
* MCS: add mcs_is_*() helpers
* MCS: add Channel Coding Command encoder
* Fix Channel Coding Command for MCS
* Rewrite Packet Uplink IA Rest Octets for MBA
* Rewrite Packet Uplink IA Rest Octets for SBA
* Use Timing Advance Index in UL assignments
* TBF: update MCS counters
* TBF-DL: cosmetic update for helper routines
* Update IA Rest Octets encoding
* TS alloc: expand tests log
* vty: add commands to show TBF of a certain kind
* Update MCS selection for retransmission
* cosmetic: use const pointer for bts_data
* Add test for MS mode and (M)CS settings
* Use libosmocore for IMSI parsing
[ Harald Welte ]
* Mark gprs_ns_reconnect() as static (not used outside of C file)
* Optionally Use the NS Sub-Network-Service (SNS) on Gb
* pcu_l1_if: Fix erroneous endian-swapping of the CellID
* Forward GPRS SUSPEND REQ from BTS to SGSN using BSSGP
* gprs_debug: Use named initializers and explicit array indicies
* bssgp: Fix dead code: PDUT_STATUS can never reach this part
[ JF Dionne ]
* encoding: Fixes TMSI vs MI bit selection in repeated page info
[ Oliver Smith ]
* tests: use -no-install libtool flag to avoid ./lt-* scripts
* debian: create -doc subpackage with pdf manuals
* contrib/jenkins.sh: run "make maintainer-clean"
[ Daniel Willmann ]
* Include pdch.h in bts.h even if we're not compiling C++
* oc2g: Remove custom alarms
* oc2g: Change log type (Litecell15->Oc2g)
* jenkins.sh: Add oc2g build support
* manuals: Add script to regenerate vty/counter documentation
* manuals: Update VTY documentation
[ Jean-Francois Dionne ]
* Initial commit for OC-2G support.
[ Minh-Quang Nguyen ]
* OC-2G: Fix missing header
* OC-2G: Fix TA adjustment
* OC-2G: Always use positive TA information provided in PH-RA-IND
[ Alexander Couzens ]
* gprs_bssgp_pcu: make gprs_bssgp_ns_cb public
* gprs_bssgp_pcu: explicit allocate & initialize bssgp_nsi instance
* encoding: correct encoding of CRBB in ACK/NACK when not byte aligned
* encoding: use `/* */` for comments instead of `#if 0 #endif`
* egprs_rlc_compression: fix white spaces
* tests/BitcompTest: fix wording in log message
* rlc: replace int with uint16_t
* Encoding: drop struct gprs_rlcmac_bts* from all functions
* decompress_crbb: add length argument for search_runlen
* Encoding: write_packet_ack_nack_desc_egprs: don't use a reference for rest_bits
* bts.cpp: ensure left-shift operation does not exceed uint32_t
* Encoding: use uint16_t when interacting with the window object
* Encoding: ACK/NACK: always encode with length field present
[ Keith ]
* Cosmetic: Osmcoom -> Osmocom
[ Vadim Yanitskiy ]
* src/pcu_l1_if.cpp: fix: properly pass measurements from PCUIF
* gprs_bssgp_pcu_rx_dl_ud(): fix: BSSGP_IE_IMSI is optional
* gprs_bssgp_pcu.cpp: check return code of gsm48_mi_to_string()
* gprs_bssgp_pcu_rx_dl_ud(): use OSMO_IMSI_BUF_SIZE
[ Thorsten Alteholz ]
* fix spelling errors detected by lintian
[ Eric Wild ]
* ubsan: fix shift
[ Pau Espin Pedrol ]
* Remove undefined param passed to {logging,osmo_stats}_vty_add_cmds
* Require newer libosmocore to avoid compile failures
-- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 07 Aug 2019 21:09:53 +0200
osmo-pcu (0.6.0) unstable; urgency=medium
[ Harald Welte ]
* debian/rules: Don't overwrite .tarball-version
* gprs_rlcmac_received_lost(): Fix regression / uninitialized now_tv
* initial checkin of manuals to public repo
* Add link to Asciidoc source code of manual
* Initial place-holder for the new Gb/IP interface documentation
* Gb message sequence chart: flip sides (SGSN should be right)
* Gb message sequence chart: Add notion of PCU unix domain socket
* Gb: Various spelling fixes
* gb: Some language improvements, formatting changes
* consistently use '3GPP TS' not sometimes 3GPP TS and sometimes TS.
* gb/NS: Clarify the language regarding the UDP port numbers / socket
* vty-ref: Update URI of docbook 5.0 schema
[ Pau Espin Pedrol ]
* Cleanup of systemd service files
* configure.ac: Set CXXFLAGS during --enable-sanitize
* Install systemd services with autotools
* Move examples/ to doc/examples/
* Install osmo-pcu.cfg to docdir/examples
* Allow easily disabling GFDL references
[ Stefan Sperling ]
* check for overlong unix socket paths
[ Max ]
* deb: add missing copyright file
* OsmoPCU: fix Gb documentation front page
* OsmoPCU: expand NS documentation
* OsmoPCU: expand BSSGP documentation
* OsmoPCU: add MSC chart
[ Neels Hofmeyr ]
* Importing history from osmo-gsm-manuals.git
* make clean: also remove generated image files
* add 'make check' target
* fix 'make clean': shell glob, ignore failure
* refactor Makefile build rules, don't use the FORCE
[ Jonathan Brielmaier ]
* fix various typos across all manuals
[ Philipp ]
* configuration: fixing typos
[ Alexander Couzens ]
* OsmoPCU: add rate counter documentation
[ Daniel Willmann ]
* Change OpenBSC mentions to OsmoBSC where applicable
[ Oliver Smith ]
* build manuals moved here from osmo-gsm-manuals.git
* Fix DISTCHECK_CONFIGURE_FLAGS override
* contrib/jenkins.sh: build and publish manuals
* contrib: fix makedistcheck with disabled systemd
-- Harald Welte <laforge@gnumonks.org> Mon, 21 Jan 2019 19:03:52 +0100
osmo-pcu (0.5.1) unstable; urgency=medium
[ Harald Welte ]

19
debian/control vendored
View File

@@ -1,13 +1,13 @@
Source: osmo-pcu
Section: net
Priority: optional
Maintainer: Holger Hans Peter Freyther <holger@moiji-mobile.com>
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
Build-Depends: debhelper (>= 9),
dh-autoreconf,
dh-systemd (>= 1.5),
autotools-dev,
pkg-config,
libosmocore-dev (>= 0.10.1)
libosmocore-dev (>= 1.6.0),
osmo-gsm-manuals-dev (>= 1.2.0)
Standards-Version: 3.9.8
Homepage: http://osmocom.org/projects/osmopcu
Vcs-Git: git://git.osmocom.org/osmo-pcu
@@ -19,7 +19,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Osmocom GPRS/EDGE Packet Control Unit (PCU)
The GPRS Packet Control Unit is co-located with the GSM BTS or GSM BSC
in order to provide packet-switched services for 2G (2.5G, 2.75G)
networks. OsmoPCU is the Osmcoom implementation of this network
networks. OsmoPCU is the Osmocom implementation of this network
element. It interfaces to osmo-bts via the PCU socket of OsmoBTS
and via Gb (NS-over-IP) interface with the SGSN such as OsmoSGSN.
@@ -32,6 +32,15 @@ Depends: osmo-pcu (= ${binary:Version}),
Description: Debug symbols for the Osmocom GPRS/EDGE Packet Control Unit (PCU)
The GPRS Packet Control Unit is co-located with the GSM BTS or GSM BSC
in order to provide packet-switched services for 2G (2.5G, 2.75G)
networks. OsmoPCU is the Osmcoom implementation of this network
networks. OsmoPCU is the Osmocom implementation of this network
element. It interfaces to osmo-bts via the PCU socket of OsmoBTS
and via Gb (NS-over-IP) interface with the SGSN such as OsmoSGSN.
Package: osmo-pcu-doc
Architecture: all
Section: doc
Priority: optional
Depends: ${misc:Depends}
Description: ${misc:Package} PDF documentation
Various manuals: user manual, VTY reference manual and/or
protocol/interface manuals.

133
debian/copyright vendored Normal file
View File

@@ -0,0 +1,133 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: osmo-pcu
Source: git://git.osmocom.org/osmo-pcu
Files-Excluded: debian
Files: *
Copyright: 2009-2015 Holger Hans Peter Freyther <zecke@selfish.org>
2013 Jacob Erlbeck <jerlbeck@sysmocom.de>
2016-2019 sysmocom s.m.f.c. GmbH <info@sysmocom.de>
2015 by Yves Godin <support@nuranwireless.com>
License: AGPL-3.0+
Files: src/gprs_ms_storage.h
src/gprs_ms_storage.cpp
src/gprs_ms.h
src/gprs_coding_scheme.cpp
src/gprs_coding_scheme.h
src/coding_scheme.c
src/coding_scheme.h
src/cxx_linuxlist.h
src/pcu_vty_functions.cpp
src/pcu_vty_functions.h
src/pcu_utils.h
src/gprs_codel.c
src/gprs_codel.h
Copyright: 2016-2019 sysmocom s.m.f.c. GmbH <info@sysmocom.de>
License: GPL-2.0+
Files: osmoappdesc.py
Copyright: 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
License: GPL-3.0+
Files: src/gprs_debug.cpp
src/gprs_debug.h
src/pcu_main.cpp
src/pcu_l1_if.h
Copyright: 2012 Ivan Klyuchnikov
License: GPL-2.0+
Files: src/tbf.cpp
src/tbf_ul.cpp
src/tbf_dl.cpp
src/sba.cpp
src/sba.h
src/llc.cpp
src/encoding.cpp
src/encoding.h
src/gprs_rlcmac.cpp
src/gprs_rlcmac_ts_alloc.cpp
Copyright: 2012 Ivan Klyuchnikov
2012 Andreas Eversberg <jolly@eversberg.eu>
2013 by Holger Hans Peter Freyther
License: GPL-2.0+
Files: src/gprs_rlcmac.h
src/gprs_bssgp_pcu.cpp
src/gprs_bssgp_pcu.h
src/bts.h
Copyright: 2012 Ivan Klyuchnikov
2013 by Holger Hans Peter Freyther
License: GPL-2.0+
Files: src/rlc.h
src/decoding.cpp
src/decoding.h
Copyright: 2012 Ivan Klyuchnikov
2012 Andreas Eversberg <jolly@eversberg.eu>
License: GPL-2.0+
Files: src/rlc.cpp
src/llc.h
src/tbf.h
Copyright: 2013 by Holger Hans Peter Freyther
License: GPL-2.0+
Files: src/pcu_l1_if.cpp
src/gprs_rlcmac_meas.cpp
src/gprs_rlcmac_sched.cpp
src/osmobts_sock.cpp
Copyright: 2012 Andreas Eversberg <jolly@eversberg.eu>
License: GPL-2.0+
Files: src/csn1.h
src/csn1.c
src/csn1_dec.c
src/csn1_enc.c
src/gsm_rlcmac.cpp
src/gsm_rlcmac.h
Copyright: 2011 Vincent Helfre
2011 ST-Ericsson (Jari Sassi)
License: GPL-2.0+
License: AGPL-3.0+
All rights not specifically granted under this license are 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.
License: GPL-3.0+
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/>.
.
On Debian systems, the complete text of the GNU General Public License
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
License: GPL-2.0+
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, see <http://www.gnu.org/licenses/>.
.
On Debian systems, the complete text of the GNU General Public License
Version 2 can be found in `/usr/share/common-licenses/GPL-2'.

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

@@ -0,0 +1 @@
usr/share/doc/osmo-pcu-doc/*.pdf

View File

@@ -1,4 +1,4 @@
etc/osmocom/osmo-pcu.cfg
lib/systemd/system/osmo-pcu.service
usr/bin/osmo-pcu
usr/include/osmocom/pcu/pcuif_proto.h
usr/lib/*/pkgconfig/osmo-pcu.pc
usr/share/doc/osmo-pcu/examples/osmo-pcu/osmo-pcu.cfg

View File

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

11
debian/rules vendored
View File

@@ -14,10 +14,6 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all
override_dh_strip:
dh_strip --dbg-package=osmo-pcu-dbg
override_dh_autoreconf:
echo $(VERSION) > .tarball-version
dh_autoreconf
override_dh_clean:
dh_clean
$(RM) tests/package.m4
@@ -26,3 +22,10 @@ override_dh_clean:
# Print test results in case of a failure
override_dh_auto_test:
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
override_dh_auto_configure:
dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
# Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers)
override_dh_compress:
dh_compress -X.pdf

4
doc/Makefile.am Normal file
View File

@@ -0,0 +1,4 @@
SUBDIRS = \
examples \
manuals \
$(NULL)

7
doc/examples/Makefile.am Normal file
View File

@@ -0,0 +1,7 @@
examples_pcudir = $(docdir)/examples/osmo-pcu
examples_pcu_DATA = osmo-pcu.cfg
osmoconfdir = $(sysconfdir)/osmocom
osmoconf_DATA = osmo-pcu.cfg
EXTRA_DIST = osmo-pcu.cfg

View File

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

28
doc/manuals/Makefile.am Normal file
View File

@@ -0,0 +1,28 @@
EXTRA_DIST = osmopcu-gb.adoc \
osmopcu-gb-docinfo.xml \
osmopcu-usermanual.adoc \
osmopcu-usermanual-docinfo.xml \
osmopcu-vty-reference.xml \
regen_doc.sh \
chapters \
gb \
vty
if BUILD_MANUALS
ASCIIDOC = osmopcu-usermanual.adoc osmopcu-gb.adoc
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
osmopcu-gb.pdf: $(srcdir)/gb/*.adoc $(srcdir)/gb/*.msc
osmopcu-usermanual.pdf: $(srcdir)/chapters/*.adoc
VTY_REFERENCE = osmopcu-vty-reference.xml
BUILT_REFERENCE_XML = $(builddir)/vty/pcu_vty_reference.xml
$(builddir)/vty/pcu_vty_reference.xml: $(top_builddir)/src/osmo-pcu
mkdir -p $(builddir)/vty
$(top_builddir)/src/osmo-pcu --vty-ref-xml > $@
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
OSMO_REPOSITORY = osmo-pcu
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
endif

View File

@@ -0,0 +1,492 @@
== Configuring OsmoPCU
Contrary to other network elements (like OsmoBSC, OsmoNITB), the
OsmoPCU has a relatively simple minimum configuration.
This is primarily because most of the PCU configuration happens
indirectly from the BSC, who passes the configuation over A-bis OML via
OsmoBTS and its PCU socket into OsmoPCU.
A minimal OsmoPCU configuration file is provided below for your reference:
.Example: Minimal OsmoPCU configuration file (`osmo-pcu.cfg`)
----
pcu
flow-control-interval 10 <1>
cs 2 <2>
alloc-algorithm dynamic <3>
gamma 0
----
<1> send a BSSGP flow-control PDU every 10 seconds
<2> start a TBF with the initial coding scheme 2
<3> dynamically chose between single-slot or multi-slot TBF allocations
depending on system load
However, there are plenty of tuning parameters for people interested to
optimize PCU throughput or latency according to their requirements.
=== Configuring the Coding Schemes and Rate Adaption
As a reminder:
- GPRS supports Coding Schemes 1-4 (CS1-4), all of them use GMSK modulation
(same as GSM).
- EGPRS supports MCS1-9, where MCS1-4 is GMSK, and MCS5-9 use 8-PSK modulation.
The range of Coding Schemes above only apply to RLCMAC data blocks; RLCMAC
control blocks are always transmitted with CS1 (GMSK). Hence, CS1 is always
supported and must be always permitted.
The BSC includes a bit-mask of permitted [E]GPRS coding schemes as part of the
A-bis OML configuration, controlled by VTY `gprs mode (none|gprs|egprs)`. This
is passed from the BTS via the PCU socket into OsmoPCU, and the resulting set
can be further constrained by OsmoPCU VTY configuration.
Some additional OsmoPCU parameters can be set as described below.
==== Initial Coding Scheme
You can use the `cs <1-4> [<1-4>]` command at the `pcu` VTY config node
to set the initial GPRS coding scheme to be used. The optional second
value allows to specify a different initial coding scheme for uplink.
Similarly, `mcs <1-9> [<1-9>]` can be used to set up the initial EGPRS coding
scheme.
[[max_cs_mcs]]
==== Maximum Coding Scheme
You can use the `cs max <1-4> [<1-4>]` command at the `pcu` VTY config
node to set the maximum GPRS coding scheme that should be used as part of the
rate adaption. The optional second value allows to specify a different maximum
coding scheme for uplink.
Similarly, `mcs max <1-9> [<1-9>]` can be used to set up the maximum EGPRS
coding scheme.
The actual Maximum Coding Scheme for each direction used at runtime is actually
the result of taking the maximum value from the permitted GPRS coding schemes
received by the BSC (or BTS) over PCUIF which is equal or lower tho the
configured value.
Example: PCUIF announces permitted MCS bit-mask (`MCS1 MCS2 MCS3 MCS4`) and
OsmoPCU is configured `mcs max 6`, then the actual maximum MCS used at runtime
will be `MCS4`.
==== Rate Adaption Error Thresholds
You can use the `cs threshold <0-100> <0-100>` command at the `pcu` VTY
config node to determine the upper and lower limit for the error rate
percentage to use in the rate adaption. If the upper threshold is
reached, a lower coding sheme is chosen, and if the lower threshold is
reached, a higher coding scheme is chosen.
==== Rate Adation Link Quality Thresholds
You can use the `cs link-quality-ranges cs1 <0-35> cs2 <0-35> <0-35> cs3
<0-35> <0-35> cs4 <0-35>` command at the `pcu` VTY config node to tune
the link quality ranges for the respective coding schemes.
==== Data Size based CS downgrade Threshold
You can use the `cs downgrade-threshold <1-10000>` command at the `pcu`
VTY config node to ask the PCU to down-grade the coding scheme if less
than the specified number of octets are left to be transmitted.
=== Miscellaneous Configuration / Tuning Parameters
==== Downlink TBF idle time
After a down-link TBF is idle (all data in the current LLC downlink
queue for the MS has been transmitted), we can keep the TBF established
for a configurable time. This avoids having to go through a new one or
two phase TBF establishment once the next data for downlink arrives.
You can use the `dl-tbf-idle-time <1-5000>` to specify that time in
units of milli-seconds. The default is 2 seconds.
==== MS idle time
Using the `ms-idle-time <1-7200>` command at the `pcu` VTY config node
you can configure the number of seconds for which the PCU should keep
the MS data structure alive before releasing it if there are no active
TBF for this MS.
The OsmoPCU default value is 60 seconds, which is slightly more than
what 3GPP TS 24.008 recommends for T3314 (44s).
The MS data structure only consumes memory in the PCU and does not
require any resources of the air interface.
==== Forcing two-phase access
If the MS is using a single-phase access, you can still force it to
use a two-phase access using the `two-phase-access` VTY configuration
command at the `pcu` VTY config node.
=== Configuring BSSGP flow control
BSSGP between SGSN and PCU contains a two-level nested flow control
mechanism:
. one global flow control instance for the overall (downlink) traffic
from the SGSN to this PCU
. a per-MS flow control instance for each individual MS served by this
PCU
Each of the flow control instance is implemented as a TBF (token bucket
filter).
==== Normal BSSGP Flow Control Tuning parameters
You can use the following commands at the `pcu` VTY config node to tune
the BSSGP flow control parameters:
`flow-control-interval <1-10>`::
configure the interval (in seconds) between subsequent flow
control PDUs from PCU to SGSN
`flow-control bucket-time <1-65534>`::
set the target downlink maximum queueing time in centi-seconds.
The PCU will attempt to adjust the advertised bucket size to match this
target.
==== Extended BSSGP Flow Control Tuning parameters
There are some extended flow control related parameters at the `pcu` VTY
config node that override the automatic flow control as specified in the
BSSGP specification. Use them with care!
`flow-control force-bvc-bucket-size <1-6553500>`::
force the BVC (global) bucket size to the given number of octets
`flow-control force-bvc-leak-rate <1-6553500>`::
force the BVC (global) bucket leak rate to the given number of bits/s
`flow-control force-ms-bucket-size <1-6553500>`::
force the per-MS bucket size to the given number of octets
`flow-control force-ms-leak-rate <1-6553500>`::
force the per-MS bucket leak rate to the given number of bits/s
=== Configuring LLC queue
The downlink LLC queue in the PCU towards the MS can be tuned with a
variety of parameters at the `pcu` VTY config node, depending on your
needs.
`queue lifetime <1-65534>`::
Each downlink LLC PDU is assigned a lifetime by the SGSN, which
is respected by the PDU *unless* you use this command to
override the PDU lifetime with a larger value (in centi-seconds)
`queue lifetime infinite`::
Never drop LLC PDUs, i.e. give them an unlimited lifetime.
`queue hysteresis <1-65535>`::
When the downlink LLC queue is full, the PCU starts dropping
packets. Using this parameter, we can set the lifetime
hysteresis in centi-seconds, i.e. it will continue discarding
until "lifetime - hysteresis" is reached.
`queue codel`::
Use the 'CoDel' (Controlled Delay) scheduling algorithm, which
is designed to overcome buffer bloat. It will use a default
interval of 4 seconds.
`queue codel interval <1-1000>`::
Use the 'CoDel' (Controlled Delay) scheduling algorithm, which
is designed to overcome buffer bloat. Use the specified
interval in centi-seconds.
`queue idle-ack-delay <1-65535>`::
Delay the request for an ACK after the last downlink LLC frame
by the specified amount of centi-seconds.
=== Configuring MS power control
GPRS MS power control works completely different than the close MS power
control loop in circuit-switched GSM.
Rather than instructing the MS constantly about which transmit power to
use, some parameters are provided to the MS by which the MS-based power
control algorithm is tuned.
See 3GPP TS 05.08 for further information on the algorithm and the
parameters.
You can set those parameters at the `pcu` VTY config node as follows:
`gamma <0-62>`::
Set the gamma parameter for MS power control in units of dB.
Parameter `ALPHA` is set on the BSC VTY configuration file on a per-BTS basis,
and forwarded by OsmoPCU to the MS through the SI13 received from the former
over PCUIF. OsmoPCU VTY command `alpha <0-10>` overrides the value received over
PCUIF to keep backward compatibility with existing config files, but it is
currently deprecated and its use is discouraged; one should configure it per-BTS
in OsmoBSC VTY instead.
=== Configuring Network Assisted Cell Change (NACC)
Network Assisted Cell Change, defined in 3GPP TS 44.060 sub-clause 8.8, is a
feature providing the MS aid when changing to a new cell due to autonomous
reselection. In summary, the MS informs the current cell its intention to change
to a new target cell, and the network decides whether to grant the intended cell
change or order a change to another neighbor cell. It also provides several
System Informations of the target cell to the MS to allow for quicker
reselection towards it.
OsmoPCU will automatically provide the required neighbor System Information when
the MS requests NACC towards a target cell also under the management of the same
OsmoPCU instance, since it already has the System Information of all BTS under
their control, obtained through PCUIF when the BTS registers against OsmoPCU, so
no specific user configuration is required here.
In general, OsmoPCU requires to gather the information from somewhere else
before being able to provide it to the MS requesting the NACC.
If OsmoPCU fails to gather the System Information, it will simply answer the MS
allowing the proposed changed but without previously providing the System
Information of the target cell.
==== Neighbor Address Resolution
First of all, it needs to translate the <ARFCN + BSIC> identity of the target
cell to change to, provided by the MS, into an identity that the Core Network
can use and understand to identify the target cell, which happens to be a key
composed of <RAI + Cell Identity>. This key is also named conveniently as
CGI-PS, since it actually equals to the Circuit Switch CGI + RAC.
In order to apply this target cell identity translation, OsmoPCU uses the
OsmoBSC Neighbor Resolution Service. This service is nowadays provided by means
of PCUIF container messages, which are transparently forwarded in both directions
by the BTS using the IPA multiplex of the OML connection against the BSC. No
specific configuration is required in any of the involved nodes, they should
behave properly out of the box.
These neighbor address resolutions (<ARFCN + BSIC> => <RAI + CI>) are by default
cached for a while, in order to avoid querying the BSC frequently. As a result,
the resolution time is also optimized.
.Example: Configure Neighbor Resolution cache and timeouts
----
pcu
timer X1 500 <1>
timer X0 60 <2>
----
<1> Time out if the BSC doesn't answer our resolution request after 500 ms
<2> Keep resolved neighbor addresses cached for 60 seconds
===== OsmoBSC CTRL interface (deprecated)
CAUTION: This interface is nowadays considered deprecated and should not be used
anymore. Any related VTY options should be dropped from configuration files, to
let OsmoPCU use the new interface instead. This section is kept here for a while
as a reference for old deployments using old versions of the programs.
This Neighbor Address Resolution Service was initially implemented by means of a
separate CTRL interface (see OsmoBSC User Manual), where OsmoPCU would create a
CTRL connection to the BSC each time an address resolution was required.
Older versions of OsmoBSC may not support the current Neighbor Address
Resolution Service over the IPA multiplex (see above). For those cases, OsmoPCU
can be configured to use the old deprecated CTRL interface.
By default, the use of this interface is not configured and hence disabled in
OsmoPCU. As a result, until configured, the network won't be able to provide the
System Information to the MS prior to allowing the change during NACC against
remote cells, which means the cell change will take longer to complete. In order
to configure the interface, the OsmoBSC IP address and port to connect to must
be configured in OsmoPCU VTY.
.Example: Configure Neighbor Resolution CTRL interface against OsmoBSC
----
pcu
neighbor resolution 172.18.13.10 4248 <1>
----
<1> Port 4248 is the default and hence could be omitted in this case
==== System Information Resolution
Once OsmoPCU gains knowledge of the target cell's address in the Core Network,
it can query its System Information.
OsmoPCU will gather the requested System Information of target cells under its
control without need for any external query, since the System Information of all
BTSs it manages are received over PCUIF and stored internally in OsmoPCU.
For those targets cells not managed by the OsmoPCU instance, the query is
accomplished by using RIM procedures (NACC RAN-INFO application) over the Gb
interface against the SGSN that OsmoPCU is connected to. In its turn, the SGSN
will potentially forward this query to the PCU serving the target cell, which
will provide back the System Information of that cell.
The System Information received from external PCUs over RIM are by default
cached for a while in order to avoid querying the SGSN frequently and, as a
result, optimizing the resolution time too.
.Example: Configure System Information resolution
----
pcu
timer X2 500 <1>
timer X11 60 <2>
----
<1> Time out if the SGSN doesn't answer our RIM RAN-INFO request request after 500 ms
<2> Keep resolved remote neighbor System Information cached for 60 seconds
=== GPRS vs EGPRS considerations
==== Configuration
OsmoPCU can be configured to either:
- Allocate only GPRS TBFs to all MS (no EGPRS)
- Allocate EGPRS TBFs to EGPRS capable phones while still falling back to
allocating GPRS TBFs on GPRS-only capable MS.
These two different modes of operation are selected by properly configuring the
Coding Schemes (see <<max_cs_mcs>>).
The first mode of operation (GPRS-only for all MS) can be accomplished
configuring OsmoPCU so that the resulting MCS set is empty. This can be done in
two ways:
- Announcing an empty MCS bit-mask over PCUIF to OsmoPCU:
That's actually done automatically by OsmoBSC on BTS with VTY config set to
`gprs mode gprs`.
- Configuring OsmoPCU to force an empty set by using VTY command `mcs max 0`.
Hence, if the resulting MCS bit-mask is not empty, (BSC configuring the BTS with
`gprs mode egprs` and OsmoPCU VTY containing something other than 'mcs max 0'),
EGPRS TBFs will be allocated for all MS announcing EGPRS capabilities.
It is important to remark that in order to use MCS5-9, the BTS must support
8-PSK modulation. Nevertheless, in case 8-PSK is not supported by the BTS, one
can still enable EGPRS and simply make sure 8-PSK MCS are never used by
configuring OsmoPCU with `mcs max 4 4`.
Similarly, a BTS may support 8-PSK modulation only on downlink, since it is
easier to implement than the uplink, together with the fact that higher downlink
throughput is usually more interesting from user point of view. In this
scenario, OsmoPCU can be configured to allow for full MCS range in downlink
while still preventing use of 8-PSK on the uplink: `mcs max 9 4`.
Some other interesting configurations to control use of EGPRS in the network
which lay outside OsmoPCU include:
- If `osmo-bts-trx` together with `osmo-trx` is used, remember to enable EGPRS
support (OsmoTRX VTY `egprs enable`).
- It is possible to improve EGPRS performance (in particular, the TBF
establishment timing) a bit by enabling 11-bit Access Burst support. This
allows EGPRS capable phones to indicate their EGPRS capability, establishment
cause, and multi-slot class directly in the Access Burst (OsmoTRX VTY
`ext-rach enable`, OsmoBSC VTY `gprs egprs-packet-channel-request`).
NOTE: If you enable MCS5-9 you will also need an 8-PSK capable OsmoBTS+PHY,
which means `osmo-bts-sysmo` or `osmo-bts-litecell15` with their associated PHY,
or `osmo-bts-trx` with `osmo-trx` properly configured.
==== GPRS+EGPRS multiplexing
Both EGPRS and GPRS-only capable MS can be driven concurrently in the same PDCH
timeslot by the PCU, hence no special configuration is required per timeslot
regarding this topic; OsmoPCU scheduler takes care of the specific requirements
when driving MS with different capabilities.
These specific requirements translate to some restrictions regarding which
Coding Schemes can be used at given frame numbers, and hence which kind of
RLCMAC blocks can be sent, which means serving a GPRS-only MS in a PDCH may end
up affecting slightly the downlink throughput of EGPRS capable MS.
Throughput loss based on MS capabilities with TBF attached to a certain PDCH
timeslot:
All UEs are EGPRS capable::
No throughput loss, since all data is sent using EGPRS, and EGPRS control
messages are used when appropriate.
All UEs are GPRS-only (doesn't support EGPRS)::
No throughput loss, since all data and control blocks use GPRS.
Some UEs are GPRS-only, some EGPRS::
In general EGPRS capable UEs will use EGPRS, and GPRS-only UEs will use GPRS,
with some restrictions affecting throughput of EGPRS capable UEs:
- Whenever a GPRS-only MS is to be polled to send uplink data to PCU, then a
downlink RLCMAC block modulated using GMSK must be sent, which means that if the
scheduler selects a EGPRS MS for downlink on that block it will force sending of
data with MCS1-4 (if it's new data, if it's a retransmission it cannot be
selected since MCS from original message cannot be changed). In the worst case
if no control block needs to be sent or no new data in MCS1-4 is available to
send, then an RLCMAC Dummy Block is sent.
- For synchronization purposes, each MS needs to receive an RLCMAC block which
it can fully decode at least every 360ms, which means the scheduler must enforce
a downlink block in CS1-4 every 360ms, that is, every 18th RLCMAC block. In
general this is not a big issue since anyway all control RLCMAC blocks are
encoded in CS1, so in case any control block is sent from time to time it's
accomplished and there's no penalty. However, if only EGPRS downlink data is sent
over that time frame, then the scheduler will force sending a RLCMAC Dummy
Block.
[[gsmtap]]
=== Configuring GSMTAP tracing
In addition to being able to obtain pcap protocol traces of the NS/BSSGP
communication and the text-based logging from the OsmoPCU software, there is
also the capability of tracing all communication on the radio interface related
to PS. To do so, OsmoPCU can encapsulate MAC blocks (23-155 byte messages at the
L2-L1 interface depending on coding scheme) into _GSMTAP_ and send them via
UDP/IP. At that point, they can be captured with utilities like *tcpdump* or
*tshark* for further analysis by the *wireshark* protocol analyzer.
In order to activate this feature, you first need to make sure to specify
the remote address of _GSMTAP_ host in the configuration file. In most
cases, using 127.0.0.1 for passing the messages over the loopback (`lo`)
device will be sufficient:
.Example: Enabling GSMTAP Um-frame logging to localhost
----
pcu
gsmtap-remote-host 127.0.0.1 <1>
----
<1> Destination address for _GSMTAP_ Um-frames
NOTE: Changing this parameter at run-time will not affect the existing
_GSMTAP_ connection, full program restart is required.
NOTE: Command line parameters `-i` and `--gsmtap-ip` have been deprecated.
OsmoPCU can selectively trace such messages based on different categories, for
both Ul and Dl. For a complete list of cateogry values, please refer to the
_OsmoPCU VTY reference manual_ <<vty-ref-osmopcu>>.
For example, to enable GSMTAP tracing for all DL EGPRS rlcmac data blocks, you
can use the `gsmtap-category dl-data-egprs` command at the `pcu` node of the
OsmoPCU VTY.
.Example: Enabling GSMTAP for for all DL EGPRS rlcmac data blocks
----
OsmoPCU> enable
OsmoPCU# configure terminal
OsmoPCU(config)# pcu
OsmoPCU(pcu)# gsmtap-category dl-data-egprs
OsmoPCU(trx)# write <1>
----
<1> the `write` command will make the configuration persistent in the
configuration file. This is not required if you wish to enable GSMTAP
only in the current session of OsmoPCU.
De-activation can be performed similarly by using the `no gsmtap-category
dl-data-egprs` command at the `pcu` node of the OsmoPCU VTY.
It may be useful to enable all categories with a few exceptions, or vice versa
disable everything using one command. For this purpose, the VTY provides
`gsmtap-category enable-all` and `gsmtap-category disable-all` commands.
.Example: Enabling all categoriess except _dl-dummy_
----
pcu
gsmtap-category enable-all <1>
no gsmtap-category dl-dummy <2>
----
<1> Enable all available SAPIs
<2> Exclude DL RLCMAC blocks
From the moment they are enabled via VTY, GSMTAP messages will be
generated and sent in UDP encapsulation to the IANA-registered UDP port
for GSMTAP (4729) of the specified remote address.

View File

@@ -0,0 +1,4 @@
[[counters]]
== Counters
include::./counters_generated.adoc[]

View File

@@ -0,0 +1,208 @@
// autogenerated by show asciidoc counters
These counters and their description are based on OsmoPCU 0.9.0.244-de96 (OsmoPCU).
=== Rate Counters
// generating tables for rate_ctr_group
// rate_ctr_group table NSVC Peer Statistics
.ns:nsvc - NSVC Peer Statistics
[options="header"]
|===
| Name | Reference | Description
| packets:in | <<ns:nsvc_packets:in>> | Packets at NS Level ( In)
| packets:out | <<ns:nsvc_packets:out>> | Packets at NS Level (Out)
| packets:out:drop | <<ns:nsvc_packets:out:drop>> | Dropped Packets (Out)
| bytes:in | <<ns:nsvc_bytes:in>> | Bytes at NS Level ( In)
| bytes:out | <<ns:nsvc_bytes:out>> | Bytes at NS Level (Out)
| bytes:out:drop | <<ns:nsvc_bytes:out:drop>> | Dropped Bytes (Out)
| blocked | <<ns:nsvc_blocked>> | NS-VC Block count
| unblocked | <<ns:nsvc_unblocked>> | NS-VC Unblock count
| dead | <<ns:nsvc_dead>> | NS-VC gone dead count
| replaced | <<ns:nsvc_replaced>> | NS-VC replaced other count
| nsei-chg | <<ns:nsvc_nsei-chg>> | NS-VC changed NSEI count
| inv-nsvci | <<ns:nsvc_inv-nsvci>> | NS-VCI was invalid count
| inv-nsei | <<ns:nsvc_inv-nsei>> | NSEI was invalid count
| lost:alive | <<ns:nsvc_lost:alive>> | ALIVE ACK missing count
| lost:reset | <<ns:nsvc_lost:reset>> | RESET ACK missing count
|===
// rate_ctr_group table NSE Peer Statistics
.ns:nse - NSE Peer Statistics
[options="header"]
|===
| Name | Reference | Description
| packets:in | <<ns:nse_packets:in>> | Packets at NS Level ( In)
| packets:out | <<ns:nse_packets:out>> | Packets at NS Level (Out)
| packets:out:drop | <<ns:nse_packets:out:drop>> | Dropped Packets (Out)
| bytes:in | <<ns:nse_bytes:in>> | Bytes at NS Level ( In)
| bytes:out | <<ns:nse_bytes:out>> | Bytes at NS Level (Out)
| bytes:out:drop | <<ns:nse_bytes:out:drop>> | Dropped Bytes (Out)
| blocked | <<ns:nse_blocked>> | NS-VC Block count
| unblocked | <<ns:nse_unblocked>> | NS-VC Unblock count
| dead | <<ns:nse_dead>> | NS-VC gone dead count
| replaced | <<ns:nse_replaced>> | NS-VC replaced other count
| nsei-chg | <<ns:nse_nsei-chg>> | NS-VC changed NSEI count
| inv-nsvci | <<ns:nse_inv-nsvci>> | NS-VCI was invalid count
| inv-nsei | <<ns:nse_inv-nsei>> | NSEI was invalid count
| lost:alive | <<ns:nse_lost:alive>> | ALIVE ACK missing count
| lost:reset | <<ns:nse_lost:reset>> | RESET ACK missing count
|===
// rate_ctr_group table SGSN Statistics
.pcu:sgsn - SGSN Statistics
[options="header"]
|===
| Name | Reference | Description
| rx_paging_cs | <<pcu:sgsn_rx_paging_cs>> | Amount of paging CS requests received
| rx_paging_ps | <<pcu:sgsn_rx_paging_ps>> | Amount of paging PS requests received
|===
// rate_ctr_group table BSSGP Peer Statistics
.bssgp:bss_ctx - BSSGP Peer Statistics
[options="header"]
|===
| Name | Reference | Description
| packets:in | <<bssgp:bss_ctx_packets:in>> | Packets at BSSGP Level ( In)
| packets:out | <<bssgp:bss_ctx_packets:out>> | Packets at BSSGP Level (Out)
| bytes:in | <<bssgp:bss_ctx_bytes:in>> | Bytes at BSSGP Level ( In)
| bytes:out | <<bssgp:bss_ctx_bytes:out>> | Bytes at BSSGP Level (Out)
| blocked | <<bssgp:bss_ctx_blocked>> | BVC Blocking count
| discarded | <<bssgp:bss_ctx_discarded>> | BVC LLC Discarded count
| status | <<bssgp:bss_ctx_status>> | BVC Status count
|===
// rate_ctr_group table BTS Statistics
.bts - BTS Statistics
[options="header"]
|===
| Name | Reference | Description
| tbf:dl:alloc | <<bts_tbf:dl:alloc>> | TBF DL Allocated
| tbf:dl:freed | <<bts_tbf:dl:freed>> | TBF DL Freed
| tbf:dl:aborted | <<bts_tbf:dl:aborted>> | TBF DL Aborted
| tbf:ul:alloc | <<bts_tbf:ul:alloc>> | TBF UL Allocated
| tbf:ul:freed | <<bts_tbf:ul:freed>> | TBF UL Freed
| tbf:ul:aborted | <<bts_tbf:ul:aborted>> | TBF UL Aborted
| tbf:reused | <<bts_tbf:reused>> | TBF Reused
| tbf:alloc:algo-a | <<bts_tbf:alloc:algo-a>> | TBF Alloc Algo A
| tbf:alloc:algo-b | <<bts_tbf:alloc:algo-b>> | TBF Alloc Algo B
| tbf:alloc:failed | <<bts_tbf:alloc:failed>> | TBF Alloc Failure (any reason)
| tbf:alloc:failed:no_tfi | <<bts_tbf:alloc:failed:no_tfi>> | TBF Alloc Failure (TFIs exhausted)
| tbf:alloc:failed:no_usf | <<bts_tbf:alloc:failed:no_usf>> | TBF Alloc Failure (USFs exhausted)
| tbf:alloc:failed:no_slot_combi | <<bts_tbf:alloc:failed:no_slot_combi>> | TBF Alloc Failure (No valid UL/DL slot combination found)
| tbf:alloc:failed:no_slot_avail | <<bts_tbf:alloc:failed:no_slot_avail>> | TBF Alloc Failure (No slot available)
| rlc:sent | <<bts_rlc:sent>> | RLC Sent
| rlc:resent | <<bts_rlc:resent>> | RLC Resent
| rlc:restarted | <<bts_rlc:restarted>> | RLC Restarted
| rlc:stalled | <<bts_rlc:stalled>> | RLC Stalled
| rlc:nacked | <<bts_rlc:nacked>> | RLC Nacked
| rlc:final_block_resent | <<bts_rlc:final_block_resent>> | RLC Final Blk resent
| rlc:ass:timedout | <<bts_rlc:ass:timedout>> | RLC Assign Timeout
| rlc:ass:failed | <<bts_rlc:ass:failed>> | RLC Assign Failed
| rlc:ack:timedout | <<bts_rlc:ack:timedout>> | RLC Ack Timeout
| rlc:ack:failed | <<bts_rlc:ack:failed>> | RLC Ack Failed
| rlc:rel:timedout | <<bts_rlc:rel:timedout>> | RLC Release Timeout
| rlc:late-block | <<bts_rlc:late-block>> | RLC Late Block
| rlc:sent-dummy | <<bts_rlc:sent-dummy>> | RLC Sent Dummy
| rlc:sent-control | <<bts_rlc:sent-control>> | RLC Sent Control
| rlc:dl_bytes | <<bts_rlc:dl_bytes>> | RLC DL Bytes
| rlc:dl_payload_bytes | <<bts_rlc:dl_payload_bytes>> | RLC DL Payload Bytes
| rlc:ul_bytes | <<bts_rlc:ul_bytes>> | RLC UL Bytes
| rlc:ul_payload_bytes | <<bts_rlc:ul_payload_bytes>> | RLC UL Payload Bytes
| decode:errors | <<bts_decode:errors>> | Decode Errors
| sba:allocated | <<bts_sba:allocated>> | SBA Allocated
| sba:freed | <<bts_sba:freed>> | SBA Freed
| sba:timedout | <<bts_sba:timedout>> | SBA Timeout
| llc:timeout | <<bts_llc:timeout>> | Timedout Frames
| llc:dropped | <<bts_llc:dropped>> | Dropped Frames
| llc:scheduled | <<bts_llc:scheduled>> | Scheduled Frames
| llc:dl_bytes | <<bts_llc:dl_bytes>> | RLC encapsulated PDUs
| llc:ul_bytes | <<bts_llc:ul_bytes>> | full PDUs received
| pch:requests | <<bts_pch:requests>> | PCH requests sent
| pch:requests:timeout | <<bts_pch:requests:timeout>> | PCH requests timeout
| rach:requests | <<bts_rach:requests>> | RACH requests received
| rach:requests:11bit | <<bts_rach:requests:11bit>> | 11BIT_RACH requests received
| rach:requests:one_phase | <<bts_rach:requests:one_phase>> | One phase packet access with request for single TS UL
| rach:requests:two_phase | <<bts_rach:requests:two_phase>> | Single block packet request for two phase packet access
| rach:requests:unexpected | <<bts_rach:requests:unexpected>> | RACH Request with unexpected content received
| spb:uplink_first_segment | <<bts_spb:uplink_first_segment>> | First seg of UL SPB
| spb:uplink_second_segment | <<bts_spb:uplink_second_segment>> | Second seg of UL SPB
| spb:downlink_first_segment | <<bts_spb:downlink_first_segment>> | First seg of DL SPB
| spb:downlink_second_segment | <<bts_spb:downlink_second_segment>> | Second seg of DL SPB
| immediate:assignment_UL | <<bts_immediate:assignment_UL>> | Immediate Assign UL
| immediate:assignment_ul:one_phase | <<bts_immediate:assignment_ul:one_phase>> | Immediate Assign UL (one phase packet access)
| immediate:assignment_ul:two_phase | <<bts_immediate:assignment_ul:two_phase>> | Immediate Assign UL (two phase packet access)
| immediate:assignment_ul:contention_resolution_success | <<bts_immediate:assignment_ul:contention_resolution_success>> | First RLC Block (PDU) on the PDTCH from the MS received
| immediate:assignment_rej | <<bts_immediate:assignment_rej>> | Immediate Assign Rej
| immediate:assignment_DL | <<bts_immediate:assignment_DL>> | Immediate Assign DL
| channel:request_description | <<bts_channel:request_description>> | Channel Request Desc
| pkt:ul_assignment | <<bts_pkt:ul_assignment>> | Packet UL Assignment
| pkt:access_reject | <<bts_pkt:access_reject>> | Packet Access Reject
| pkt:dl_assignment | <<bts_pkt:dl_assignment>> | Packet DL Assignment
| pkt:cell_chg_notification | <<bts_pkt:cell_chg_notification>> | Packet Cell Change Notification
| pkt:cell_chg_continue | <<bts_pkt:cell_chg_continue>> | Packet Cell Change Continue
| pkt:neigh_cell_data | <<bts_pkt:neigh_cell_data>> | Packet Neighbour Cell Data
| ul:control | <<bts_ul:control>> | UL control Block
| ul:assignment_poll_timeout | <<bts_ul:assignment_poll_timeout>> | UL Assign Timeout
| ul:assignment_failed | <<bts_ul:assignment_failed>> | UL Assign Failed
| dl:assignment_timeout | <<bts_dl:assignment_timeout>> | DL Assign Timeout
| dl:assignment_failed | <<bts_dl:assignment_failed>> | DL Assign Failed
| pkt:ul_ack_nack_timeout | <<bts_pkt:ul_ack_nack_timeout>> | PUAN Poll Timeout
| pkt:ul_ack_nack_failed | <<bts_pkt:ul_ack_nack_failed>> | PUAN poll Failed
| pkt:dl_ack_nack_timeout | <<bts_pkt:dl_ack_nack_timeout>> | PDAN poll Timeout
| pkt:dl_ack_nack_failed | <<bts_pkt:dl_ack_nack_failed>> | PDAN poll Failed
| gprs:downlink_cs1 | <<bts_gprs:downlink_cs1>> | CS1 downlink
| gprs:downlink_cs2 | <<bts_gprs:downlink_cs2>> | CS2 downlink
| gprs:downlink_cs3 | <<bts_gprs:downlink_cs3>> | CS3 downlink
| gprs:downlink_cs4 | <<bts_gprs:downlink_cs4>> | CS4 downlink
| egprs:downlink_mcs1 | <<bts_egprs:downlink_mcs1>> | MCS1 downlink
| egprs:downlink_mcs2 | <<bts_egprs:downlink_mcs2>> | MCS2 downlink
| egprs:downlink_mcs3 | <<bts_egprs:downlink_mcs3>> | MCS3 downlink
| egprs:downlink_mcs4 | <<bts_egprs:downlink_mcs4>> | MCS4 downlink
| egprs:downlink_mcs5 | <<bts_egprs:downlink_mcs5>> | MCS5 downlink
| egprs:downlink_mcs6 | <<bts_egprs:downlink_mcs6>> | MCS6 downlink
| egprs:downlink_mcs7 | <<bts_egprs:downlink_mcs7>> | MCS7 downlink
| egprs:downlink_mcs8 | <<bts_egprs:downlink_mcs8>> | MCS8 downlink
| egprs:downlink_mcs9 | <<bts_egprs:downlink_mcs9>> | MCS9 downlink
| gprs:uplink_cs1 | <<bts_gprs:uplink_cs1>> | CS1 Uplink
| gprs:uplink_cs2 | <<bts_gprs:uplink_cs2>> | CS2 Uplink
| gprs:uplink_cs3 | <<bts_gprs:uplink_cs3>> | CS3 Uplink
| gprs:uplink_cs4 | <<bts_gprs:uplink_cs4>> | CS4 Uplink
| egprs:uplink_mcs1 | <<bts_egprs:uplink_mcs1>> | MCS1 Uplink
| egprs:uplink_mcs2 | <<bts_egprs:uplink_mcs2>> | MCS2 Uplink
| egprs:uplink_mcs3 | <<bts_egprs:uplink_mcs3>> | MCS3 Uplink
| egprs:uplink_mcs4 | <<bts_egprs:uplink_mcs4>> | MCS4 Uplink
| egprs:uplink_mcs5 | <<bts_egprs:uplink_mcs5>> | MCS5 Uplink
| egprs:uplink_mcs6 | <<bts_egprs:uplink_mcs6>> | MCS6 Uplink
| egprs:uplink_mcs7 | <<bts_egprs:uplink_mcs7>> | MCS7 Uplink
| egprs:uplink_mcs8 | <<bts_egprs:uplink_mcs8>> | MCS8 Uplink
| egprs:uplink_mcs9 | <<bts_egprs:uplink_mcs9>> | MCS9 Uplink
|===
=== Osmo Stat Items
// generating tables for osmo_stat_items
NSVC Peer Statistics
// osmo_stat_item_group table NSVC Peer Statistics
.ns.nsvc - NSVC Peer Statistics
[options="header"]
|===
| Name | Reference | Description | Unit
| alive.delay | <<ns.nsvc_alive.delay>> | ALIVE response time | ms
|===
NS Bind Statistics
// osmo_stat_item_group table NS Bind Statistics
.ns.bind - NS Bind Statistics
[options="header"]
|===
| Name | Reference | Description | Unit
| tx_backlog_length | <<ns.bind_tx_backlog_length>> | Transmit backlog length | packets
|===
BTS Statistics
// osmo_stat_item_group table BTS Statistics
.bts - BTS Statistics
[options="header"]
|===
| Name | Reference | Description | Unit
| ms.present | <<bts_ms.present>> | MS Present |
| pdch.available | <<bts_pdch.available>> | PDCH available |
| pdch.occupied | <<bts_pdch.occupied>> | PDCH occupied (all) |
| pdch.occupied.gprs | <<bts_pdch.occupied.gprs>> | PDCH occupied (GPRS) |
| pdch.occupied.egprs | <<bts_pdch.occupied.egprs>> | PDCH occupied (EGPRS) |
|===
// there are no ungrouped osmo_counters

View File

@@ -0,0 +1,68 @@
== Overview
=== About OsmoPCU
OsmoPCU is the Osmocom implementation of the GPRS PCU (Packet Control
Unit) element inside the GPRS network.
The OsmoPCU is co-located within the BTS and connects to OsmoBTS via its
PCU socket interface.
On the other side, OsmoPCU is connected via the Gb interface to the
SGSN.
[[fig-gprs-pcubts]]
.GPRS network architecture with PCU in BTS
[graphviz]
----
digraph G {
rankdir=LR;
MS0 [label="MS"]
MS1 [label="MS"]
MS0->BTS [label="Um"]
MS1->BTS [label="Um"]
BTS->BSC [label="Abis"]
BSC->MSC [label="A"]
BTS->PCU [label="pcu_sock"]
PCU->SGSN [label="Gb"]
SGSN->GGSN [label="GTP"]
PCU [color=red]
}
----
=== Software Components
OsmoPCU consists of a variety of components, including
* Gb interface (NS/BSSGP protocol)
* `pcu_sock` interface towards OsmoBTS
* TBF management for uplink and downlink TBF
* RLC/MAC protocol implementation
* per-MS context for each MS currently served
* CSN.1 encoding/decoding routines
==== Gb Implementation
OsmoPCU implements the ETSI/3GPP specified Gb interface, including TS
08.16 (NS), TS 08.18 (BSSGP) protocols. As transport layer for NS, it
supports NS/IP (NS encapsulated in UDP/IP).
The actual Gb Implementation is part of the libosmogb library, which is
in turn part of the libosmocore software package. This allows the same
Gb implementation to be used from OsmoPCU, OsmoGbProxy as well as
OsmoSGSN.
==== `pcu_sock` Interface to OsmoBTS
The interface towards OsmoBTS is called 'pcu_sock' and implemented as a
set of non-standardized primitives over a unix domain socket. The
default file system path for this socket is `/tmp/pcu_bts`.
The PCU socket can be changed on both OsmoBTS and OsmoPCU to a different
file/path name, primarily to permit running multiple independent BTS+PCU
pairs on a single Linux machine without having to use filesystem
namespaces or other complex configurations.
NOTE: If you change the PCU socket path on OsmoBTS by means of the
`pcu-socket` VTY configuration command, you must ensure to make the
identical change on the OsmoPCU side.

View File

@@ -0,0 +1,44 @@
==== Full example of QoS for osmo-pcu uplink QoS
In the below example we will show the full set of configuration required
for both DSCP and PCP differentiation of uplink Gb traffic by osmo-pcu.
What we want to achieve in this example is the following configuration:
.DSCP and PCP assignments for osmo-bts uplink traffic in this example
[options="header",width="30%",cols="2,1,1"]
|===
|Traffic |DSCP|PCP
|Gb (NS) | 10| 1
|===
. configure the osmocom program to set the DSCP value
* osmo-pcu.cfg: `dscp 10` in `udp bind` vty node
. configure an egrees QoS map to map from priority to PCP
.Example Step 1: add related VTY configuration to `osmo-pcu.cfg`
----
...
pcu
gb ip-dscp 10
gb socket-priority 1
...
----
.Example Step 2: egress QoS map to map from DSCP values to priority values
----
$ sudo ip link set dev eth0.9<1> type vlan egress-qos-map 0:0 1:1 5:5 6:6 7:7 <2>
----
<1> make sure to specify your specific VLAN interface name here instead of `eth0.9`.
<2> create a egress QoS map that maps the priority value 1:1 to the PCP. We also
include the mappings for 5, 6, and 7 from the osmo-bts example here (see
<<userman-osmobts>>).
NOTE:: The settings of the `ip` command are volatile and only active until
the next reboot (or the network device or VLAN is removed). Please refer to
the documentation of your specific Linux distribution in order to find out how
to make such settings persistent by means of an `ifup` hook whenever the interface
comes up. For CentOS/RHEL 8 this can e.g. be achieved by means of an `/sbin/ifup-local
script` (when using `network-scripts` and not NetworkManager). For Debian or Ubuntu,
this typically involves adding `up` lines to `/etc/network/interfaces` or a `/etc/network/if-up.d`
script.

View File

@@ -0,0 +1,27 @@
== Running OsmoPCU
The OsmoPCU executable (`osmo-pcu`) offers the following command-line
options:
=== SYNOPSIS
*osmo-pcu* [-h|-V] [-D] [-c 'CONFIGFILE'] [-r 'PRIO'] [-m 'MCC'] [-n 'MNC'] [-i A.B.C.D]
=== OPTIONS
*-h, --help*::
Print a short help message about the supported options
*-V, --version*::
Print the compile-time version number of the program
*-D, --daemonize*::
Fork the process as a daemon into background.
*-c, --config-file 'CONFIGFILE'*::
Specify the file and path name of the configuration file to be
used. If none is specified, use `osmo-pcu.cfg` in the current
working directory.
*-m, --mcc 'MCC'*::
Use the given MCC instead of that provided by BTS via PCU socket
*-n, --mnc 'MNC'*::
Use the given MNC instead of that provided by BTS via PCU socket

501
doc/manuals/gb/bssgp.adoc Normal file
View File

@@ -0,0 +1,501 @@
[[bssgp]]
== BSS GPRS Protocol (BSSGP)
=== List of Messages
The following tables list the BSSGP messages used by OsmoPCU, grouped
by their level of compliance with 3GPP TS 48.018.
==== Messages Compliant With TS 48.018
.Messages compliant with TS 48.018
[options="header",cols="10%,10%,20%,35%,5%,20%"]
|===
| TS 48.018 § | type code (hex) | This document § | Message | <-/-> | Received/Sent by OsmoPCU
6+<| *RL and BSSGP SAP Messages:*
| 10.2.1 | 0x00 | <<dl_unit_data>> | DL-UNITDATA | <- | Received
| 10.2.2 | 0x01 | <<ul_unit_data>> | UL-UNITDATA | -> | Sent
| 10.2.3 | 0x02 | <<ra_capab>> | RA-CAPABILITY | <- | Received
6+<| *GMM SAP Messages:*
| 10.3.1 | 0x06 | <<paging_ps>> | PAGING PS | <- | Received
| 10.3.2 | 0x07 | <<paging_cs>> | PAGING CS | <- | Received
| 10.3.7 | 0x0c | <<susp_ack>> | SUSPEND-ACK | <- | Received
| 10.3.8 | 0x0d | <<susp_nack>> | SUSPEND-NACK | <- | Received
| 10.3.10 | 0x0f | <<res_ack>> | RESUME-ACK | <- | Received
| 10.3.11 | 0x10 | <<res_nack>> | RESUME-NACK | <- | Received
6+<| *NM SAP Messages:*
| 10.4.9 | 0x21 | <<block_ack>> | BVC-BLOCK-ACK | <- | Received
| 10.4.12 | 0x22 | <<bvc_reset>> | BVC-RESET | <-/-> | Received/Sent
| 10.4.13 | 0x23 | <<reset_ack>> | BVC-RESET-ACK | <- | Received
| 10.4.10 | 0x24 | <<bvc_unblock>> | BVC-UNBLOCK | -> | Sent
| 10.4.11 | 0x25 | <<unblock_ack>> | BVC-UNBLOCK-ACK | <- | Received
| 10.4.4 | 0x26 | <<flow_bvc>> | FLOW-CONTROL-BVC | -> | Sent
| 10.4.5 | 0x27 | <<flow_bvc_ack>> | FLOW-CONTROL-BVC-ACK | <- | Received
| 10.4.7 | 0x29 | <<flow_ms_ack>> | FLOW-CONTROL-MS-ACK | <- | Received
| 10.4.1 | 0x2a | <<flush_ll>> | FLUSH-LL | <- | Received
| 10.4.15 | 0x40 | <<invoke_trace>> | SGSN-INVOKE-TRACE | <- | Received
| 10.4.14 | 0x41 | <<bssgp_status>> | STATUS | <-/-> | Received/Sent
|===
==== Messages Specific to OsmoPCU
There are no OsmoPCU specific BSSGP messages.
[[not_impl]]
==== Messages Not Implemented by OsmoPCU
.3GPP TS 48.018 messages not implemented by OsmoPCU
[options="header",cols="10%,10%,80%"]
|===
| TS 48.018 § | type code (hex) | Message
3+<| *RL (relay) and BSSGP SAP Messages:*
| 10.2.4 | 0x03 | PTM-UNITDATA
3+<| *GMM (GPRS mobility management) SAP Messages:*
| 10.3.3 | 0x08 | RA-CAPABILITY-UPDATE
| 10.3.4 | 0x09 | RA-CAPABILITY-UPDATE-ACK
| 10.3.5 | 0x0a | RADIO-STATUS
| 10.3.6 | 0x0b | SUSPEND
| 10.3.9 | 0x0e | RESUME
3+<| *NM (network management) SAP Messages:*
| 10.4.8 | 0x20 | BVC-BLOCK
| 10.4.6 | 0x28 | FLOW-CONTROL-MS
| 10.4.2 | 0x2b | FLUSH-LL-ACK
| 10.4.3 | 0x2c | LLC-DISCARDED
3+<| *PFM (packet flow management) SAP Messages:*
| 10.4.16 | 0x50 | DOWNLOAD-BSS-PFC
| 10.4.17 | 0x51 | CREATE-BSS-PFC
| 10.4.18 | 0x52 | CREATE-BSS-PFC-ACK
| 10.4.19 | 0x53 | CREATE-BSS-PFC-NACK
| 10.4.20 | 0x54 | MODIFY-BSS-PFC
| 10.4.21 | 0x55 | MODIFY-BSS-PFC-ACK
| 10.4.22 | 0x56 | DELETE-BSS-PFC
| 10.4.23 | 0x57 | DELETE-BSS-PFC-ACK
|===
=== Details on Compliant BSSGP Messages
[[dl_unit_data]]
==== DL-UNITDATA
This message conforms to 3GPP TS 48.018 § 10.2.1, with the following
limitations:
* OsmoPCU does not support QoS
* all optional IEs except for IMSI and old TLLI are ignored.
._DL-UNITDATA_ IE limitations
[options="header",cols="10%,30%,60%"]
|===
| TS 48.018 § | IE Name | Handling
| 11.3.28 | QoS Profile | _ignored_
| 11.3.22 | MS Radio Access Capability | _ignored_
| 11.3.27 | Priority | _ignored_
| 11.3.11 | DRX Parameters | _ignored_
| 1.3.42 | PFI | _ignored_
| 11.3.19 | LSA Information | _ignored_
| 11.3.47 | Service UTRAN CCO | _ignored_
|===
[[ul_unit_data]]
==== UL-UNITDATA
This message conforms to 3GPP TS 48.018 § 10.2.2, with the following limitations:
* OsmoPCU does not send optional IEs - PFI (§ 12.3.42) and LSA
Identifier List (§ 11.3.18).
* QoS Profile (§ 11.3.28) IE is always set to 0x04.
[[ra_capab]]
==== RA-CAPABILITY
This message is received and logged but ignored by OsmoPCU at the moment.
[[paging_ps]]
==== PAGING PS
This message conforms to 3GPP TS 48.018 § 10.3.1, with the following
limitations:
* only IMSI and P-TMSI are parsed by OsmoPCU.
._DL-UNITDATA_ IE limitations
[options="header",cols="10%,30%,60%"]
|===
| TS 48.018 § | IE Name | Handling
| 11.3.11 | DRX Parameters | _ignored_
| 11.3.6 | BVCI | _ignored_
| 11.3.17 | Location Are | _ignored_
| 11.3.31 | Routeing Area | _ignored_
| 11.3.3 | BSS Area Indication | _ignored_
| 11.3.42 | PFI | _ignored_
| 11.3.43 | ABQP | _ignored_
| 11.3.28 | QoS Profile | _ignored_
| 11.3.36 | P-TMSI | treated as mandatory (in case of absence paging with 0-length P-TMSI will be sent)
|===
[[paging_cs]]
==== PAGING CS
This message is received and logged but ignored by OsmoPCU at the
moment.
[[susp_ack]]
==== SUSPEND-ACK
This message is received and logged but ignored by OsmoPCU at the
moment.
[[susp_nack]]
==== SUSPEND-NACK
This message is received and logged but ignored by OsmoPCU at the
moment.
[[res_ack]]
==== RESUME-ACK
This message is received and logged but ignored by OsmoPCU at the
moment.
[[res_nack]]
==== RESUME-NACK
This message is received and logged but ignored by OsmoPCU at the
moment.
[[block_ack]]
==== BVC-BLOCK-ACK
This message is received and logged but ignored by OsmoPCU at the
moment.
[[bvc_reset]]
==== BVC-RESET
OsmoPCU never transmits optional Feature bitmap (3GPP TS 48.018 §
11.3.40) IE.
Receiving BVC RESET will cause OsmoPCU to respond with "Unknown BVCI"
status message.
[[reset_ack]]
==== BVC-RESET-ACK
This message conforms to 3GPP TS 48.018 § 10.4.13.
After receiving it OsmoPCU completes the RESET procedure for BVC
according to 3GPP TS 48.018 § 8.4.
[[unblock_ack]]
==== BVC-UNBLOCK-ACK
This message conforms to 3GPP TS 48.018 § 10.4.11.
After receiving it OsmoPCU completes the RESET procedure for BVC
according to 3GPP TS 48.018 § 8.3.
[[bvc_unblock]]
==== BVC-UNBLOCK
This message conforms to 3GPP TS 48.018 § 10.4.10 and is send by
OsmoPCU as part of UNBLOCK procedure described in 3GPP TS 48.018 § 8.3.
[[flow_ms_ack]]
==== FLOW-CONTROL-MS-ACK
This message is received and logged but ignored by OsmoPCU at the
moment.
[[flow_bvc_ack]]
==== FLOW-CONTROL-BVC-ACK
This message is received and logged but ignored by OsmoPCU at the
moment.
[[flow_bvc]]
==== FLOW-CONTROL-BVC
This message conforms to 3GPP TS 48.018 § 10.4.4, with the following
limitations:
* OsmoPCU does not support Current Bucket Level (CBL) feature so
Bucket_Full Ratio (TS 48.018 § 11.3.46) IE is not transmitted as part
of this message.
[[flush_ll]]
==== FLUSH-LL
This message is received and logged but ignored by OsmoPCU at the
moment.
[[invoke_trace]]
==== SGSN-INVOKE-TRACE
This message is received and logged but ignored by OsmoPCU at the
moment.
[[bssgp_status]]
==== STATUS
This message conforms to 3GPP TS 48.018 § 10.4.14.
=== Information Elements Overview
All of the IEs handled by OsmoPCU are listed below, with limitations
and additions to 3GPP TS 48.018 specified in more detail.
==== IEs Conforming to 3GPP TS 48.018
The following Information Elements are accepted by OsmoPCU. Not all
IEs are actually evaluated.
.IEs conforming to 3GPP TS 48.018
[options="header",cols="5%,10%,40%,5%,40%"]
|===
| tag (hex) | TS 48.018 § | IE name | <-/-> | Received/Sent by OsmoPCU
| 0x00 | 11.3.1 | Alignment Octets | <-/-> | Received/Sent
| 0x01 | 11.3.2 | Bmax default MS | -> | Sent
| 0x02 | 11.3.3 | BSS Area Indication | <- | Received
| 0x03 | 11.3.4 | Bucket Leak Rate | -> | Sent
| 0x04 | 11.3.6 | BVCI | <-/-> | Received/Sent
| 0x05 | 11.3.5 | BVC Bucket Size | -> | Sent
| 0x06 | 11.3.7 | BVC Measurement | -> | Sent
| 0x07 | 11.3.8 | Cause | <-/-> | Received/Sent
| 0x08 | 11.3.9 | Cell Identifier | -> | Sent
| 0x09 | 11.3.10 | Channel needed | <- | Received
| 0x0a | 11.3.11 | DRX Parameters | <- | Received
| 0x0b | 11.3.12 | eMLPP-Priority | <- | Received
| 0x0c | 11.3.13 | Flush Action | <- | Received
| 0x0d | 11.3.14 | IMSI | <-/-> | Received/Sent
| 0x0e | 11.3.15 | LLC-PDU | <-/-> | Received/Sent
| 0x0f | 11.3.16 | LLC Frames Discarded | -> | Sent
| 0x10 | 11.3.17 | Location Area | <- | Received
| 0x11 | 11.3.20 | Mobile Id | <- | Received
| 0x12 | 11.3.21 | MS Bucket Size | -> | Sent
| 0x13 | 11.3.22 | MS Radio Access Capability | <- | Received
| 0x14 | 11.3.23 | OMC Id | <- | Received
| 0x15 | 11.3.24 | PDU In Error | <-/-> | Received/Sent
| 0x16 | 11.3.25 | PDU Lifetime | <- | Received
| 0x17 | 11.3.27 | Priority | <- | Received
| 0x19 | 11.3.29 | Radio Cause | -> | Sent
| 0x1a | 11.3.30 | RA-Cap-UPD-Cause | -> | Sent
| 0x1b | 11.3.31 | Routeing Area | <-/-> | Received/Sent
| 0x1c | 11.3.32 | R_default_MS | -> | Sent
| 0x1d | 11.3.33 | Suspend Reference Number | <-/-> | Received/Sent
| 0x1e | 11.3.34 | Tag | <-/-> | Received/Sent
| 0x1f | 11.3.35 | TLLI | <-/-> | Received/Sent
| 0x20 | 11.3.36 | TMSI | <-/-> | Received/Sent
| 0x21 | 11.3.37 | Trace Reference | <- | Received
| 0x22 | 11.3.38 | Trace Type | <- | Received
| 0x23 | 11.3.39 | TransactionId | <- | Received
| 0x24 | 11.3.40 | Trigger Id | <- | Received
| 0x25 | 11.3.41 | Number of octets affected | -> | Sent
| 0x26 | 11.3.18 | LSA Identifier List | -> | Sent
| 0x27 | 11.3.19 | LSA Information | <- | Received
| 0x28 | 11.3.42 | Packet Flow Identifier | <-/-> | Received/Sent
| 0x3a | 11.3.43 | Aggregate BSS QoS Profile (ABQP) | <-/-> | Received/Sent
| 0x3b | 11.3.45 | Feature Bitmap | <-/-> | Received/Sent
| 0x3c | 11.3.46 | Bucket_Full Ratio | -> | Sent
| 0x3d | 11.3.47 | Service UTRAN CCO (Cell Change Order) | <- | Received
|===
==== IEs Not Conforming to 3GPP TS 48.018
.IEs not conforming to 3GPP TS 48.018
[options="header",cols="5%,10%,30%,55%"]
|===
| tag (hex) | TS 48.018 § | IE name | Description
| 0x18 | 11.3.28 | QoS Profile | Received value is ignored. Sent value is hard-coded to 0x4 (3 octets).
|===
==== Additional Attributes and Parameters
There are no OsmoPCU specific additional Attributes and Parameters.
=== Details on IEs
==== BSS Area Indication
This IE is ignored by OsmoPCU.
==== Bucket Leak Rate
The value used by OsmoPCU for this IE can be set through configuration
file or vty via "flow-control force-ms-leak-rate <1-6553500>" command.
==== BVC Bucket Size
The value used by OsmoPCU for this IE can be set through configuration file or vty via
"flow-control force-bvc-bucket-size <1-6553500>" command.
==== Channel needed
This IE is ignored because entire message which contains it is ignored
by OsmoPCU - see <<paging_cs>> for details.
==== DRX Parameters
This IE is ignored by OsmoPCU.
==== eMLPP-Priority
This IE is ignored because entire message which contains it is ignored
by OsmoPCU - see <<paging_cs>> for details.
==== Flush Action
This IE is ignored because entire message which contains it is ignored
by OsmoPCU - see <<flush_ll>> for details.
==== LLC Frames Discarded
This IE is not available because entire message which contains it
(LLC-DISCARDED) is not implemented by OsmoPCU - see for <<not_impl>>
details.
==== Location Area
This IE is ignored by OsmoPCU.
==== Mobile Id
This IE is ignored because entire message which contains it is ignored
by OsmoPCU - see <<invoke_trace>> for details.
==== MS Bucket Size
The value used by OsmoPCU for this IE can be set through configuration
file or vty via "flow-control force-ms-bucket-size <1-6553500>"
command.
==== MS Radio Access Capability
This IE is ignored by OsmoPCU.
==== OMC Id
This IE is ignored because entire message which contains it is ignored
by OsmoPCU - see <<invoke_trace>> for details.
==== Priority
This IE is ignored by OsmoPCU.
==== QoS Profile
No QoS is supported by OsmoPCU so this IE is ignored or safe default
used when mandatory.
==== Radio Cause
This IE is not available because entire message which contains it
(RADIO-STATUS) is not implemented by OsmoPCU - see for <<not_impl>>
details.
==== RA-Cap-UPD-Cause
This IE is not available because entire message which contains it
(RA-CAPABILITY-UPDATE-ACK) is not implemented by OsmoPCU - see for
<<not_impl>> details.
==== Routeing Area
This IE is ignored by OsmoPCU upon receiving.
The messages which might require this IE to be send are not
implemented by OsmoPCU - see for <<not_impl>> details.
==== Suspend Reference Number
This IE is ignored by OsmoPCU upon receiving.
The messages which might require this IE to be send are not
implemented by OsmoPCU - see for <<not_impl>> details.
==== Tag
This IE currently only used by OsmoPCU for Flow Control procedure (TS
48.018 § 8.2). In other cases it's either ignored or unavailable.
==== Trace Reference
This IE is ignored because entire message which contains it is ignored
by OsmoPCU - see <<invoke_trace>> for details.
==== Trace Type
This IE is ignored because entire message which contains it is ignored
by OsmoPCU - see <<invoke_trace>> for details.
==== TransactionId
This IE is ignored because entire message which contains it is ignored
by OsmoPCU - see <<invoke_trace>> for details.
==== Trigger Id
This IE is ignored because entire message which contains it is ignored
by OsmoPCU - see <<invoke_trace>> for details.
==== Number of octets affected
This IE is not available because the messages which contains it
(FLUSH-LL-ACK and LLC-DISCARDE) are not implemented by OsmoPCU - see
for <<not_impl>> details.
==== LSA Information
This IE is ignored by OsmoPCU.
==== LSA Identifier List
This IE is not implemented by OsmoPCU.
==== Packet Flow Identifier
This IE is ignored by OsmoPCU upon receiving.
The messages which might require this IE to be send are not
implemented by OsmoPCU - see for <<not_impl>> details.
==== Aggregate BSS QoS Profile (ABQP)
This IE is ignored by OsmoPCU upon receiving.
The messages which might require this IE to be send are not
implemented by OsmoPCU - see for <<not_impl>> details.
==== Feature Bitmap
This IE is not implemented by OsmoPCU.
This IE is ignored by OsmoPCU when received.
Absence of Feature Bitmap automatically disables optional features for
Network Service Entity (NSE) communicating with OsmoPCU.
==== Bucket_Full Ratio
This IE is not implemented by OsmoPCU.
==== Service UTRAN CCO (Cell Change Order)
This IE is ignored by OsmoPCU.
=== Gb BSSGP Initialization / PCU bring-up
The BSSGP initialization directly follows NS connection establishment
described in <<ns_init>>.
OsmoPCU allocates a BVC context for the BVCI given by OsmoBTS, which
in turn receives it from OsmoBSC or OsmoNITB via OML procedures.
In addition to the BVCI identifying the OsmoPCU side of BSSGP
connection, there is also special BVCI which is accepted by OsmoPCU in
accordance with 3GPP TS 48.018 § 5.4.1: BVCI = 0 represents signaling data
between SGSN and PCU in contrast to PTP (Peer-To-Peer) user's data.
The mapping between BSSGP PDUs and signaling or PTP BVCIs is available
in 3GPP TS 48.018 Table 5.4.

View File

@@ -0,0 +1,27 @@
msc {
hscale="1.2";
bsc [label="BSC"], bts [label="BTS"], pcu [label="PCU"], sgsn [label="SGSN"];
|||;
bts box bsc [label="A-bis OML connection"];
bsc => bts [label="Set OML Attrbibutes (NSVC,CELL)"];
bts rbox pcu [label="PCU Unix Domain Socket"];
pcu => bts [label="connect to PCU socket"];
pcu <: bts [label="Config. parameters"];
pcu rbox pcu [label="bind/connect UDP socket"];
pcu note sgsn [label="NS-over-IP (UDP port 23000)"];
pcu => sgsn [label="NS RESET"];
pcu <= sgsn [label="NS RESET ACK"];
...;
pcu => sgsn [label="NS UNBLOCK"];
pcu <= sgsn [label="NS UNBLOCK ACK"];
pcu box sgsn [label="NS link established"];
...;
pcu => sgsn [label="BVC RESET"];
pcu <= sgsn [label="BVC RESET ACK"];
...;
pcu => sgsn [label="BVC UNBLOCK"];
pcu <= sgsn [label="BVC UNBLOCK ACK"];
pcu box sgsn [label="BSSGP link established"];
|||;
}

278
doc/manuals/gb/ns.adoc Normal file
View File

@@ -0,0 +1,278 @@
== Network Service (NS)
=== List of Messages
The following tables list the NS messages used by osmo-pcu and osmo-gbproxy, grouped by their level of
compliance with 3GPP TS 48.016.
==== Messages Compliant With 3GPP TS 48.016
The NS protocol is implemented inside libosmogb so none of the messages below are sent by OsmoPCU explicitly.
Instead corresponding functions from libosmogb are called which send and receive messages as necessary. See <<ns_init>> for details
on establishing NS connection.
.Messages compliant with 3GPP TS 48.016
[options="header",cols="10%,10%,20%,35%,5%,20%"]
|===
| TS 48.016 § | type code (hex) | This document § | Message | <-/-> | Received/Sent by OsmoPCU
| 9.2.1 | 0x0a | <<ns_alive>> | NS-ALIVE | <-/-> | Received/Sent
| 9.2.2 | 0x0b | <<ns_alive_ack>> | NS-ALIVE-ACK | <-/-> | Received/Sent
| 9.2.3 | 0x04 | <<ns_block>> | NS-BLOCK | <-/-> | Received/Sent
| 9.2.4 | 0x05 | <<ns_block_ack>> | NS-BLOCK-ACK | <-/-> | Received/Sent
| 9.2.5 | 0x02 | <<ns_reset>> | NS-RESET | <-/-> | Received/Sent
| 9.2.6 | 0x03 | <<ns_reset_ack>> | NS-RESET-ACK | <-/-> | Received/Sent
| 9.2.7 | 0x08 | <<ns_status>> | NS-STATUS | <-/-> | Received/Sent
| 9.2.8 | 0x06 | <<ns_unblock>> | NS-UNBLOCK | <-/-> | Received/Sent
| 9.2.9 | 0x07 | <<ns_unblock_ack>> | NS-UNBLOCK-ACK | <-/-> | Received/Sent
| 9.2.10 | 0x00 | <<ns_unit_data>> | NS-UNITDATA | <-/-> | Received/Sent
| 9.3.1 | 0x0c | <<sns_ack>> | SNS-ACK | <-/-> | Received/Sent
| 9.3.2 | 0x0d | <<sns_add>> | SNS-ADD | <-/-> | Received/Sent
| 9.3.3 | 0x0e | <<sns_changeweight>> | SNS-CHANGEWEIGHT | <-/-> | Received/Sent
| 9.3.4 | 0x0f | <<sns_config>> | SNS-CONFIG | <-/-> | Received/Sent
| 9.3.5 | 0x10 | <<sns_config_ack>> | SNS-CONFIG | <-/-> | Received/Sent
| 9.3.6 | 0x11 | <<sns_delete>> | SNS-DELETE | <-/-> | Received/Sent
| 9.3.7 | 0x12 | <<sns_size>> | SNS-SIZE | <-/-> | Received/Sent
| 9.3.8 | 0x13 | <<sns_size_ack>> | SNS-SIZE-ACK | <-/-> | Received/Sent
|===
==== Messages Specific to OsmoPCU
There are no OsmoPCU specific NS messages.
==== Messages Not Implemented by OsmoPCU
All the NS protocol messages from 3GPP TS 48.016 are implemented in OsmoPCU.
=== Details on Compliant NS Messages
[[ns_unit_data]]
==== NS-UNITDATA
This PDU transfers one NS SDU (specified in 3GPP TS 08.18) between
OsmoPCU and SGSN. Upon receiving it OsmoPCU passes it to BSSGP
implementation to handle. It is also sent by BSSGP as necessary - see
<<bssgp>> for details.
It contains BVCI (<<ie_bvci>>) and NS SDU (<<ie_nssdu>>) IEs.
[[ns_reset]]
==== NS-RESET
This message is send by OsmoPCU in order to initiate reset procedure
described in 3GPP TS 48.016 § 7.3. The expected reply is NS-RESET-ACK
(<<ns_reset_ack>>) message. If no expected reply is received in 3
seconds than the sending is retried up to 3 times. When this message
is received it is replied with NS-RESET-ACK (<<ns_reset_ack>>).
It might be ignored under conditions described in 3GPP TS 48.016 § 7.3.1.
The message conforms to 3GPP TS 48.016 § 9.2.5 specification.
It contains Cause (<<ie_cause>>), NSVCI (<<ie_nsvci>>) and NSEI (<<ie_nsei>>) IEs.
[[ns_reset_ack]]
==== NS-RESET-ACK
This message is sent as a response to proper NS-RESET (<<ns_reset>>)
message initiating reset procedure.
The message conforms to 3GPP TS 48.016 § 9.2.6 specification.
It contains NSVCI (<<ie_nsvci>>) and NSEI (<<ie_nsei>>) IEs.
[[ns_block]]
==== NS-BLOCK
Upon receiving this message corresponding NS-VC is marked as blocked
by OsmoPCU and NS-BLOCK-ACK (<<ns_block_ack>>) reply is transmitted.
When this message is sent by OsmoPCU corresponding NS-BLOCK-ACK
(<<ns_block_ack>>) reply is expected before NS-VC is actually marked
as blocked. This behavior follows the blocking procedure described in
3GPP TS 48.016 § 7.2.
The message conforms to 3GPP TS 48.016 § 9.2.3 specification.
It contains Cause (<<ie_cause>>) and NSVCI (<<ie_nsvci>>) IEs.
[[ns_block_ack]]
==== NS-BLOCK-ACK
This message is sent by OsmoPCU automatically upon reception of
correct NS-BLOCK (<<ns_block>>) message. It is expected as a reply
for NS-BLOCK (<<ns_block>>) message sent by OsmoPCU.
The message conforms to 3GPP TS 48.016 § 9.2.4 specification.
It contains NSVCI (<<ie_nsvci>>) IE.
[[ns_unblock]]
==== NS-UNBLOCK
Upon receiving this message corresponding NS-VC is unblocked by
OsmoPCU and NS-UNBLOCK-ACK (<<ns_unblock_ack>>) reply is sent. When
this message is sent by OsmoPCU corresponding NS-UNBLOCK-ACK
(<<ns_unblock_ack>>) reply is expected before NS-VC is actually marked
as unblocked. This behavior follows the blocking procedure described
in 3GPP TS 48.016 § 7.2.
The message conforms to 3GPP TS 48.016 § 9.2.8 specification.
[[ns_unblock_ack]]
==== NS-UNBLOCK-ACK
Receiving this message notifies OsmoPCU that NS-VC unblocking request
is confirmed and thus NS-VC is marked as unblocked. This message is
also sent as a reply to NS-UNBLOCK (<<ns_unblock>>) message.
The message conforms to 3GPP TS 48.016 § 9.2.9 specification.
[[ns_status]]
==== NS-STATUS
This message is sent to inform other party about error conditions as a
response to various unexpected PDUs or PDUs with unexpected/missing
data. If this message is received for unknown NS-VC it is ignored in
accordance with 3GPP TS 48.016 § 7.5.1, otherwise the error cause is
logged if present in NS-STATUS.
The message conforms to 3GPP TS 48.016 § 9.2.7 specification.
It contains Cause (<<ie_cause>>) and might (depending on actual error)
contain NSVCI (<<ie_nsvci>>), NS PDU (<<ie_nspdu>>) and BVCI
(<<ie_bvci>>) IEs.
[[ns_alive]]
==== NS-ALIVE
This message is sent periodically to test connectivity according to
3GPP TS 48.016 § 4.5.3. The expected response is NS-ALIVE-ACK
(<<ns_alive_ack>>). If no such response arrives within given amount of
time (3 seconds) than another NS-ALIVE message is sent and failed test
attempt is recorded. After 10 failed attempts NS connection is
considered dead and OsmoPCU tries to reconnect.
The message conforms to 3GPP TS 48.016 § 9.2.1 specification.
[[ns_alive_ack]]
==== NS-ALIVE-ACK
This message is sent automatically in reply to NS-ALIVE (<<ns_alive>>)
message.
The message conforms to 3GPP TS 48.016 § 9.2.2 specification.
[[sns_ack]]
==== SNS-ACK
[[sns_add]]
==== SNS-ADD
[[sns_changeweight]]
==== SNS-CHANGEWEIGHT
[[sns_config]]
==== SNS-CONFIG
[[sns_config_ack]]
==== SNS-CONFIG-ACK
[[sns_delete]]
==== SNS-DELETE
[[ssn_size]]
==== SNS-SIZE
[[sns_size_ack]]
==== SNS-SIZE-ACK
=== Information Elements Overview
All of the IEs handled by OsmoPCU are listed below, with limitations and
additions to 3GPP TS 48.016 specified in more detail.
==== IEs Conforming to 3GPP TS 48.016
The following Information Elements are accepted by OsmoPCU.
.IEs conforming to 3GPP TS 48.016
[options="header",cols="5%,10%,40%,5%,40%"]
|===
| tag (hex) | TS 48.016 § | IE name | <-/-> | Received/Sent by OsmoPCU
| 0x03 | 10.3.1 | BVCI | <-/-> | Received/Sent
| 0x00 | 10.3.2 | Cause | <-/-> | Received/Sent
| - | 10.3.2a | End Flag | <-/-> | Received/Sent
| 0x0b | 10.3.2b | IP Address | <-/-> | Received/Sent
| 0x05 | 10.3.2c | List of IP4 Elements | <-/-> | Received/Sent
| 0x06 | 10.3.2d | List of IP6 Elements | <-/-> | Received/Sent
| 0x07 | 10.3.2e | Maximum Number of NS-VCs | <-/-> | Received/Sent
| 0x08 | 10.3.2f | Number of IP4 Endpoints | <-/-> | Received/Sent
| 0x09 | 10.3.2g | Number of IP6 Endpoints | <-/-> | Received/Sent
| 0x02 | 10.3.3 | NS PDU | <-/-> | Received/Sent
| 0x01 | 10.3.5 | NSVCI | <-/-> | Received/Sent
| 0x04 | 10.3.6 | NSEI | <-/-> | Received/Sent
| - | 10.3.7 | PDU Type | <-/-> | Received/Sent
| 0x0a | 10.3.7a | Reset Flag | <-/-> | Received/Sent
| - | 10.3.8 | Spare Octet | <-/-> | Received/Sent
| - | 10.3.10 | Transaction ID | <-/-> | Received/Sent
|===
==== IEs Not Conforming to 3GPP TS 48.016
.IEs conforming to 3GPP TS 48.016
[options="header",cols="5%,10%,40%,5%,40%"]
|===
| tag (hex) | TS 48.016 § | IE name | <-/-> | Notice
| - | 10.3.9 | NS-SDU Control Bits | <-/-> | Not implemented yet
|===
All other IEs defined in 3GPP TS 48.016 § 10.3 are supported by OsmoPCU.
==== Additional Attributes and Parameters
There are no OsmoPCU specific additional Attributes and Parameters.
=== Details on IEs
[[ie_cause]]
==== Cause
This IE contains reason for a procedure or error as described in 3GPP TS 48.016 § 10.3.2.
[[ie_nsvci]]
==== NSVCI
This IE represents NSVCI identity described in <<ident>> and 3GPP TS 48.016 § 10.3.5.
[[ie_nspdu]]
==== NS PDU
This IE contains PDU (possibly truncated) which cause error described
in NS-STATUS message (<<ns_status>>) as described in 3GPP TS 48.016 §
10.3.3.
[[ie_nssdu]]
==== NS SDU
This IE contains BSSGP data - see <<bssgp>> for details.
[[ie_bvci]]
==== BVCI
This IE represents BSSGP identity described in <<ident>> and 3GPP TS 48.016
§ 10.3.1.
[[ie_nsei]]
==== NSEI
This IE represents NSEI identity described in <<ident>> and 3GPP TS 48.016 §
10.3.6.
[[ns_init]]
=== Gb NS Initialization / PCU bring-up
OsmoPCU binds and connects an UDP socket for NS using port numbers and IP
information given by OsmoBTS via the PCU socket. OsmoBTS in turn
receives this information from the BSC vi A-bis OML.
Following successful initialization of the UDP socket, the reset
procedure is initiated as described in <<ns_reset>>.

View File

@@ -0,0 +1,46 @@
<authorgroup>
<author>
<firstname>Max</firstname>
<surname>Suraev</surname>
<email>msuraev@sysmocom.de</email>
<authorinitials>MS</authorinitials>
<affiliation>
<shortaffil>sysmocom</shortaffil>
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
<jobtitle>Software Developer</jobtitle>
</affiliation>
</author>
<author>
<firstname>Harald</firstname>
<surname>Welte</surname>
<email>hwelte@sysmocom.de</email>
<authorinitials>HW</authorinitials>
<affiliation>
<shortaffil>sysmocom</shortaffil>
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
<jobtitle>Managing Director</jobtitle>
</affiliation>
</author>
</authorgroup>
<copyright>
<year>2015-2021</year>
<holder>sysmocom - s.f.m.c. GmbH</holder>
</copyright>
<legalnotice>
<para>
Permission is granted to copy, distribute and/or modify this
document under the terms of the GNU Free Documentation License,
Version 1.3 or any later version published by the Free Software
Foundation; with no Invariant Sections, no Front-Cover Texts,
and no Back-Cover Texts. A copy of the license is included in
the section entitled "GNU Free Documentation License".
</para>
<para>
The Asciidoc source code of this manual can be found at
<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
http://git.osmocom.org/osmo-gsm-manuals/
</ulink>
</para>
</legalnotice>

View File

@@ -0,0 +1,95 @@
:gfdl-enabled:
OsmoPCU Gb Protocol Specification
=================================
Harald Welte <hwelte@sysmocom.de>
== Introduction
This document describes the Gb interface of *OsmoPCU*. Based on 3GPP TS
48.016 and 48.018, this document indicates which of the 3GPP specified Gb
messages and IEs are implemented according to 3GPP specifications, which of
these are not or not fully implemented, as well as OsmoPCU-specific extensions
to the Gb interface not specified by 3GPP.
Extensions to the Gb interface specific to OsmoPCU are detailed in this
document. For details on the messages and IEs that comply with above-mentioned
3GPP specifications, please refer to those documents.
.3GPP document versions referred to by this document
[cols="20%,80%"]
|===
|3GPP TS 08.56 | version 8.0.1 Release 1999
|3GPP TS 08.58 | version 8.6.0 Release 1999
|3GPP TS 08.60 | version 8.2.1 Release 1999
|3GPP TS 12.21 | version 8.0.0 Release 1999
|3GPP TS 48.016 | version 15.0.0 Release 15
|3GPP TS 48.018 | version 15.0.0 Release 15
|===
.IETF documents referred to by his document
[cols="20%,80%"]
|===
|IETF RFC 768 | User Datagram Protocol
|IETF RFC 791 | Internet Protocol
|===
== Overview
The OsmoPCU Gb interface consists of the NS (Network Services) and
BSSGP (Base Station Subsystem Gateway Protocol), encapsulated in UDP
(User Datagram Protocol) and IP (Internet Protocol) version 4.
Use of other underlying protocols (e. g. Frame Relay) is not supported.
.UDP port numbers used by OsmoPCU Gb/IP
[options="header",width="50%",cols="35%,65%"]
|===
|TCP Port Number|Usage
|23000|NS over UDP (default port)
|===
The NS-over-UDP link is established in the PCU -> SGSN direction, i.e.
the PCU is running as client while the SGSN is running as server.
Establishment of the NS-over-UDP link is only possible after OsmoPCU
has been configured via the *PCU socket* interface from OsmoBTS.
OsmoBTS in turn receives relevant configuration parameters from
OsmoBSC or the BSC component inside OsmoNITB.
.Overview of Gb link establishment
["mscgen"]
----
include::{srcdir}/gb/gb-startup.msc[]
----
[[ident]]
=== Identities
The Gb interface identities of the PCU are configured via BSC ->
OsmoBTS -> PCU Socket. They consist of
NSEI:: NS Equipment Identifier
NSVCI:: NS Virtual Connection Identifier
BVCI:: BSSGP Virtual Connection Identifier
For an explanation of those identifiers and their use in the NS and
BSSGP protocols, please see the relevant 3GPP specifications for NS (TS 48.016)
and BSSGP (TS 48.018).
In most cases, all above identities belong to different namespaces and
must be unique within their respective namespace and within the SGSN
they connect to.
This means that typically each OsmoPCU has one unique set of NSEI,
NSVCI and BVCI in your network.
include::{srcdir}/gb/ns.adoc[]
include::{srcdir}/gb/bssgp.adoc[]
include::./common/chapters/port_numbers.adoc[]
include::./common/chapters/glossary.adoc[]
include::./common/chapters/gfdl.adoc[]

View File

@@ -0,0 +1,35 @@
<authorgroup>
<author>
<firstname>Harald</firstname>
<surname>Welte</surname>
<email>hwelte@sysmocom.de</email>
<authorinitials>HW</authorinitials>
<affiliation>
<shortaffil>sysmocom</shortaffil>
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
<jobtitle>Managing Director</jobtitle>
</affiliation>
</author>
</authorgroup>
<copyright>
<year>2013-2021</year>
<holder>sysmocom - s.f.m.c. GmbH</holder>
</copyright>
<legalnotice>
<para>
Permission is granted to copy, distribute and/or modify this
document under the terms of the GNU Free Documentation License,
Version 1.3 or any later version published by the Free Software
Foundation; with no Invariant Sections, no Front-Cover Texts,
and no Back-Cover Texts. A copy of the license is included in
the section entitled "GNU Free Documentation License".
</para>
<para>
The Asciidoc source code of this manual can be found at
<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
http://git.osmocom.org/osmo-gsm-manuals/
</ulink>
</para>
</legalnotice>

View File

@@ -0,0 +1,34 @@
:gfdl-enabled:
OsmoPCU User Manual
===================
Harald Welte <hwelte@sysmocom.de>
include::./common/chapters/preface.adoc[]
include::{srcdir}/chapters/overview.adoc[]
include::{srcdir}/chapters/running.adoc[]
include::./common/chapters/vty.adoc[]
include::./common/chapters/logging.adoc[]
include::{srcdir}/chapters/configuration.adoc[]
include::{srcdir}/chapters/counters.adoc[]
include::./common/chapters/gb.adoc[]
include::./common/chapters/qos-dscp-pcp.adoc[]
include::./common/chapters/vty_cpu_sched.adoc[]
include::./common/chapters/port_numbers.adoc[]
include::./common/chapters/bibliography.adoc[]
include::./common/chapters/glossary.adoc[]
include::./common/chapters/gfdl.adoc[]

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
ex:ts=2:sw=42sts=2:et
-*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-->
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
<!ENTITY chapter-vty SYSTEM "./common/chapters/vty.xml">
<!ENTITY sections-vty SYSTEM "generated/docbook_vty.xml" >
]>
<book>
<info>
<title>OsmoPCU VTY Reference</title>
<copyright>
<year>2014-2021</year>
</copyright>
<legalnotice>
<para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved.
</para>
</legalnotice>
</info>
<!-- Main chapters-->
&chapter-vty;
</book>

17
doc/manuals/regen_doc.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/sh -x
if [ -z "$DOCKER_PLAYGROUND" ]; then
echo "You need to set DOCKER_PLAYGROUND"
exit 1
fi
SCRIPT=$(realpath "$0")
MANUAL_DIR=$(dirname "$SCRIPT")
COMMIT=${COMMIT:-$(git log -1 --format=format:%H)}
cd "$DOCKER_PLAYGROUND/scripts" || exit 1
OSMO_PCU_BRANCH=$COMMIT ./regen_doc.sh osmo-pcu 4240 \
"$MANUAL_DIR/chapters/counters_generated.adoc" \
"$MANUAL_DIR/vty/osmo-pcu_vty_reference.xml"

View File

@@ -0,0 +1,9 @@
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
<node id='14'>
<child_of id='4' />
<name>PCU Configuration Node</name>
<description>The main PCU configuration including the timeslot
assignment algorithm and other parameters.</description>
</node>
</vtydoc>

View File

@@ -14,16 +14,16 @@ Notes:
Queue of next frames to be transmitted.
States:
GPRS_RLCMAC_ASSIGN
TBF_ST_ASSIGN
After a downlink TBF is created, it resides in this state until the
block flow can start. This is required to give the mobile time to listen
to connect to downlink PDCH.
GPRS_RLCMAC_FLOW,
TBF_ST_FLOW,
During packet flow, this state indicates downlink and uplink TBF block
flow.
GPRS_RLCMAC_FINISHED,
TBF_ST_FINISHED,
Uplink TBF:
After final block is received AND all other blocks are completely
received, the state is entered. The PACKET CONTROL ACK is still not
@@ -33,11 +33,11 @@ States:
downlink blocks are acknowledged yet. (Counter N3015 is counted on each
poll request.)
GPRS_RLCMAC_WAIT_RELEASE,
TBF_ST_WAIT_RELEASE,
The all blocks on downlink TBF have been acked by mobile. The penalty
timer T3192 is running on mobile.
GPRS_RLCMAC_RELEASING,
TBF_ST_RELEASING,
Wait for TFI/USF to be re-used. This state is entered when a counter
reaches it's maximum and T3169 is running.
@@ -52,7 +52,7 @@ When downlink LLC PDU is received:
Attach PDU to LLC Frame of TBF.
Put TBF back into FLOW state.
Done.
If dowlink TBF does not exists for given TLLI, or in RELEASING state:
If downlink TBF does not exists for given TLLI, or in RELEASING state:
Create new downlink TBF.
Attach PDU to LLC Frame of TBF.
If uplink TBF exists for given TLLI, but not in RELEASING state:
@@ -117,13 +117,17 @@ Control TS:
Polling:
In order to poll uplink control block from MS, a special poll state and
frame number is stored at TBF. The scheduler reads that value and will not
assign uplink resource for other TBFs at that frame number.
frame number is stored at PDCH UL Controller. The scheduler reads that value
and will not assign uplink resource for other TBFs at that frame number.
When there is no uplink transmission received on the block, a timeout is
indicated by layer 1 interface. There are two ways of checking timeout:
- The received frame is bad (BFI).
- The GSM indicates that the block should have been already received.
On receipt of an Uplink RLCMAC block, it's the duty of each specific message
handler to release the expectancies previously stored in the PDCH UL
Controller. After the specific handler returns, the generic return path will
expire all reserved events up to the curent FN for that PDCH, eventually
calling timeout functions on TBFs and SBAs owning those reservations. Hence,
the layer 1 interface is expected to provide DATA.ind for each FN block,
containing data=0 if decoding failed, in order to keep the clock up-to-date in
upper layers.
Because polling requires uplink response from MS, the polling must be
performed at control TS.
@@ -131,13 +135,21 @@ Polling:
Data structures of TBFs and PDCHs:
There is a global structure for BTS.
There is a global structure for PCU (struct gprs_pcu the_pcu).
The BTS structure has 8 TRX structures.
A BTS is created and put into PCU's list for each PCUIF INFO_IND with a unique
bts_nr received.
Each BTS structure has 8 TRX structures.
Each TRX structure has 8 PDCH structures, one for each timeslot.
There are two linked lists of TBF instances:
Each BTS structure has a list of MS (struct GprsMs).
Each MS can have 1 UL TBF and 1 DL TBF, and 1 TBF is always assigned to an
existing GprsMS structure.
Each BTS also has two linked lists of TBF instances:
- uplink TBFs
- downlink TBFs
@@ -158,4 +170,6 @@ Data structures of TBFs and PDCHs:
On release of a TBF, the link to this PDCH is removed from all assigned
PDCHs. The TBF is removed from the list of TBFs. The TBF is destroyed.
GprsMs structures are kept alive for a while once they become unused, in order to
have a cache of MS related information it would otherwise need to acquire
again once it interacts with the PCU again.

View File

@@ -1,4 +0,0 @@
pcuconfdir = $(sysconfdir)/osmocom
pcuconf_DATA = osmo-pcu.cfg
EXTRA_DIST = osmo-pcu.cfg

View File

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

View File

@@ -2,24 +2,33 @@
#define _PCUIF_PROTO_H
#include <osmocom/gsm/l1sap.h>
#include <arpa/inet.h>
#define PCU_SOCK_DEFAULT "/tmp/pcu_bts"
#define PCU_IF_VERSION 0x09
#define PCU_IF_VERSION 0x0a
#define TXT_MAX_LEN 128
/* msg_type */
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
#define PCU_IF_MSG_DATA_CNF 0x01 /* confirm (e.g. transmission on PCH) */
#define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */
#define PCU_IF_MSG_SUSP_REQ 0x03 /* BTS forwards GPRS SUSP REQ to PCU */
#define PCU_IF_MSG_APP_INFO_REQ 0x04 /* BTS asks PCU to transmit APP INFO via PACCH */
#define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send request */
#define PCU_IF_MSG_DATA_CNF_DT 0x11 /* confirm (with direct tlli) */
#define PCU_IF_MSG_RACH_IND 0x22 /* receive RACH */
#define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */
#define PCU_IF_MSG_ACT_REQ 0x40 /* activate/deactivate PDCH */
#define PCU_IF_MSG_TIME_IND 0x52 /* GSM time indication */
#define PCU_IF_MSG_INTERF_IND 0x53 /* interference report */
#define PCU_IF_MSG_PAG_REQ 0x60 /* paging request */
#define PCU_IF_MSG_TXT_IND 0x70 /* Text indication for BTS */
#define PCU_IF_MSG_CONTAINER 0x80 /* Transparent container message */
/* msg_type coming from BSC (inside PCU_IF_MSG_CONTAINER) */
#define PCU_IF_MSG_NEIGH_ADDR_REQ 0x81 /* Neighbor Address Resolution Request */
#define PCU_IF_MSG_NEIGH_ADDR_CNF 0x82 /* Neighbor Address Resolution Confirmation */
/* sapi */
#define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */
@@ -48,6 +57,13 @@
#define PCU_IF_FLAG_MCS8 (1 << 27)
#define PCU_IF_FLAG_MCS9 (1 << 28)
/* NSVC address type */
#define PCU_IF_ADDR_TYPE_UNSPEC 0x00 /* No address - empty entry */
#define PCU_IF_ADDR_TYPE_IPV4 0x04 /* IPv4 address */
#define PCU_IF_ADDR_TYPE_IPV6 0x29 /* IPv6 address */
#define PCU_IF_NUM_NSVC 2
enum gsm_pcu_if_text_type {
PCU_VERSION,
PCU_OML_ALERT,
@@ -106,14 +122,25 @@ struct gsm_pcu_if_rach_ind {
uint16_t arfcn;
uint8_t is_11bit;
uint8_t burst_type;
uint8_t trx_nr;
uint8_t ts_nr;
} __attribute__ ((packed));
struct gsm_pcu_if_info_ts {
uint8_t tsc;
uint8_t h;
uint8_t hsn;
uint8_t maio;
uint8_t ma_bit_len;
uint8_t ma[8];
} __attribute__ ((packed));
struct gsm_pcu_if_info_trx {
uint16_t arfcn;
uint8_t pdch_mask; /* PDCH channels per TS */
uint8_t pdch_mask; /* PDCH timeslot mask */
uint8_t spare;
uint8_t tsc[8]; /* TSC per channel */
uint32_t hlayer1;
struct gsm_pcu_if_info_ts ts[8]; /* timeslots per TRX */
} __attribute__ ((packed));
struct gsm_pcu_if_info_ind {
@@ -148,10 +175,14 @@ struct gsm_pcu_if_info_ind {
uint8_t initial_cs;
uint8_t initial_mcs;
/* NSVC */
uint16_t nsvci[2];
uint16_t local_port[2];
uint16_t remote_port[2];
uint32_t remote_ip[2];
uint16_t nsvci[PCU_IF_NUM_NSVC];
uint16_t local_port[PCU_IF_NUM_NSVC];
uint16_t remote_port[PCU_IF_NUM_NSVC];
uint8_t address_type[PCU_IF_NUM_NSVC];
union {
struct in_addr v4;
struct in6_addr v6;
} remote_ip[PCU_IF_NUM_NSVC];
} __attribute__ ((packed));
struct gsm_pcu_if_act_req {
@@ -171,6 +202,60 @@ struct gsm_pcu_if_pag_req {
uint8_t identity_lv[9];
} __attribute__ ((packed));
/* BTS tells PCU to [once] send given application data via PACCH to all UE with active TBF */
struct gsm_pcu_if_app_info_req {
uint8_t application_type; /* 4bit field, see TS 44.060 11.2.47 */
uint8_t len; /* length of data */
uint8_t data[162]; /* random size choice; ETWS needs 56 bytes */
} __attribute__ ((packed));
/* BTS tells PCU about a GPRS SUSPENSION REQUEST received on DCCH */
struct gsm_pcu_if_susp_req {
uint32_t tlli;
uint8_t ra_id[6];
uint8_t cause;
} __attribute__ ((packed));
/* Interference measurements on PDCH timeslots */
struct gsm_pcu_if_interf_ind {
uint8_t trx_nr;
uint8_t spare[3];
uint32_t fn;
uint8_t interf[8];
} __attribute__ ((packed));
/* Contains messages transmitted BSC<->PCU, potentially forwarded by BTS via IPA/PCU */
struct gsm_pcu_if_container {
uint8_t msg_type;
uint8_t spare;
uint16_t length; /* network byte order */
uint8_t data[0];
} __attribute__ ((packed));
/*** Used inside container: NOTE: values must be network byte order here! ***/
/* Neighbor Address Resolution Request */
struct gsm_pcu_if_neigh_addr_req {
uint16_t local_lac;
uint16_t local_ci;
uint16_t tgt_arfcn;
uint8_t tgt_bsic;
} __attribute__ ((packed));
/* Neighbor Address Resolution Confirmation */
struct gsm_pcu_if_neigh_addr_cnf {
struct gsm_pcu_if_neigh_addr_req orig_req;
uint8_t err_code; /* 0 success, !0 failed & below unset */
/* RAI + CI (CGI-PS): */
struct __attribute__ ((packed)) {
uint16_t mcc;
uint16_t mnc;
uint8_t mnc_3_digits;
uint16_t lac;
uint8_t rac;
uint16_t cell_identity;
} cgi_ps;
} __attribute__ ((packed));
struct gsm_pcu_if {
/* context based information */
uint8_t msg_type; /* message type */
@@ -182,6 +267,7 @@ struct gsm_pcu_if {
struct gsm_pcu_if_data data_cnf;
struct gsm_pcu_if_data_cnf_dt data_cnf_dt;
struct gsm_pcu_if_data data_ind;
struct gsm_pcu_if_susp_req susp_req;
struct gsm_pcu_if_rts_req rts_req;
struct gsm_pcu_if_rach_ind rach_ind;
struct gsm_pcu_if_txt_ind txt_ind;
@@ -189,6 +275,9 @@ struct gsm_pcu_if {
struct gsm_pcu_if_act_req act_req;
struct gsm_pcu_if_time_ind time_ind;
struct gsm_pcu_if_pag_req pag_req;
struct gsm_pcu_if_app_info_req app_info_req;
struct gsm_pcu_if_interf_ind interf_ind;
struct gsm_pcu_if_container container;
} u;
} __attribute__ ((packed));

View File

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

View File

@@ -15,12 +15,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
app_configs = {
"osmo-pcu": ["examples/osmo-pcu.cfg"]
"osmo-pcu": ["doc/examples/osmo-pcu.cfg"]
}
apps = [(4240, "src/osmo-pcu", "Osmo-PCU", "osmo-pcu"),
apps = [(4240, "src/osmo-pcu", "OsmoPCU", "osmo-pcu"),
]
vty_command = ["src/osmo-pcu", "-c", "examples/osmo-pcu.cfg"]
vty_command = ["src/osmo-pcu", "-c", "doc/examples/osmo-pcu.cfg"]
vty_app = apps[0]

View File

@@ -19,7 +19,7 @@
#
AUTOMAKE_OPTIONS = subdir-objects
AM_CPPFLAGS = -I$(top_srcdir)/include $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS)
AM_CPPFLAGS = -I$(top_srcdir)/include $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOGSM_CFLAGS)
if ENABLE_SYSMODSP
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
@@ -29,6 +29,10 @@ if ENABLE_LC15BTS_PHY
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
endif
if ENABLE_OC2GBTS_PHY
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
endif
AM_CXXFLAGS = -Wall -ldl -pthread
AM_LDFLAGS = -lrt
@@ -36,35 +40,46 @@ noinst_LTLIBRARIES = libgprs.la
libgprs_la_SOURCES = \
gprs_debug.cpp \
csn1.cpp \
gsm_rlcmac.cpp \
gprs_bssgp_pcu.cpp \
csn1.c \
csn1_dec.c \
csn1_enc.c \
gsm_rlcmac.c \
gprs_bssgp_pcu.c \
gprs_bssgp_rim.c \
gprs_rlcmac.cpp \
gprs_rlcmac_sched.cpp \
gprs_rlcmac_meas.cpp \
gprs_rlcmac_ts_alloc.cpp \
gprs_ms.cpp \
gprs_ms.c \
gprs_ms_storage.cpp \
gsm_timer.cpp \
gprs_pcu.c \
pcu_l1_if.cpp \
pcu_vty.c \
pcu_vty_functions.cpp \
mslot_class.c \
nacc_fsm.c \
neigh_cache.c \
tbf.cpp \
tbf_fsm.c \
tbf_ul.cpp \
tbf_ul_ack_fsm.c \
tbf_ul_ass_fsm.c \
tbf_dl.cpp \
tbf_dl_ass_fsm.c \
bts.cpp \
bts_pch_timer.c \
pdch.cpp \
poll_controller.cpp \
pdch_ul_controller.c \
encoding.cpp \
sba.cpp \
sba.c \
decoding.cpp \
llc.cpp \
rlc.cpp \
osmobts_sock.cpp \
osmobts_sock.c \
gprs_codel.c \
gprs_coding_scheme.cpp \
egprs_rlc_compression.cpp
coding_scheme.c \
egprs_rlc_compression.cpp \
gprs_rlcmac_sched.cpp
bin_PROGRAMS = \
osmo-pcu
@@ -76,18 +91,28 @@ noinst_HEADERS = \
csn1.h \
gsm_rlcmac.h \
gprs_bssgp_pcu.h \
gprs_bssgp_rim.h \
gprs_rlcmac.h \
gprs_ms.h \
gprs_ms_storage.h \
gprs_pcu.h \
pcu_l1_if.h \
gsm_timer.h \
pcu_vty.h \
pcu_vty_functions.h \
mslot_class.h \
nacc_fsm.h \
neigh_cache.h \
tbf.h \
tbf_fsm.h \
tbf_ul.h \
tbf_ul_ack_fsm.h \
tbf_ul_ass_fsm.h \
tbf_dl.h \
tbf_dl_ass_fsm.h \
bts.h \
bts_pch_timer.h \
pdch.h \
poll_controller.h \
pdch_ul_controller.h \
encoding.h \
sba.h \
rlc.h \
@@ -96,11 +121,9 @@ noinst_HEADERS = \
pcu_utils.h \
cxx_linuxlist.h \
gprs_codel.h \
gprs_coding_scheme.h \
egprs_rlc_compression.h
nobase_include_HEADERS =
osmocom/pcu/pcuif_proto.h
coding_scheme.h \
egprs_rlc_compression.h \
wireshark_compat.h
osmo_pcu_SOURCES = pcu_main.cpp
@@ -136,6 +159,7 @@ osmo_pcu_remote_LDADD = \
libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(COMMON_LA)
endif
@@ -160,10 +184,31 @@ osmo_pcu_SOURCES += \
osmo-bts-litecell15/lc15bts.c
endif
if ENABLE_OC2GBTS_PHY
AM_CPPFLAGS += -I$(OC2G_INCDIR) -I$(srcdir)/osmo-bts-oc2g
EXTRA_DIST = \
osmo-bts-oc2g/oc2g_l1_if.c \
osmo-bts-oc2g/oc2g_l1_if.h \
osmo-bts-oc2g/oc2g_l1_hw.c \
osmo-bts-oc2g/oc2gbts.c \
osmo-bts-oc2g/oc2gbts.h
noinst_HEADERS += \
osmo-bts-oc2g/oc2g_l1_if.h \
osmo-bts-oc2g/oc2gbts.h
osmo_pcu_SOURCES += \
osmo-bts-oc2g/oc2g_l1_if.c \
osmo-bts-oc2g/oc2g_l1_hw.c \
osmo-bts-oc2g/oc2gbts.c
endif
osmo_pcu_LDADD = \
libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(COMMON_LA)

File diff suppressed because it is too large Load Diff

828
src/bts.h
View File

@@ -2,6 +2,7 @@
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2013 by Holger Hans Peter Freyther
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -20,51 +21,35 @@
#pragma once
#include <pdch.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stat_item.h>
#include <osmocom/core/tdef.h>
#include <osmocom/core/time_cc.h>
#include <osmocom/gprs/gprs_ns2.h>
#include <osmocom/gsm/l1sap.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <mslot_class.h>
#include <osmocom/gsm/gsm48_rest_octets.h>
#include <osmocom/gsm/gsm48.h>
#include "mslot_class.h"
#include "gsm_rlcmac.h"
#include "gprs_pcu.h"
#ifdef __cplusplus
}
#include <gsm_rlcmac.h>
#include "poll_controller.h"
#include "sba.h"
#include "tbf.h"
#include "gprs_ms_storage.h"
#include "gprs_coding_scheme.h"
#include <cxx_linuxlist.h>
#include <pdch.h>
#endif
#include <stdint.h>
#include "tbf.h"
#include "coding_scheme.h"
#define LLC_CODEL_DISABLE 0
#define LLC_CODEL_USE_DEFAULT (-1)
#define MAX_GPRS_CS 9
/* see bts->gsmtap_categ_mask */
enum pcu_gsmtap_category {
PCU_GSMTAP_C_DL_UNKNOWN = 0, /* unknown or undecodable downlink blocks */
PCU_GSMTAP_C_DL_DUMMY = 1, /* downlink dummy blocks */
PCU_GSMTAP_C_DL_CTRL = 2, /* downlink control blocks */
PCU_GSMTAP_C_DL_DATA_GPRS = 3, /* downlink GPRS data blocks */
PCU_GSMTAP_C_DL_DATA_EGPRS = 4, /* downlink EGPRS data blocks */
PCU_GSMTAP_C_DL_PTCCH = 5, /* downlink PTCCH blocks */
PCU_GSMTAP_C_UL_UNKNOWN = 15, /* unknown or undecodable uplink blocks */
PCU_GSMTAP_C_UL_DUMMY = 16, /* uplink dummy blocks */
PCU_GSMTAP_C_UL_CTRL = 17, /* uplink control blocks */
PCU_GSMTAP_C_UL_DATA_GPRS = 18, /* uplink GPRS data blocks */
PCU_GSMTAP_C_UL_DATA_EGPRS = 19, /* uplink EGPRS data blocks */
};
struct BTS;
struct GprsMs;
struct gprs_rlcmac_bts;
struct gprs_rlcmac_trx {
void *fl1h;
@@ -72,524 +57,335 @@ struct gprs_rlcmac_trx {
struct gprs_rlcmac_pdch pdch[8];
/* back pointers */
struct BTS *bts;
struct gprs_rlcmac_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
/* list of uplink TBFs */
struct llist_head ul_tbfs; /* list of gprs_rlcmac_tbf */
/* list of downlink TBFs */
struct llist_head dl_tbfs; /* list of gprs_rlcmac_tbf */
};
#ifdef __cplusplus
extern "C" {
#endif
void bts_update_tbf_ta(const char *p, uint32_t fn, uint8_t trx_no, uint8_t ts, int8_t ta, bool is_rach);
void bts_trx_init(struct gprs_rlcmac_trx *trx, struct gprs_rlcmac_bts *bts, uint8_t trx_no);
void bts_trx_reserve_slots(struct gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction dir, uint8_t slots);
void bts_trx_unreserve_slots(struct gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction dir, uint8_t slots);
void bts_trx_free_all_tbf(struct gprs_rlcmac_trx *trx);
void bts_update_tbf_ta(struct gprs_rlcmac_bts *bts, const char *p, uint32_t fn,
uint8_t trx_no, uint8_t ts, int8_t ta, bool is_rach);
#ifdef __cplusplus
}
#endif
/**
* This is the data from C. As soon as our minimal compiler is gcc 4.7
* we can start to compile pcu_vty.c with c++ and remove the split.
*/
struct gprs_rlcmac_bts {
bool active;
uint8_t bsic;
uint8_t fc_interval;
uint16_t fc_bucket_time;
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;
uint32_t gsmtap_categ_mask;
struct gprs_rlcmac_trx trx[8];
int (*alloc_algorithm)(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
bool single, int8_t use_tbf);
uint8_t force_two_phase;
uint8_t alpha, gamma;
uint8_t egprs_enabled;
uint32_t dl_tbf_idle_msec; /* hold time for idle DL TBFs */
uint8_t si13[GSM_MACBLOCK_LEN];
bool si13_is_set;
/* 0 to support resegmentation in DL, 1 for no reseg */
uint8_t dl_arq_type;
uint32_t ms_idle_sec;
uint8_t cs_adj_enabled;
uint8_t cs_adj_upper_limit;
uint8_t cs_adj_lower_limit;
struct {int16_t low; int16_t high; } cs_lqual_ranges[MAX_GPRS_CS];
struct {int16_t low; int16_t high; } mcs_lqual_ranges[MAX_GPRS_CS];
uint16_t cs_downgrade_threshold; /* downgrade if less packets left (DL) */
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;
/* Path to be used for the pcu-bts socket */
char *pcu_sock_path;
enum {
CTR_PDCH_ALL_ALLOCATED,
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_ALLOC_FAIL,
CTR_TBF_ALLOC_FAIL_NO_TFI,
CTR_TBF_ALLOC_FAIL_NO_USF,
CTR_TBF_ALLOC_FAIL_NO_SLOT_COMBI,
CTR_TBF_ALLOC_FAIL_NO_SLOT_AVAIL,
CTR_RLC_SENT,
CTR_RLC_RESENT,
CTR_RLC_RESTARTED,
CTR_RLC_STALLED,
CTR_RLC_NACKED,
CTR_RLC_FINAL_BLOCK_RESENT,
CTR_RLC_ASS_TIMEDOUT,
CTR_RLC_ASS_FAILED,
CTR_RLC_ACK_TIMEDOUT,
CTR_RLC_ACK_FAILED,
CTR_RLC_REL_TIMEDOUT,
CTR_RLC_LATE_BLOCK,
CTR_RLC_SENT_DUMMY,
CTR_RLC_SENT_CONTROL,
CTR_RLC_DL_BYTES,
CTR_RLC_DL_PAYLOAD_BYTES,
CTR_RLC_UL_BYTES,
CTR_RLC_UL_PAYLOAD_BYTES,
CTR_DECODE_ERRORS,
CTR_SBA_ALLOCATED,
CTR_SBA_FREED,
CTR_SBA_TIMEDOUT,
CTR_LLC_FRAME_TIMEDOUT,
CTR_LLC_FRAME_DROPPED,
CTR_LLC_FRAME_SCHED,
CTR_LLC_DL_BYTES,
CTR_LLC_UL_BYTES,
CTR_PCH_REQUESTS,
CTR_PCH_REQUESTS_ALREADY,
CTR_PCH_REQUESTS_TIMEDOUT,
CTR_RACH_REQUESTS,
CTR_RACH_REQUESTS_11BIT,
CTR_RACH_REQUESTS_ONE_PHASE,
CTR_RACH_REQUESTS_TWO_PHASE,
CTR_RACH_REQUESTS_UNEXPECTED,
CTR_SPB_UL_FIRST_SEGMENT,
CTR_SPB_UL_SECOND_SEGMENT,
CTR_SPB_DL_FIRST_SEGMENT,
CTR_SPB_DL_SECOND_SEGMENT,
CTR_IMMEDIATE_ASSIGN_UL_TBF,
CTR_IMMEDIATE_ASSIGN_UL_TBF_ONE_PHASE,
CTR_IMMEDIATE_ASSIGN_UL_TBF_TWO_PHASE,
CTR_IMMEDIATE_ASSIGN_UL_TBF_CONTENTION_RESOLUTION_SUCCESS,
CTR_IMMEDIATE_ASSIGN_REJ,
CTR_IMMEDIATE_ASSIGN_DL_TBF,
CTR_CHANNEL_REQUEST_DESCRIPTION,
CTR_PKT_UL_ASSIGNMENT,
CTR_PKT_ACCESS_REJ,
CTR_PKT_DL_ASSIGNMENT,
CTR_PKT_CELL_CHG_NOTIFICATION,
CTR_PKT_CELL_CHG_CONTINUE,
CTR_PKT_NEIGH_CELL_DATA,
CTR_RLC_RECV_CONTROL,
CTR_PUA_POLL_TIMEDOUT,
CTR_PUA_POLL_FAILED,
CTR_PDA_POLL_TIMEDOUT,
CTR_PDA_POLL_FAILED,
CTR_PUAN_POLL_TIMEDOUT,
CTR_PUAN_POLL_FAILED,
CTR_PDAN_POLL_TIMEDOUT,
CTR_PDAN_POLL_FAILED,
CTR_GPRS_DL_CS1,
CTR_GPRS_DL_CS2,
CTR_GPRS_DL_CS3,
CTR_GPRS_DL_CS4,
CTR_EGPRS_DL_MCS1,
CTR_EGPRS_DL_MCS2,
CTR_EGPRS_DL_MCS3,
CTR_EGPRS_DL_MCS4,
CTR_EGPRS_DL_MCS5,
CTR_EGPRS_DL_MCS6,
CTR_EGPRS_DL_MCS7,
CTR_EGPRS_DL_MCS8,
CTR_EGPRS_DL_MCS9,
CTR_GPRS_UL_CS1,
CTR_GPRS_UL_CS2,
CTR_GPRS_UL_CS3,
CTR_GPRS_UL_CS4,
CTR_EGPRS_UL_MCS1,
CTR_EGPRS_UL_MCS2,
CTR_EGPRS_UL_MCS3,
CTR_EGPRS_UL_MCS4,
CTR_EGPRS_UL_MCS5,
CTR_EGPRS_UL_MCS6,
CTR_EGPRS_UL_MCS7,
CTR_EGPRS_UL_MCS8,
CTR_EGPRS_UL_MCS9,
};
#ifdef __cplusplus
enum {
STAT_MS_PRESENT,
STAT_PDCH_AVAILABLE,
STAT_PDCH_OCCUPIED,
STAT_PDCH_OCCUPIED_GPRS,
STAT_PDCH_OCCUPIED_EGPRS,
};
/* RACH.ind parameters (to be parsed) */
struct rach_ind_params {
enum ph_burst_type burst_type;
bool is_11bit;
uint16_t ra;
uint8_t trx_nr;
uint8_t ts_nr;
uint32_t rfn;
int16_t qta;
};
/* [EGPRS Packet] Channel Request parameters (parsed) */
struct chan_req_params {
unsigned int egprs_mslot_class;
unsigned int priority;
bool single_block;
};
struct GprsMsStorage;
struct pcu_l1_meas;
/**
* 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_FINAL_BLOCK_RESENT,
CTR_RLC_ASS_TIMEDOUT,
CTR_RLC_ASS_FAILED,
CTR_RLC_ACK_TIMEDOUT,
CTR_RLC_ACK_FAILED,
CTR_RLC_REL_TIMEDOUT,
CTR_RLC_LATE_BLOCK,
CTR_RLC_SENT_DUMMY,
CTR_RLC_SENT_CONTROL,
CTR_RLC_DL_BYTES,
CTR_RLC_DL_PAYLOAD_BYTES,
CTR_RLC_UL_BYTES,
CTR_RLC_UL_PAYLOAD_BYTES,
CTR_DECODE_ERRORS,
CTR_SBA_ALLOCATED,
CTR_SBA_FREED,
CTR_SBA_TIMEDOUT,
CTR_LLC_FRAME_TIMEDOUT,
CTR_LLC_FRAME_DROPPED,
CTR_LLC_FRAME_SCHED,
CTR_LLC_DL_BYTES,
CTR_LLC_UL_BYTES,
CTR_RACH_REQUESTS,
CTR_11BIT_RACH_REQUESTS,
CTR_SPB_UL_FIRST_SEGMENT,
CTR_SPB_UL_SECOND_SEGMENT,
CTR_SPB_DL_FIRST_SEGMENT,
CTR_SPB_DL_SECOND_SEGMENT,
CTR_IMMEDIATE_ASSIGN_UL_TBF,
CTR_IMMEDIATE_ASSIGN_REJ,
CTR_IMMEDIATE_ASSIGN_DL_TBF,
CTR_CHANNEL_REQUEST_DESCRIPTION,
CTR_PKT_UL_ASSIGNMENT,
CTR_PKT_ACCESS_REJ,
CTR_PKT_DL_ASSIGNMENT,
CTR_RLC_RECV_CONTROL,
CTR_PUA_POLL_TIMEDOUT,
CTR_PUA_POLL_FAILED,
CTR_PDA_POLL_TIMEDOUT,
CTR_PDA_POLL_FAILED,
CTR_PUAN_POLL_TIMEDOUT,
CTR_PUAN_POLL_FAILED,
CTR_PDAN_POLL_TIMEDOUT,
CTR_PDAN_POLL_FAILED,
CTR_GPRS_DL_CS1,
CTR_GPRS_DL_CS2,
CTR_GPRS_DL_CS3,
CTR_GPRS_DL_CS4,
CTR_EGPRS_DL_MCS1,
CTR_EGPRS_DL_MCS2,
CTR_EGPRS_DL_MCS3,
CTR_EGPRS_DL_MCS4,
CTR_EGPRS_DL_MCS5,
CTR_EGPRS_DL_MCS6,
CTR_EGPRS_DL_MCS7,
CTR_EGPRS_DL_MCS8,
CTR_EGPRS_DL_MCS9,
CTR_GPRS_UL_CS1,
CTR_GPRS_UL_CS2,
CTR_GPRS_UL_CS3,
CTR_GPRS_UL_CS4,
CTR_EGPRS_UL_MCS1,
CTR_EGPRS_UL_MCS2,
CTR_EGPRS_UL_MCS3,
CTR_EGPRS_UL_MCS4,
CTR_EGPRS_UL_MCS5,
CTR_EGPRS_UL_MCS6,
CTR_EGPRS_UL_MCS7,
CTR_EGPRS_UL_MCS8,
CTR_EGPRS_UL_MCS9,
};
struct gprs_rlcmac_bts {
uint8_t nr; /* bts_nr */
struct llist_head list; /* queued in pcu->bts_list */
bool active;
struct osmo_cell_global_id_ps cgi_ps;
uint8_t bsic;
uint8_t cs_mask; /* Allowed CS mask from BTS */
uint16_t mcs_mask; /* Allowed MCS mask from BTS */
struct { /* information stored from last received PCUIF info_ind message */
uint8_t initial_cs;
uint8_t initial_mcs;
} pcuif_info_ind;
uint8_t initial_cs_dl, initial_cs_ul;
uint8_t initial_mcs_dl, initial_mcs_ul;
/* Timer defintions */
struct osmo_tdef *T_defs_bts; /* timers controlled by BTS, received through PCUIF */
uint8_t n3101;
uint8_t n3103;
uint8_t n3105;
struct gprs_rlcmac_trx trx[8];
enum {
STAT_MS_PRESENT,
};
uint8_t si1[GSM_MACBLOCK_LEN];
bool si1_is_set;
uint8_t si2[GSM_MACBLOCK_LEN];
bool si2_is_set;
struct gsm_sysinfo_freq si2_bcch_cell_list[1024];
uint8_t si3[GSM_MACBLOCK_LEN];
bool si3_is_set;
uint8_t si13[GSM_MACBLOCK_LEN];
struct osmo_gsm48_si13_info si13_ro_decoded;
bool si13_is_set;
enum {
TIMER_T3190_MSEC = 5000,
};
/* State for dynamic algorithm selection */
int multislot_disabled;
BTS();
~BTS();
/* Packet Application Information (3GPP TS 44.060 11.2.47, usually ETWS primary message). We don't need to store
* more than one message, because they get sent so rarely. */
struct msgb *app_info;
uint32_t app_info_pending; /* Count of MS with active TBF, to which we did not send app_info yet */
static BTS* main_bts();
/* main nsei */
struct gprs_ns2_nse *nse;
struct gprs_rlcmac_bts *bts_data();
SBAController *sba();
/* back pointer to PCU object */
struct gprs_pcu *pcu;
/** 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;
uint32_t cur_fn;
int cur_blk_fn;
uint8_t max_cs_dl, max_cs_ul;
uint8_t max_mcs_dl, max_mcs_ul;
struct rate_ctr_group *ratectrs;
struct osmo_stat_item_group *statg;
/** add paging to paging queue(s) */
int add_paging(uint8_t chan_needed, uint8_t *identity_lv);
struct GprsMsStorage *ms_store;
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);
/* List of struct bts_pch_timer for active PCH pagings */
struct llist_head pch_timer;
int tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx) const;
int rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn);
uint32_t rfn_to_fn(int32_t rfn);
int rcv_rach(uint16_t ra, uint32_t Fn, int16_t qta, bool is_11bit,
enum ph_burst_type burst_type);
void snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi);
GprsMsStorage &ms_store();
GprsMs *ms_by_tlli(uint32_t tlli, uint32_t old_tlli = 0);
GprsMs *ms_by_imsi(const char *imsi);
GprsMs *ms_alloc(uint8_t ms_class, uint8_t egprs_ms_class = 0);
void send_gsmtap(enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
uint8_t ts_no, uint8_t channel, uint32_t fn,
const uint8_t *data, unsigned int len);
/*
* Statistics
*/
void tbf_dl_created();
void tbf_dl_freed();
void tbf_dl_aborted();
void tbf_ul_created();
void tbf_ul_freed();
void tbf_ul_aborted();
void tbf_reused();
void tbf_alloc_algo_a();
void tbf_alloc_algo_b();
void tbf_failed_egprs_only();
void rlc_sent();
void rlc_resent();
void rlc_restarted();
void rlc_stalled();
void rlc_nacked();
void rlc_final_block_resent();
void rlc_ass_timedout();
void rlc_ass_failed();
void rlc_ack_timedout();
void rlc_ack_failed();
void rlc_rel_timedout();
void rlc_late_block();
void rlc_sent_dummy();
void rlc_sent_control();
void rlc_dl_bytes(int bytes);
void rlc_dl_payload_bytes(int bytes);
void rlc_ul_bytes(int bytes);
void rlc_ul_payload_bytes(int bytes);
void decode_error();
void sba_allocated();
void sba_freed();
void sba_timedout();
void llc_timedout_frame();
void llc_dropped_frame();
void llc_frame_sched();
void llc_dl_bytes(int bytes);
void llc_ul_bytes(int bytes);
void rach_frame();
void rach_frame_11bit();
void spb_uplink_first_segment();
void spb_uplink_second_segment();
void spb_downlink_first_segment();
void spb_downlink_second_segment();
void immediate_assignment_ul_tbf();
void immediate_assignment_reject();
void immediate_assignment_dl_tbf();
void channel_request_description();
void pkt_ul_assignment();
void pkt_access_reject();
void pkt_dl_assignemnt();
void rlc_rcvd_control();
void pua_poll_timedout();
void pua_poll_failed();
void pda_poll_timedout();
void pda_poll_failed();
void pkt_ul_ack_nack_poll_timedout();
void pkt_ul_ack_nack_poll_failed();
void pkt_dl_ack_nack_poll_timedout();
void pkt_dl_ack_nack_poll_failed();
void gprs_dl_cs1();
void gprs_dl_cs2();
void gprs_dl_cs3();
void gprs_dl_cs4();
void egprs_dl_mcs1();
void egprs_dl_mcs2();
void egprs_dl_mcs3();
void egprs_dl_mcs4();
void egprs_dl_mcs5();
void egprs_dl_mcs6();
void egprs_dl_mcs7();
void egprs_dl_mcs8();
void egprs_dl_mcs9();
void gprs_ul_cs1();
void gprs_ul_cs2();
void gprs_ul_cs3();
void gprs_ul_cs4();
void egprs_ul_mcs1();
void egprs_ul_mcs2();
void egprs_ul_mcs3();
void egprs_ul_mcs4();
void egprs_ul_mcs5();
void egprs_ul_mcs6();
void egprs_ul_mcs7();
void egprs_ul_mcs8();
void egprs_ul_mcs9();
void ms_present(int32_t n);
int32_t ms_present_get();
/*
* Below for C interface for the VTY
*/
struct rate_ctr_group *rate_counters() const;
struct osmo_stat_item_group *stat_items() const;
LListHead<gprs_rlcmac_tbf>& ul_tbfs();
LListHead<gprs_rlcmac_tbf>& dl_tbfs();
private:
int m_cur_fn;
int m_cur_blk_fn;
struct gprs_rlcmac_bts m_bts;
PollController m_pollController;
SBAController m_sba;
struct rate_ctr_group *m_ratectrs;
struct osmo_stat_item_group *m_statg;
GprsMsStorage m_ms_store;
/* list of uplink TBFs */
LListHead<gprs_rlcmac_tbf> m_ul_tbfs;
/* list of downlink TBFs */
LListHead<gprs_rlcmac_tbf> m_dl_tbfs;
/* disable copying to avoid slicing */
BTS(const BTS&);
BTS& operator=(const BTS&);
struct osmo_time_cc all_allocated_pdch;
};
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 struct rate_ctr_group *BTS::rate_counters() const
{
return m_ratectrs;
}
inline struct osmo_stat_item_group *BTS::stat_items() const
{
return m_statg;
}
#define CREATE_COUNT_ADD_INLINE(func_name, ctr_name) \
inline void BTS::func_name(int inc) {\
rate_ctr_add(&m_ratectrs->ctr[ctr_name], inc); \
}
#define CREATE_COUNT_INLINE(func_name, ctr_name) \
inline void BTS::func_name() {\
rate_ctr_inc(&m_ratectrs->ctr[ctr_name]); \
}
CREATE_COUNT_INLINE(tbf_dl_created, CTR_TBF_DL_ALLOCATED)
CREATE_COUNT_INLINE(tbf_dl_freed, CTR_TBF_DL_FREED)
CREATE_COUNT_INLINE(tbf_dl_aborted, CTR_TBF_DL_ABORTED)
CREATE_COUNT_INLINE(tbf_ul_created, CTR_TBF_UL_ALLOCATED)
CREATE_COUNT_INLINE(tbf_ul_freed, CTR_TBF_UL_FREED)
CREATE_COUNT_INLINE(tbf_ul_aborted, CTR_TBF_UL_ABORTED)
CREATE_COUNT_INLINE(tbf_reused, CTR_TBF_REUSED)
CREATE_COUNT_INLINE(tbf_alloc_algo_a, CTR_TBF_ALLOC_ALGO_A)
CREATE_COUNT_INLINE(tbf_alloc_algo_b, CTR_TBF_ALLOC_ALGO_B)
CREATE_COUNT_INLINE(tbf_failed_egprs_only, CTR_TBF_FAILED_EGPRS_ONLY)
CREATE_COUNT_INLINE(rlc_sent, CTR_RLC_SENT)
CREATE_COUNT_INLINE(rlc_resent, CTR_RLC_RESENT)
CREATE_COUNT_INLINE(rlc_restarted, CTR_RLC_RESTARTED)
CREATE_COUNT_INLINE(rlc_stalled, CTR_RLC_STALLED)
CREATE_COUNT_INLINE(rlc_nacked, CTR_RLC_NACKED)
CREATE_COUNT_INLINE(rlc_final_block_resent, CTR_RLC_FINAL_BLOCK_RESENT);
CREATE_COUNT_INLINE(rlc_ass_timedout, CTR_RLC_ASS_TIMEDOUT);
CREATE_COUNT_INLINE(rlc_ass_failed, CTR_RLC_ASS_FAILED);
CREATE_COUNT_INLINE(rlc_ack_timedout, CTR_RLC_ACK_TIMEDOUT);
CREATE_COUNT_INLINE(rlc_ack_failed, CTR_RLC_ACK_FAILED);
CREATE_COUNT_INLINE(rlc_rel_timedout, CTR_RLC_REL_TIMEDOUT);
CREATE_COUNT_INLINE(rlc_late_block, CTR_RLC_LATE_BLOCK);
CREATE_COUNT_INLINE(rlc_sent_dummy, CTR_RLC_SENT_DUMMY);
CREATE_COUNT_INLINE(rlc_sent_control, CTR_RLC_SENT_CONTROL);
CREATE_COUNT_ADD_INLINE(rlc_dl_bytes, CTR_RLC_DL_BYTES);
CREATE_COUNT_ADD_INLINE(rlc_dl_payload_bytes, CTR_RLC_DL_PAYLOAD_BYTES);
CREATE_COUNT_ADD_INLINE(rlc_ul_bytes, CTR_RLC_UL_BYTES);
CREATE_COUNT_ADD_INLINE(rlc_ul_payload_bytes, CTR_RLC_UL_PAYLOAD_BYTES);
CREATE_COUNT_INLINE(decode_error, CTR_DECODE_ERRORS)
CREATE_COUNT_INLINE(sba_allocated, CTR_SBA_ALLOCATED)
CREATE_COUNT_INLINE(sba_freed, CTR_SBA_FREED)
CREATE_COUNT_INLINE(sba_timedout, CTR_SBA_TIMEDOUT)
CREATE_COUNT_INLINE(llc_timedout_frame, CTR_LLC_FRAME_TIMEDOUT);
CREATE_COUNT_INLINE(llc_dropped_frame, CTR_LLC_FRAME_DROPPED);
CREATE_COUNT_INLINE(llc_frame_sched, CTR_LLC_FRAME_SCHED);
CREATE_COUNT_ADD_INLINE(llc_dl_bytes, CTR_LLC_DL_BYTES);
CREATE_COUNT_ADD_INLINE(llc_ul_bytes, CTR_LLC_UL_BYTES);
CREATE_COUNT_INLINE(rach_frame, CTR_RACH_REQUESTS);
CREATE_COUNT_INLINE(rach_frame_11bit, CTR_11BIT_RACH_REQUESTS);
CREATE_COUNT_INLINE(spb_uplink_first_segment, CTR_SPB_UL_FIRST_SEGMENT);
CREATE_COUNT_INLINE(spb_uplink_second_segment, CTR_SPB_UL_SECOND_SEGMENT);
CREATE_COUNT_INLINE(spb_downlink_first_segment, CTR_SPB_DL_FIRST_SEGMENT);
CREATE_COUNT_INLINE(spb_downlink_second_segment, CTR_SPB_DL_SECOND_SEGMENT);
CREATE_COUNT_INLINE(immediate_assignment_ul_tbf, CTR_IMMEDIATE_ASSIGN_UL_TBF);
CREATE_COUNT_INLINE(immediate_assignment_reject, CTR_IMMEDIATE_ASSIGN_REJ);
CREATE_COUNT_INLINE(immediate_assignment_dl_tbf, CTR_IMMEDIATE_ASSIGN_DL_TBF);
CREATE_COUNT_INLINE(channel_request_description, CTR_CHANNEL_REQUEST_DESCRIPTION);
CREATE_COUNT_INLINE(pkt_ul_assignment, CTR_PKT_UL_ASSIGNMENT);
CREATE_COUNT_INLINE(pkt_access_reject, CTR_PKT_ACCESS_REJ);
CREATE_COUNT_INLINE(pkt_dl_assignemnt, CTR_PKT_DL_ASSIGNMENT);
CREATE_COUNT_INLINE(rlc_rcvd_control, CTR_RLC_RECV_CONTROL);
CREATE_COUNT_INLINE(pua_poll_timedout, CTR_PUA_POLL_TIMEDOUT);
CREATE_COUNT_INLINE(pua_poll_failed, CTR_PUA_POLL_FAILED);
CREATE_COUNT_INLINE(pda_poll_timedout, CTR_PDA_POLL_TIMEDOUT);
CREATE_COUNT_INLINE(pda_poll_failed, CTR_PDA_POLL_FAILED);
CREATE_COUNT_INLINE(pkt_ul_ack_nack_poll_timedout, CTR_PUAN_POLL_TIMEDOUT);
CREATE_COUNT_INLINE(pkt_ul_ack_nack_poll_failed, CTR_PUAN_POLL_FAILED);
CREATE_COUNT_INLINE(pkt_dl_ack_nack_poll_timedout, CTR_PDAN_POLL_TIMEDOUT);
CREATE_COUNT_INLINE(pkt_dl_ack_nack_poll_failed, CTR_PDAN_POLL_FAILED);
CREATE_COUNT_INLINE(gprs_dl_cs1, CTR_GPRS_DL_CS1);
CREATE_COUNT_INLINE(gprs_dl_cs2, CTR_GPRS_DL_CS2);
CREATE_COUNT_INLINE(gprs_dl_cs3, CTR_GPRS_DL_CS3);
CREATE_COUNT_INLINE(gprs_dl_cs4, CTR_GPRS_DL_CS4);
CREATE_COUNT_INLINE(egprs_dl_mcs1, CTR_EGPRS_DL_MCS1);
CREATE_COUNT_INLINE(egprs_dl_mcs2, CTR_EGPRS_DL_MCS2);
CREATE_COUNT_INLINE(egprs_dl_mcs3, CTR_EGPRS_DL_MCS3);
CREATE_COUNT_INLINE(egprs_dl_mcs4, CTR_EGPRS_DL_MCS4);
CREATE_COUNT_INLINE(egprs_dl_mcs5, CTR_EGPRS_DL_MCS5);
CREATE_COUNT_INLINE(egprs_dl_mcs6, CTR_EGPRS_DL_MCS6);
CREATE_COUNT_INLINE(egprs_dl_mcs7, CTR_EGPRS_DL_MCS7);
CREATE_COUNT_INLINE(egprs_dl_mcs8, CTR_EGPRS_DL_MCS8);
CREATE_COUNT_INLINE(egprs_dl_mcs9, CTR_EGPRS_DL_MCS9);
CREATE_COUNT_INLINE(gprs_ul_cs1, CTR_GPRS_UL_CS1);
CREATE_COUNT_INLINE(gprs_ul_cs2, CTR_GPRS_UL_CS2);
CREATE_COUNT_INLINE(gprs_ul_cs3, CTR_GPRS_UL_CS3);
CREATE_COUNT_INLINE(gprs_ul_cs4, CTR_GPRS_UL_CS4);
CREATE_COUNT_INLINE(egprs_ul_mcs1, CTR_EGPRS_UL_MCS1);
CREATE_COUNT_INLINE(egprs_ul_mcs2, CTR_EGPRS_UL_MCS2);
CREATE_COUNT_INLINE(egprs_ul_mcs3, CTR_EGPRS_UL_MCS3);
CREATE_COUNT_INLINE(egprs_ul_mcs4, CTR_EGPRS_UL_MCS4);
CREATE_COUNT_INLINE(egprs_ul_mcs5, CTR_EGPRS_UL_MCS5);
CREATE_COUNT_INLINE(egprs_ul_mcs6, CTR_EGPRS_UL_MCS6);
CREATE_COUNT_INLINE(egprs_ul_mcs7, CTR_EGPRS_UL_MCS7);
CREATE_COUNT_INLINE(egprs_ul_mcs8, CTR_EGPRS_UL_MCS8);
CREATE_COUNT_INLINE(egprs_ul_mcs9, CTR_EGPRS_UL_MCS9);
#undef CREATE_COUNT_INLINE
#define CREATE_STAT_INLINE(func_name, func_name_get, stat_name) \
inline void BTS::func_name(int32_t val) {\
osmo_stat_item_set(m_statg->items[stat_name], val); \
} \
inline int32_t BTS::func_name_get() {\
return osmo_stat_item_get_last(m_statg->items[stat_name]); \
}
CREATE_STAT_INLINE(ms_present, ms_present_get, STAT_MS_PRESENT);
#undef CREATE_STAT_INLINE
#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
struct paging_req_cs {
uint8_t chan_needed;
uint32_t tlli; /* GSM_RESERVED_TMSI if not present */
bool mi_tmsi_present;
struct osmo_mobile_identity mi_tmsi;
bool mi_imsi_present;
struct osmo_mobile_identity mi_imsi;
};
struct GprsMs *bts_alloc_ms(struct gprs_rlcmac_bts *bts, uint8_t ms_class, uint8_t egprs_ms_class);
int bts_add_paging(struct gprs_rlcmac_bts *bts, const struct paging_req_cs *req, struct GprsMs *ms);
uint32_t bts_rfn_to_fn(const struct gprs_rlcmac_bts *bts, int32_t rfn);
struct gprs_rlcmac_dl_tbf *bts_dl_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts);
struct gprs_rlcmac_ul_tbf *bts_ul_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts);
void bts_snd_dl_ass(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf);
void bts_set_current_frame_number(struct gprs_rlcmac_bts *bts, uint32_t frame_number);
void bts_set_current_block_frame_number(struct gprs_rlcmac_bts *bts, int frame_number);
static inline uint32_t bts_current_frame_number(const struct gprs_rlcmac_bts *bts)
{
return bts->cur_fn;
}
int bts_tfi_find_free(const struct gprs_rlcmac_bts *bts, enum gprs_rlcmac_tbf_direction dir,
uint8_t *_trx, int8_t use_trx);
int bts_rcv_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip);
int bts_rcv_ptcch_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip);
int bts_rcv_imm_ass_cnf(struct gprs_rlcmac_bts *bts, const uint8_t *data, uint32_t fn);
void bts_send_gsmtap(struct gprs_rlcmac_bts *bts,
enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
uint8_t ts_no, uint8_t channel, uint32_t fn,
const uint8_t *data, unsigned int len);
void bts_send_gsmtap_meas(struct gprs_rlcmac_bts *bts,
enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
uint8_t ts_no, uint8_t channel, uint32_t fn,
const uint8_t *data, unsigned int len, struct pcu_l1_meas *meas);
void bts_send_gsmtap_rach(struct gprs_rlcmac_bts *bts,
enum pcu_gsmtap_category categ, uint8_t channel,
const struct rach_ind_params *rip);
struct GprsMsStorage *bts_ms_store(const struct gprs_rlcmac_bts *bts);
struct GprsMs *bts_ms_by_tlli(struct gprs_rlcmac_bts *bts, uint32_t tlli, uint32_t old_tlli);
static inline struct rate_ctr_group *bts_rate_counters(struct gprs_rlcmac_bts *bts)
{
return bts->ratectrs;
}
static inline struct osmo_stat_item_group *bts_stat_items(struct gprs_rlcmac_bts *bts)
{
return bts->statg;
}
static inline void bts_do_rate_ctr_inc(const struct gprs_rlcmac_bts *bts, unsigned int ctr_id) {
rate_ctr_inc(rate_ctr_group_get_ctr(bts->ratectrs, ctr_id));
}
static inline void bts_do_rate_ctr_add(const struct gprs_rlcmac_bts *bts, unsigned int ctr_id, int inc) {
rate_ctr_add(rate_ctr_group_get_ctr(bts->ratectrs, ctr_id), inc);
}
static inline void bts_stat_item_add(struct gprs_rlcmac_bts *bts, unsigned int stat_id, int inc) {
struct osmo_stat_item *item = osmo_stat_item_group_get_item(bts->statg, stat_id);
int32_t val = osmo_stat_item_get_last(item);
osmo_stat_item_set(item, val + inc);
}
#define bts_stat_item_inc(bts, stat_id) bts_stat_item_add(bts, stat_id, 1)
#define bts_stat_item_dec(bts, stat_id) bts_stat_item_add(bts, stat_id, -1)
struct gprs_rlcmac_bts *bts_alloc(struct gprs_pcu *pcu, uint8_t bts_nr);
struct gprs_rlcmac_sba *bts_alloc_sba(struct gprs_rlcmac_bts *bts, uint8_t ta);
void bts_recalc_initial_cs(struct gprs_rlcmac_bts *bts);
void bts_recalc_initial_mcs(struct gprs_rlcmac_bts *bts);
void bts_recalc_max_cs(struct gprs_rlcmac_bts *bts);
void bts_recalc_max_mcs(struct gprs_rlcmac_bts *bts);
struct GprsMs *bts_ms_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi);
uint8_t bts_max_cs_dl(const struct gprs_rlcmac_bts *bts);
uint8_t bts_max_cs_ul(const struct gprs_rlcmac_bts *bts);
uint8_t bts_max_mcs_dl(const struct gprs_rlcmac_bts *bts);
uint8_t bts_max_mcs_ul(const struct gprs_rlcmac_bts *bts);
void bts_set_max_cs_dl(struct gprs_rlcmac_bts *bts, uint8_t cs_dl);
void bts_set_max_cs_ul(struct gprs_rlcmac_bts *bts, uint8_t cs_ul);
void bts_set_max_mcs_dl(struct gprs_rlcmac_bts *bts, uint8_t mcs_dl);
void bts_set_max_mcs_ul(struct gprs_rlcmac_bts *bts, uint8_t mcs_ul);
bool bts_cs_dl_is_supported(const struct gprs_rlcmac_bts *bts, enum CodingScheme cs);
const struct llist_head* bts_ms_list(struct gprs_rlcmac_bts *bts);
uint8_t bts_get_ms_pwr_alpha(const struct gprs_rlcmac_bts *bts);
bool bts_all_pdch_allocated(const struct gprs_rlcmac_bts *bts);
#ifdef __cplusplus
}
#endif

121
src/bts_pch_timer.c Normal file
View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Oliver Smith
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/tdef.h>
#include <osmocom/core/utils.h>
#include <gprs_debug.h>
#include <gprs_pcu.h>
#include <bts_pch_timer.h>
#include <gprs_ms.h>
static struct bts_pch_timer *bts_pch_timer_get_by_ptmsi(struct gprs_rlcmac_bts *bts, uint32_t ptmsi)
{
struct bts_pch_timer *p;
OSMO_ASSERT(ptmsi != GSM_RESERVED_TMSI);
llist_for_each_entry(p, &bts->pch_timer, entry) {
if (p->ptmsi != GSM_RESERVED_TMSI && p->ptmsi == ptmsi)
return p;
}
return NULL;
}
struct bts_pch_timer *bts_pch_timer_get_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi)
{
struct bts_pch_timer *p;
llist_for_each_entry(p, &bts->pch_timer, entry) {
if (strcmp(p->imsi, imsi) == 0)
return p;
}
return NULL;
}
static void bts_pch_timer_remove(struct bts_pch_timer *p)
{
osmo_timer_del(&p->T3113);
llist_del(&p->entry);
LOGP(DPCU, LOGL_DEBUG, "PCH paging timer stopped for IMSI=%s\n", p->imsi);
talloc_free(p);
}
static void T3113_callback(void *data)
{
struct bts_pch_timer *p = data;
LOGP(DPCU, LOGL_INFO, "PCH paging timeout for IMSI=%s\n", p->imsi);
bts_do_rate_ctr_inc(p->bts, CTR_PCH_REQUESTS_TIMEDOUT);
bts_pch_timer_remove(p);
}
void bts_pch_timer_start(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi_paging,
const char *imsi)
{
struct bts_pch_timer *p;
struct osmo_tdef *tdef;
p = talloc_zero(bts, struct bts_pch_timer);
llist_add_tail(&p->entry, &bts->pch_timer);
p->bts = bts;
OSMO_STRLCPY_ARRAY(p->imsi, imsi);
p->ptmsi = (mi_paging->type == GSM_MI_TYPE_TMSI) ? mi_paging->tmsi : GSM_RESERVED_TMSI;
tdef = osmo_tdef_get_entry(the_pcu->T_defs, 3113);
OSMO_ASSERT(tdef);
osmo_timer_setup(&p->T3113, T3113_callback, p);
osmo_timer_schedule(&p->T3113, tdef->val, 0);
if (log_check_level(DPCU, LOGL_DEBUG)) {
char str[64];
osmo_mobile_identity_to_str_buf(str, sizeof(str), mi_paging);
LOGP(DPCU, LOGL_DEBUG, "PCH paging timer started for MI=%s IMSI=%s\n", str, p->imsi);
}
}
void bts_pch_timer_stop(struct gprs_rlcmac_bts *bts, const struct GprsMs *ms)
{
struct bts_pch_timer *p = NULL;
uint32_t tlli = ms_tlli(ms);
const char *imsi = ms_imsi(ms);
/* First try matching by TMSI if available in MS */
if (tlli != GSM_RESERVED_TMSI)
p = bts_pch_timer_get_by_ptmsi(bts, tlli);
/* Otherwise try matching by IMSI if available in MS */
if (!p && imsi[0] != '\0')
p = bts_pch_timer_get_by_imsi(bts, imsi);
if (p)
bts_pch_timer_remove(p);
}
void bts_pch_timer_stop_all(struct gprs_rlcmac_bts *bts)
{
struct bts_pch_timer *p, *n;
llist_for_each_entry_safe(p, n, &bts->pch_timer, entry) {
bts_pch_timer_remove(p);
}
}

49
src/bts_pch_timer.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Oliver Smith
*
* 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 Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <bts.h>
#ifdef __cplusplus
extern "C" {
#endif
struct bts_pch_timer {
struct llist_head entry;
struct gprs_rlcmac_bts *bts;
struct osmo_timer_list T3113;
uint32_t ptmsi; /* GSM_RESERVED_TMSI if not available */
char imsi[OSMO_IMSI_BUF_SIZE];
};
struct GprsMs;
void bts_pch_timer_start(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi_paging,
const char *imsi);
void bts_pch_timer_stop(struct gprs_rlcmac_bts *bts, const struct GprsMs *ms);
void bts_pch_timer_stop_all(struct gprs_rlcmac_bts *bts);
struct bts_pch_timer *bts_pch_timer_get_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi);
#ifdef __cplusplus
}
#endif

408
src/coding_scheme.c Normal file
View File

@@ -0,0 +1,408 @@
/* coding_scheme.c
*
* Copyright (C) 2019 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.
*/
#include <stdint.h>
#include <stdbool.h>
#include <osmocom/core/utils.h>
#include "coding_scheme.h"
const struct value_string mcs_names[] = {
{ UNKNOWN, "UNKNOWN" },
{ CS1, "CS-1" },
{ CS2, "CS-2" },
{ CS3, "CS-3" },
{ CS4, "CS-4" },
{ MCS1, "MCS-1" },
{ MCS2, "MCS-2" },
{ MCS3, "MCS-3" },
{ MCS4, "MCS-4" },
{ MCS5, "MCS-5" },
{ MCS6, "MCS-6" },
{ MCS7, "MCS-7" },
{ MCS8, "MCS-8" },
{ MCS9, "MCS-9" },
{ 0, NULL }
};
enum Family {
FAMILY_INVALID,
FAMILY_A,
FAMILY_B,
FAMILY_C,
};
static struct {
struct {
uint8_t bytes;
uint8_t ext_bits;
uint8_t data_header_bits;
} uplink, downlink;
uint8_t data_bytes;
uint8_t optional_padding_bits;
enum HeaderType data_hdr;
enum Family family;
} mcs_info[NUM_SCHEMES] = {
{{0, 0}, {0, 0}, 0, 0,
HEADER_INVALID, FAMILY_INVALID},
{{23, 0}, {23, 0}, 20, 0,
HEADER_GPRS_DATA, FAMILY_INVALID},
{{33, 7}, {33, 7}, 30, 0,
HEADER_GPRS_DATA, FAMILY_INVALID},
{{39, 3}, {39, 3}, 36, 0,
HEADER_GPRS_DATA, FAMILY_INVALID},
{{53, 7}, {53, 7}, 50, 0,
HEADER_GPRS_DATA, FAMILY_INVALID},
{{26, 1}, {26, 1}, 22, 0,
HEADER_EGPRS_DATA_TYPE_3, FAMILY_C},
{{32, 1}, {32, 1}, 28, 0,
HEADER_EGPRS_DATA_TYPE_3, FAMILY_B},
{{41, 1}, {41, 1}, 37, 48,
HEADER_EGPRS_DATA_TYPE_3, FAMILY_A},
{{48, 1}, {48, 1}, 44, 0,
HEADER_EGPRS_DATA_TYPE_3, FAMILY_C},
{{60, 7}, {59, 6}, 56, 0,
HEADER_EGPRS_DATA_TYPE_2, FAMILY_B},
{{78, 7}, {77, 6}, 74, 48,
HEADER_EGPRS_DATA_TYPE_2, FAMILY_A},
{{118, 2}, {117, 4}, 56, 0,
HEADER_EGPRS_DATA_TYPE_1, FAMILY_B},
{{142, 2}, {141, 4}, 68, 0,
HEADER_EGPRS_DATA_TYPE_1, FAMILY_A},
{{154, 2}, {153, 4}, 74, 0,
HEADER_EGPRS_DATA_TYPE_1, FAMILY_A},
};
const char *mcs_name(enum CodingScheme val) {
return get_value_string(mcs_names, val);
}
bool mcs_is_gprs(enum CodingScheme cs)
{
return CS1 <= cs && cs <= CS4;
}
bool mcs_is_edge(enum CodingScheme cs)
{
return MCS1 <= cs && cs <= MCS9;
}
bool mcs_is_edge_gmsk(enum CodingScheme cs)
{
if (mcs_is_edge(cs))
return cs <= MCS4;
return false;
}
/* Return 3GPP TS 44.060 §12.10d (EDGE) or Table 11.2.28.2 (GPRS) Channel Coding Command value */
uint8_t mcs_chan_code(enum CodingScheme cs)
{
if (mcs_is_gprs(cs))
return cs - CS1;
if (mcs_is_edge(cs))
return cs - MCS1;
/* Defaults to (M)CS1 */
return 0;
}
enum CodingScheme mcs_get_by_size_ul(unsigned size)
{
switch (size) {
case 23: return CS1;
case 27: return MCS1;
case 33: return MCS2;
case 34: return CS2;
case 40: return CS3;
case 42: return MCS3;
case 49: return MCS4;
case 54: return CS4;
case 61: return MCS5;
case 79: return MCS6;
case 119: return MCS7;
case 143: return MCS8;
case 155: return MCS9;
default: return UNKNOWN;
}
}
enum CodingScheme mcs_get_gprs_by_num(unsigned num)
{
if (num < 1 || num > 4)
return UNKNOWN;
return CS1 + (num - 1);
}
enum CodingScheme mcs_get_egprs_by_num(unsigned num)
{
if (num < 1 || num > 9)
return UNKNOWN;
return MCS1 + (num - 1);
}
bool mcs_is_valid(enum CodingScheme cs)
{
return UNKNOWN < cs && cs <= MCS9;
}
bool mcs_is_compat_kind(enum CodingScheme cs, enum mcs_kind mode)
{
switch (mode) {
case GPRS: return mcs_is_gprs(cs);
case EGPRS_GMSK: return mcs_is_edge_gmsk(cs);
case EGPRS: return mcs_is_edge(cs);
}
return false;
}
bool mcs_is_compat(enum CodingScheme cs, enum CodingScheme o)
{
return (mcs_is_gprs(cs) && mcs_is_gprs(o)) || (mcs_is_edge(cs) && mcs_is_edge(o));
}
uint8_t mcs_size_ul(enum CodingScheme cs)
{
return mcs_info[cs].uplink.bytes + (mcs_spare_bits_ul(cs) ? 1 : 0);
}
uint8_t mcs_size_dl(enum CodingScheme cs)
{
return mcs_info[cs].downlink.bytes + (mcs_spare_bits_dl(cs) ? 1 : 0);
}
uint8_t mcs_used_size_ul(enum CodingScheme cs)
{
if (mcs_info[cs].data_hdr == HEADER_GPRS_DATA)
return mcs_info[cs].uplink.bytes;
else
return mcs_size_ul(cs);
}
uint8_t mcs_used_size_dl(enum CodingScheme cs)
{
if (mcs_info[cs].data_hdr == HEADER_GPRS_DATA)
return mcs_info[cs].downlink.bytes;
else
return mcs_size_dl(cs);
}
uint8_t mcs_max_bytes_ul(enum CodingScheme cs)
{
return mcs_info[cs].uplink.bytes;
}
uint8_t mcs_max_bytes_dl(enum CodingScheme cs)
{
return mcs_info[cs].downlink.bytes;
}
uint8_t mcs_spare_bits_ul(enum CodingScheme cs)
{
return mcs_info[cs].uplink.ext_bits;
}
uint8_t mcs_spare_bits_dl(enum CodingScheme cs)
{
return mcs_info[cs].downlink.ext_bits;
}
uint8_t mcs_max_data_block_bytes(enum CodingScheme cs)
{
return mcs_info[cs].data_bytes;
}
uint8_t mcs_opt_padding_bits(enum CodingScheme cs)
{
return mcs_info[cs].optional_padding_bits;
}
void mcs_inc_kind(enum CodingScheme *cs, enum mcs_kind mode)
{
if (!mcs_is_compat_kind(*cs, mode))
/* This should not happen. TODO: Use assert? */
return;
enum CodingScheme new_cs = *cs + 1;
if (!mcs_is_compat_kind(new_cs, mode))
/* Clipping, do not change the value */
return;
*cs = new_cs;
}
void mcs_dec_kind(enum CodingScheme *cs, enum mcs_kind mode)
{
if (!mcs_is_compat_kind(*cs, mode))
/* This should not happen. TODO: Use assert? */
return;
enum CodingScheme new_cs = *cs - 1;
if (!mcs_is_compat_kind(new_cs, mode))
/* Clipping, do not change the value */
return;
*cs = new_cs;
}
void mcs_inc(enum CodingScheme *cs)
{
if (mcs_is_gprs(*cs) && *cs == CS4)
return;
if (mcs_is_edge(*cs) && *cs == MCS9)
return;
if (!mcs_is_valid(*cs))
return;
*cs = *cs + 1;
}
void mcs_dec(enum CodingScheme *cs)
{
if (mcs_is_gprs(*cs) && *cs == CS1)
return;
if (mcs_is_edge(*cs) && *cs == MCS1)
return;
if (!mcs_is_valid(*cs))
return;
*cs = *cs - 1;
}
bool mcs_is_family_compat(enum CodingScheme cs, enum CodingScheme o)
{
if (cs == o)
return true;
if (mcs_info[cs].family == FAMILY_INVALID)
return false;
return mcs_info[cs].family == mcs_info[o].family;
}
void mcs_dec_to_single_block(enum CodingScheme *cs, bool *need_stuffing)
{
switch (*cs) {
case MCS7: *need_stuffing = false; *cs = MCS5; break;
case MCS8: *need_stuffing = true; *cs = MCS6; break;
case MCS9: *need_stuffing = false; *cs = MCS6; break;
default: *need_stuffing = false; break;
}
}
static struct {
struct {
uint8_t data_header_bits;
} uplink, downlink;
uint8_t data_block_header_bits;
uint8_t num_blocks;
const char *name;
} hdr_type_info[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" },
};
enum HeaderType mcs_header_type(enum CodingScheme mcs)
{
return mcs_info[mcs].data_hdr;
}
uint8_t num_data_blocks(enum HeaderType ht)
{
OSMO_ASSERT(ht < NUM_HEADER_TYPES);
return hdr_type_info[ht].num_blocks;
}
uint8_t num_data_header_bits_UL(enum HeaderType ht)
{
OSMO_ASSERT(ht < NUM_HEADER_TYPES);
return hdr_type_info[ht].uplink.data_header_bits;
}
uint8_t num_data_header_bits_DL(enum HeaderType ht)
{
OSMO_ASSERT(ht < NUM_HEADER_TYPES);
return hdr_type_info[ht].downlink.data_header_bits;
}
uint8_t num_data_block_header_bits(enum HeaderType ht)
{
OSMO_ASSERT(ht < NUM_HEADER_TYPES);
return hdr_type_info[ht].data_block_header_bits;
}
const struct value_string mode_names[] = {
{ GPRS, "GPRS" },
{ EGPRS_GMSK, "EGPRS_GMSK-only"},
{ EGPRS, "EGPRS"},
{ 0, NULL }
};
const char *mode_name(enum mcs_kind val) {
return get_value_string(mode_names, val);
}
/* FIXME: take into account padding and special cases of commanded MCS (MCS-6-9 and MCS-5-7) */
enum CodingScheme get_retx_mcs(enum CodingScheme initial_mcs, enum CodingScheme commanded_mcs, bool resegment_bit)
{
OSMO_ASSERT(mcs_is_edge(initial_mcs));
OSMO_ASSERT(mcs_is_edge(commanded_mcs));
OSMO_ASSERT(NUM_SCHEMES - MCS1 == 9);
if (resegment_bit) { /* 3GPP TS 44.060 Table 8.1.1.1, reflected over antidiagonal */
enum CodingScheme egprs_reseg[NUM_SCHEMES - MCS1][NUM_SCHEMES - MCS1] = {
{ MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1 },
{ MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2 },
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3 },
{ MCS1, MCS1, MCS1, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4 },
{ MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7 },
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9 },
{ MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7 },
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS8, MCS8 },
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9 },
};
return egprs_reseg[mcs_chan_code(initial_mcs)][mcs_chan_code(commanded_mcs)];
} else { /* 3GPP TS 44.060 Table 8.1.1.2, reflected over antidiagonal */
enum CodingScheme egprs_no_reseg[NUM_SCHEMES - MCS1][NUM_SCHEMES - MCS1] = {
{ MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1 },
{ MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2 },
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3 },
{ MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4 },
{ MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7 },
{ MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9 },
{ MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7 },
{ MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS8, MCS8 },
{ MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9 },
};
return egprs_no_reseg[mcs_chan_code(initial_mcs)][mcs_chan_code(commanded_mcs)];
}
}

99
src/coding_scheme.h Normal file
View File

@@ -0,0 +1,99 @@
/* coding_scheme.h
*
* Copyright (C) 2015-2019 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.
*/
#pragma once
#include <osmocom/core/utils.h>
#include <stdbool.h>
enum CodingScheme {
UNKNOWN,
/* GPRS Coding Schemes: */
CS1, CS2, CS3, CS4,
/* EDGE/EGPRS Modulation and Coding Schemes: */
MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9,
NUM_SCHEMES
};
enum mcs_kind {
GPRS,
EGPRS_GMSK,
EGPRS,
};
enum egprs_arq_type {
EGPRS_ARQ1 = 0,
EGPRS_ARQ2 = 1
};
extern const struct value_string mcs_names[];
const char *mcs_name(enum CodingScheme val);
enum CodingScheme get_retx_mcs(enum CodingScheme initial_mcs, enum CodingScheme commanded_mcs, bool resegment_bit);
bool mcs_is_gprs(enum CodingScheme cs);
bool mcs_is_edge(enum CodingScheme cs);
bool mcs_is_edge_gmsk(enum CodingScheme cs);
uint8_t mcs_chan_code(enum CodingScheme cs);
enum CodingScheme mcs_get_by_size_ul(unsigned size);
enum CodingScheme mcs_get_gprs_by_num(unsigned num);
enum CodingScheme mcs_get_egprs_by_num(unsigned num);
bool mcs_is_valid(enum CodingScheme cs);
bool mcs_is_compat(enum CodingScheme cs, enum CodingScheme o);
bool mcs_is_compat_kind(enum CodingScheme cs, enum mcs_kind mode);
uint8_t mcs_size_ul(enum CodingScheme cs);
uint8_t mcs_size_dl(enum CodingScheme cs);
uint8_t mcs_used_size_ul(enum CodingScheme cs);
uint8_t mcs_used_size_dl(enum CodingScheme cs);
uint8_t mcs_max_bytes_ul(enum CodingScheme cs);
uint8_t mcs_max_bytes_dl(enum CodingScheme cs);
uint8_t mcs_spare_bits_ul(enum CodingScheme cs);
uint8_t mcs_spare_bits_dl(enum CodingScheme cs);
uint8_t mcs_max_data_block_bytes(enum CodingScheme cs);
uint8_t mcs_opt_padding_bits(enum CodingScheme cs);
void mcs_inc_kind(enum CodingScheme *cs, enum mcs_kind mode);
void mcs_dec_kind(enum CodingScheme *cs, enum mcs_kind mode);
void mcs_inc(enum CodingScheme *cs);
void mcs_dec(enum CodingScheme *cs);
bool mcs_is_family_compat(enum CodingScheme cs, enum CodingScheme o);
void mcs_dec_to_single_block(enum CodingScheme *cs, bool *need_stuffing);
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 HeaderType mcs_header_type(enum CodingScheme mcs);
uint8_t num_data_blocks(enum HeaderType ht);
uint8_t num_data_header_bits_UL(enum HeaderType ht);
uint8_t num_data_header_bits_DL(enum HeaderType ht);
uint8_t num_data_block_header_bits(enum HeaderType ht);
const char *mode_name(enum mcs_kind val);

107
src/csn1.c Normal file
View File

@@ -0,0 +1,107 @@
/* csn1_enc.c
* Routines for CSN1 dissection in wireshark.
*
* Copyright (C) 2011 Ivan Klyuchnikov
*
* By Vincent Helfre, based on original code by Jari Sassi
* with the gracious authorization of STE
* Copyright (c) 2011 ST-Ericsson
*
* $Id: packet-csn1.c 39140 2011-09-25 22:01:50Z wmeier $
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* 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 <assert.h>
#include <string.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include "csn1.h"
#include <gprs_debug.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
const unsigned char ixBitsTab[] = {0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5};
/* Returns no_of_bits (up to 8) masked with 0x2B */
guint8
get_masked_bits8(struct bitvec *vector, unsigned *readIndex, gint bit_offset, const gint no_of_bits)
{
static const guint8 maskBits[] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF};
//gint byte_offset = bit_offset >> 3; /* divide by 8 */
gint relative_bit_offset = bit_offset & 0x07; /* modulo 8 */
guint8 result;
gint bit_shift = 8 - relative_bit_offset - (gint) no_of_bits;
*readIndex -= relative_bit_offset;
if (bit_shift >= 0)
{
result = (0x2B ^ ((guint8)bitvec_read_field(vector, readIndex, 8))) >> bit_shift;
*readIndex-= bit_shift;
result &= maskBits[no_of_bits];
}
else
{
guint8 hight_part = (0x2B ^ ((guint8)bitvec_read_field(vector, readIndex, 8))) & maskBits[8 - relative_bit_offset];
hight_part = (guint8) (hight_part << (-bit_shift));
result = (0x2B ^ ((guint8)bitvec_read_field(vector, readIndex, 8))) >> (8 + bit_shift);
*readIndex = *readIndex - (8 - (-bit_shift));
result |= hight_part;
}
return result;
}
/**
* ================================================================================================
* set initial/start values in help data structure used for packing/unpacking operation
* ================================================================================================
*/
void
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 struct value_string csn1_error_names[] = {
{ CSN_OK, "General 0" },
{ CSN_ERROR_GENERAL, "General -1" },
{ CSN_ERROR_DATA_NOT_VALID, "DATA_NOT VALID" },
{ CSN_ERROR_IN_SCRIPT, "IN SCRIPT" },
{ CSN_ERROR_INVALID_UNION_INDEX, "INVALID UNION INDEX" },
{ CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, "NEED_MORE BITS TO UNPACK" },
{ CSN_ERROR_ILLEGAL_BIT_VALUE, "ILLEGAL BIT VALUE" },
{ CSN_ERROR_INTERNAL, "INTERNAL" },
{ CSN_ERROR_STREAM_NOT_SUPPORTED, "STREAM_NOT_SUPPORTED" },
{ CSN_ERROR_MESSAGE_TOO_LONG, "MESSAGE_TOO_LONG" },
{ 0, NULL }
};
gint16 ProcessError_impl(const char *file, int line, unsigned *readIndex,
const char* sz, gint16 err, const CSN_DESCR* pDescr)
{
/* Don't add trailing newline, top caller is responsible for appending it */
if (err != CSN_OK)
LOGPSRC(DCSN1, LOGL_ERROR, file, line, "%s: error %s (%d) at %s (idx %u)",
sz, get_value_string(csn1_error_names, err), err,
pDescr ? pDescr->sz : "-", *readIndex);
return err;
}

File diff suppressed because it is too large Load Diff

View File

@@ -28,15 +28,8 @@
#ifndef _PACKET_CSN1_H_
#define _PACKET_CSN1_H_
extern "C" {
#include <osmocom/core/bitvec.h>
}
#include <iostream>
#include <cstdlib>
#define MIN(a,b) (((a)<(b))?(a):(b))
//#define max(a,b) (((a)>(b))?(a):(b))
#include "wireshark_compat.h"
/* Error codes */
#define CSN_OK 0
@@ -51,16 +44,6 @@ extern "C" {
#define CSN_ERROR_MESSAGE_TOO_LONG -9
#define CSN_ERROR_ -10
#define FALSE (0)
#define TRUE (1)
typedef signed int gint32;
typedef signed short gint16;
typedef int gint;
typedef gint gboolean;
typedef unsigned char guint8;
typedef unsigned short guint16;
typedef unsigned int guint32;
typedef unsigned long guint64;
/* CallBack return status */
typedef gint16 CSN_CallBackStatus_t;
@@ -77,6 +60,7 @@ typedef gint16 CSN_CallBackStatus_t;
#ifndef ElementsOf
#define ElementsOf(array) (sizeof(array) / sizeof(array[0]))
#endif
typedef void(*void_fn_t)(void);
/* Context holding CSN1 parameters */
typedef struct
@@ -86,7 +70,9 @@ typedef struct
gint direction; /* 0 - decode; 1 - encode */
} csnStream_t;
typedef gint16 (*StreamSerializeFcn_t)(csnStream_t* ar, bitvec *vector, unsigned& readIndex, void* data);
typedef gint16 (*StreamSerializeFcn_t)(csnStream_t* ar, struct bitvec *vector, unsigned *readIndex, void* data);
typedef CSN_CallBackStatus_t (*DissectorCallbackFcn_t)(struct bitvec *vector, unsigned *readIndex, void* param1, void* param2);
typedef enum
{
CSN_END = 0,
@@ -194,24 +180,21 @@ typedef struct
gint16 i;
union
{
void* ptr;
const void* ptr;
guint32 value;
} descr;
unsigned offset;
gboolean may_be_null;
const char* sz;
union
{
StreamSerializeFcn_t fcn;
guint32 value;
int* hf_ptr;
} serialize;
guint32 value;
void_fn_t aux_fn;
} CSN_DESCR;
typedef struct
{
guint8 bits;
guint8 value;
gboolean keep_bits;
CSN_DESCR descr;
} CSN_ChoiceElement_t;
@@ -234,16 +217,16 @@ void csnStreamInit(csnStream_t* ar,gint BitOffset,gint BitCount);
* RETURNS: int Number of bits left to be unpacked. Negative Error code if failed to unpack all bits
******************************************************************************/
gint16 csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsigned& readIndex, void* data);
gint16 csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, struct bitvec *vector, unsigned *readIndex, void* data);
gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsigned& readIndex, void* data);
gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, struct bitvec *vector, unsigned *writeIndex, void* data);
/* CSN struct macro's */
#define CSN_DESCR_BEGIN(_STRUCT)\
CSN_DESCR CSNDESCR_##_STRUCT[] = {
#define CSN_DESCR_END(_STRUCT)\
{CSN_END, 0, {0}, 0, FALSE, "", {(StreamSerializeFcn_t)0}} };
{CSN_END, 0, {0}, 0, FALSE, "", 0, NULL} };
/******************************************************************************
* CSN_ERROR(Par1, Par2, Par3)
@@ -254,7 +237,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* Par3: Error code
*****************************************************************************/
#define CSN_ERROR(_STRUCT, _Text, _ERRCODE)\
{CSN_TRAP_ERROR, _ERRCODE, {(void*)_Text}, 0, FALSE, _Text, {(StreamSerializeFcn_t)0}}
{CSN_TRAP_ERROR, _ERRCODE, {_Text}, 0, FALSE, _Text, 0, NULL}
/******************************************************************************
* M_BIT(Par1, Par2)
@@ -263,7 +246,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* Par2: C structure element name
*****************************************************************************/
#define M_BIT(_STRUCT, _MEMBER)\
{CSN_BIT, 0, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_BIT, 0, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_BIT_OR_NULL(Par1, Par2)
@@ -273,11 +256,11 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* Covers the case {null | 0 | 1}
*****************************************************************************/
#define M_BIT_OR_NULL(_STRUCT, _MEMBER)\
{CSN_BIT, 0, {0}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_BIT, 0, {0}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_NEXT_EXIST(Par1, Par2, Par3)
* Indicates whether the next element or a group of elements defined in the
* Indicates whether the next element or a group of elements defined in the
* structure is present or not.
* Par1: C structure name
* Par2: C structure element name
@@ -285,37 +268,37 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* element(s) does not exist
*****************************************************************************/
#define M_NEXT_EXIST(_STRUCT, _MEMBER, _NoOfExisting)\
{CSN_NEXT_EXIST, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_NEXT_EXIST, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_NEXT_EXIST_LH(Par1, Par2, Par3)
* similar to the M_NEXT_EXIST except that instead of bit 0/1 which is fetched
* from the message in order to find out whether the next element/elements are
* present in the message, the logical operation XOR with the background
* present in the message, the logical operation XOR with the background
* pattern 0x2B is performed on the read bit before the decision is made.
*****************************************************************************/
#define M_NEXT_EXIST_LH(_STRUCT, _MEMBER, _NoOfExisting)\
{CSN_NEXT_EXIST_LH, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_NEXT_EXIST_LH, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_NEXT_EXIST_OR_NULL(Par1, Par2, Par3)
* Similar to the M_NEXT_EXIST except that not only bit 0 or 1 but also the end
* of the message may be encountered when looking for the next element in the
* of the message may be encountered when looking for the next element in the
* message.
* Covers the case {null | 0 | 1 < IE >}
* Covers the case {null | 0 | 1 < IE >}
*****************************************************************************/
#define M_NEXT_EXIST_OR_NULL(_STRUCT, _MEMBER, _NoOfExisting)\
{CSN_NEXT_EXIST, _NoOfExisting, {(void*)1}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_NEXT_EXIST, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_NEXT_EXIST_OR_NULL_LH(Par1, Par2, Par3)
* Similar to the M_NEXT_EXIST_LH except that not only bit 0 or 1 but also the
* end of the message may be encountered when looking for the next element in
* the message.
* Covers the case {null | L | H < IE >}
* Covers the case {null | L | H < IE >}
*****************************************************************************/
#define M_NEXT_EXIST_OR_NULL_LH(_STRUCT, _MEMBER, _NoOfExisting)\
{CSN_NEXT_EXIST_LH, _NoOfExisting, {(void*)1}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_NEXT_EXIST_LH, _NoOfExisting, {(void*)1}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_UINT(Par1, Par2, Par3)
@@ -325,7 +308,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* Par3: number of bits used to code the element (between 1 and 32)
*****************************************************************************/
#define M_UINT(_STRUCT, _MEMBER, _BITS)\
{CSN_UINT, _BITS, {(void*)1}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_UINT, _BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_UINT_OR_NULL(Par1, Par2, Par3)
@@ -335,17 +318,17 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* Covers the case {null | 0 | 1 < IE >}
*****************************************************************************/
#define M_UINT_OR_NULL(_STRUCT, _MEMBER, _BITS)\
{CSN_UINT, _BITS, {0}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_UINT, _BITS, {0}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_UINT(Par1, Par2, Par3)
* This macro has the same functionality as M_UINT except that in addition the
* logical "exclusive or" operation with the background value "0x2B" is
* performed before the final value of the integer number is delivered from the
* logical "exclusive or" operation with the background value "0x2B" is
* performed before the final value of the integer number is delivered from the
* received CSN.1 message
*****************************************************************************/
#define M_UINT_LH(_STRUCT, _MEMBER, _BITS)\
{CSN_UINT_LH, _BITS, {(void*)1}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_UINT_LH, _BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_UINT_OFFSET(Par1, Par2, Par3, Par4)
@@ -356,7 +339,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* Par4: value added to the returned integer (offset)
*****************************************************************************/
#define M_UINT_OFFSET(_STRUCT, _MEMBER, _BITS, _OFFSET)\
{CSN_UINT_OFFSET, _BITS, {(void*)_OFFSET}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_UINT_OFFSET, _BITS, {(void*)_OFFSET}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_UINT_ARRAY(Par1, Par2, Par3, Par4)
@@ -367,7 +350,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* Par4: number of elements in the array (fixed integer value)
*****************************************************************************/
#define M_UINT_ARRAY(_STRUCT, _MEMBER, _BITS, _ElementCount)\
{CSN_UINT_ARRAY, _BITS, {(void*)_ElementCount}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_UINT_ARRAY, _BITS, {(void*)_ElementCount}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_VAR_UINT_ARRAY(Par1, Par2, Par3, Par4)
@@ -379,7 +362,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* structure member holding the length value
*****************************************************************************/
#define M_VAR_UINT_ARRAY(_STRUCT, _MEMBER, _BITS, _ElementCountField)\
{CSN_UINT_ARRAY, _BITS, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)1}}
{CSN_UINT_ARRAY, _BITS, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 1, NULL}
/******************************************************************************
* M_VAR_ARRAY(Par1, Par2, Par3, Par4)
@@ -390,7 +373,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* Par4: offset that is added to the Par3 to get the actual size of the array
*****************************************************************************/
#define M_VAR_ARRAY(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
{CSN_VARIABLE_ARRAY, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_VARIABLE_ARRAY, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_VAR_TARRAY(Par1, Par2, Par3, Par4)
@@ -401,32 +384,32 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* Par4: name of the structure member holding the size of the array
*****************************************************************************/
#define M_VAR_TARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
{CSN_VARIABLE_TARRAY, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
{CSN_VARIABLE_TARRAY, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), NULL}
/******************************************************************************
* M_VAR_TARRAY_OFFSET(Par1, Par2, Par3, Par4)
* Same as M_VAR_TARRAY with offset
*****************************************************************************/
#define M_VAR_TARRAY_OFFSET(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
{CSN_VARIABLE_TARRAY_OFFSET, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
{CSN_VARIABLE_TARRAY_OFFSET, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), NULL}
/******************************************************************************
* M_REC_ARRAY(Par1, Par2, Par3, Par4)
* similar to the M_VAR_ARRAY. The difference is that the size of the array is
* similar to the M_VAR_ARRAY. The difference is that the size of the array is
* not known in advance and it has to be calculated during unpacking. Its value
* is stored in a variable which belongs to the same structure as the array.
* A zero element terminates the array. The CSN.1 syntax describes it
* is stored in a variable which belongs to the same structure as the array.
* A zero element terminates the array. The CSN.1 syntax describes it
* recursively as:
* <array> ::={1 <element> <array>| 0}
* <array> ::={1 <element> <array>| 0}
*
* Par1: C structure name
* Par2: C structure element name
* Par3: name of the structure member where the calculated the size of the
* Par3: name of the structure member where the calculated the size of the
* array will be stored
* Par4: length of each element in bits
*****************************************************************************/
#define M_REC_ARRAY(_STRUCT, _MEMBER, _ElementCountField, _BITS)\
{CSN_RECURSIVE_ARRAY, _BITS, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_RECURSIVE_ARRAY, _BITS, {(const void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_VAR_TYPE_ARRAY(Par1, Par2, Par3, Par4)
@@ -437,7 +420,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* Par4: number of elements in the array (fixed integer value)
*****************************************************************************/
#define M_TYPE_ARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCount)\
{CSN_TYPE_ARRAY, _ElementCount, {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
{CSN_TYPE_ARRAY, _ElementCount, {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), NULL}
/******************************************************************************
* M_REC_TARRAY(Par1, Par2, Par3, Par4)
@@ -449,7 +432,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* Par4: will hold the number of element in the array after unpacking
*****************************************************************************/
#define M_REC_TARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
{CSN_RECURSIVE_TARRAY, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
{CSN_RECURSIVE_TARRAY, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), (void_fn_t)ElementsOf(((_STRUCT*)0)->_MEMBER)}
/******************************************************************************
* M_REC_TARRAY1(Par1, Par2, Par3, Par4)
@@ -457,7 +440,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* <list> ::= <type> {1 <type>} ** 0 ;
*****************************************************************************/
#define M_REC_TARRAY_1(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
{CSN_RECURSIVE_TARRAY_1, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
{CSN_RECURSIVE_TARRAY_1, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), (void_fn_t)ElementsOf(((_STRUCT*)0)->_MEMBER)}
/******************************************************************************
* M_REC_TARRAY2(Par1, Par2, Par3, Par4)
@@ -465,7 +448,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* <lists> ::= <type> { 0 <type> } ** 1 ;
*****************************************************************************/
#define M_REC_TARRAY_2(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
{CSN_RECURSIVE_TARRAY_2, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
{CSN_RECURSIVE_TARRAY_2, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), (void_fn_t)ElementsOf(((_STRUCT*)0)->_MEMBER)}
/******************************************************************************
* M_TYPE(Par1, Par2, Par3)
@@ -476,134 +459,161 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
* Par3: type of member
*****************************************************************************/
#define M_TYPE(_STRUCT, _MEMBER, _MEMBER_TYPE)\
{CSN_TYPE, 0, {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_TYPE, 0, {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_TYPE_OR_NULL(Par1, Par2, Par3)
* Similar to the M_TYPE except that not only the request set of bits but also the
* end of the message may be encountered when looking for the next element in
* the message.
* Covers the case {null | 0 | 1 < IE >}
*****************************************************************************/
#define M_TYPE_OR_NULL(_STRUCT, _MEMBER, _MEMBER_TYPE)\
{CSN_TYPE, 0, {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
/******************************************************************************
* M_UNION(Par1, Par2)
* Informs the CSN.1 library that a union follows and how many possible choices
* there are in the union. The actual value of the choice, which points out the
* chosen element of the union is stored in the uint8 variable and is usually
* called UnionType. The elements of the union have to be listed directly after
* chosen element of the union is stored in the uint8 variable and is usually
* called UnionType. The elements of the union have to be listed directly after
* the M_UNION statement.
* Par1: C structure name
* Par2: number of possible choice in the union
*****************************************************************************/
#define M_UNION(_STRUCT, _COUNT)\
{CSN_UNION, _COUNT, {0}, offsetof(_STRUCT, UnionType), FALSE, "UnionType", {(StreamSerializeFcn_t)0}}
{CSN_UNION, _COUNT, {0}, offsetof(_STRUCT, UnionType), FALSE, "UnionType", 0, NULL}
/******************************************************************************
* M_UNION_LH(Par1, Par2)
* Same as M_UNION but masked with background value 0x2B
*****************************************************************************/
#define M_UNION_LH(_STRUCT, _COUNT)\
{CSN_UNION_LH, _COUNT, {0}, offsetof(_STRUCT, UnionType), FALSE, "UnionType", {(StreamSerializeFcn_t)0}}
{CSN_UNION_LH, _COUNT, {0}, offsetof(_STRUCT, UnionType), FALSE, "UnionType", 0, NULL}
/******************************************************************************
* M_CHOICE(Par1, Par2, Par3, Par4)
* Similar to the M_UNION. In the M_UNION the selected element of all possible
* choices in the union is referred as a sequential numbers, i.e., the first
* choice is addressed as choice 0 the second as choice 1, the third as choice
* Similar to the M_UNION. In the M_UNION the selected element of all possible
* choices in the union is referred as a sequential numbers, i.e., the first
* choice is addressed as choice 0 the second as choice 1, the third as choice
* 2 and so on, both in the encoded message and in the variable UnionType which
* is the part of the message. In the CSN_CHOICE case, this rule does not
* apply. There is free but predefined mapping of the element of the union and
* is the part of the message. In the CSN_CHOICE case, this rule does not
* apply. There is free but predefined mapping of the element of the union and
* the value which addresses this element.
* The value of the address is called a selector.
* The value of the address is called a selector. Up to 256 (UCHAR_MAX) unique
* selectors can be handled, longer choice list would cause CSN_ERROR_IN_SCRIPT.
* After unpacking, this value is then converted to the sequential number of the
* element in the union and stored in the UnionType variable.
* element in the union and stored in the UnionType variable (Par2).
* Par1: C structure name
* Par2: C structure element name
* Par2: C structure field name holding sequential number of the chosen element.
* BEWARE! Never use an enumerated type here, because its length is
* compiler/machine dependent, while decoder would cast it to guint8.
* Par3: address of an array of type CSN_ChoiceElement_t where all possible
* values of the selector are provided, together with the selector
* length expressed in bits and the address of the CSN_DESCR type
* where the element is defined. For every element in the union
* there is one line in the Choice variable. These lines have to
* appear in the _CHOICE in the same order as the elements in the
* union. The element of the union selected in the message through
* the _CHOICE parameter is after unpacking translated to the
* corresponding sequential number of this element and stored in
* values of the selector are provided, together with the selector
* length expressed in bits and the address of the CSN_DESCR type
* where the element is defined. For every element in the union
* there is one line in the Choice variable. These lines have to
* appear in the _CHOICE in the same order as the elements in the
* union. The element of the union selected in the message through
* the _CHOICE parameter is after unpacking translated to the
* corresponding sequential number of this element and stored in
* the variable pointed out by the _MEMBER
* Par4: number of possible choices in the union
*****************************************************************************/
#define M_CHOICE(_STRUCT, _MEMBER, _CHOICE, _ElementCount)\
{CSN_CHOICE, _ElementCount, {(void*)_CHOICE}, offsetof(_STRUCT, _MEMBER), FALSE, #_CHOICE, {(StreamSerializeFcn_t)0}}
{CSN_CHOICE, _ElementCount, {(const void*)_CHOICE}, offsetof(_STRUCT, _MEMBER), FALSE, #_CHOICE, 0, NULL}
/******************************************************************************
* M_FIXED(Par1, Par2, Par3)
* Defines a fixed value of type integer which should be fetched from or stored
* Defines a fixed value of type integer which should be fetched from or stored
* in the message
* Par1: C structure name
* Par2: gives the length of the fixed number in bits.
* Par3: the value of the number. If the expected value is not present in
* Par3: the value of the number. If the expected value is not present in
* the message the unpacking procedure is aborted
*****************************************************************************/
#define M_FIXED(_STRUCT, _BITS, _BITVALUE)\
{CSN_FIXED, _BITS, {0}, _BITVALUE, FALSE, #_BITVALUE, {(StreamSerializeFcn_t)0}}
{CSN_FIXED, _BITS, {0}, _BITVALUE, FALSE, #_BITVALUE, 0, NULL}
/******************************************************************************
* M_SERIALIZE(Par1, Par2, Par3)
* Allows using a complete free format of data being encoded or decoded.
* Allows using a complete free format of data being encoded or decoded.
* When the M_SERIALIZE is encounted during encoding or decoding of a message
* the CSNstream program passes the control over to the specified function
* together with all necessary parameters about the current position within
* the message being unpacked or packed. When transferring of "serialized"
* data to or from the message is finished by the function the CSNstream gets
* the CSNstream program passes the control over to the specified function
* together with all necessary parameters about the current position within
* the message being unpacked or packed. When transferring of "serialized"
* data to or from the message is finished by the function the CSNstream gets
* back control over the data stream and continues to work with the message.
*****************************************************************************/
#define M_SERIALIZE(_STRUCT, _MEMBER, _LENGTH_LEN, _SERIALIZEFCN)\
{CSN_SERIALIZE, _LENGTH_LEN, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {_SERIALIZEFCN}}
{CSN_SERIALIZE, _LENGTH_LEN, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, (void_fn_t)_SERIALIZEFCN}
#define M_CALLBACK(_STRUCT, _CSNCALLBACKFCN, _PARAM1, _PARAM2)\
{CSN_CALLBACK, offsetof(_STRUCT, _PARAM1), {_CSNCALLBACKFCN}, offsetof(_STRUCT, _PARAM2), FALSE, "CallBack_"#_CSNCALLBACKFCN, {(StreamSerializeFcn_t)0}}
{CSN_CALLBACK, offsetof(_STRUCT, _PARAM1), {0}, offsetof(_STRUCT, _PARAM2), FALSE, "CallBack_"#_CSNCALLBACKFCN, 0, (void_fn_t)_CSNCALLBACKFCN}
/******************************************************************************
* M_BITMAP(Par1, Par2, Par3)
* Defines a type which consists of a bitmap. The size of the bitmap in bits
* Defines a type which consists of a bitmap. The size of the bitmap in bits
* is fixed and provided by the parameter Par3
* Par1: C structure name
* Par2: C structure element name
* Par3: length of the bitmap expressed in bits
*****************************************************************************/
#define M_BITMAP(_STRUCT, _MEMBER, _BITS)\
{CSN_BITMAP, _BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_BITMAP, _BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/* variable length, right aligned bitmap i.e. _ElementCountField = 11 => 00000111 11111111 */
#define M_VAR_BITMAP(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
{CSN_VARIABLE_BITMAP, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_VARIABLE_BITMAP, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/* variable length, right aligned bitmap filling the rest of message
* - when unpacking the _ElementCountField will be set in runtime
* - when packing _ElementCountField contains the size of bitmap
*/
#define M_VAR_BITMAP_1(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
{CSN_VARIABLE_BITMAP_1, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_VARIABLE_BITMAP_1, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/* variable length, left aligned bitmap i.e. _ElementCountField = 11 => 11111111 11100000 */
#define M_LEFT_VAR_BMP(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
{CSN_LEFT_ALIGNED_VAR_BMP, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_LEFT_ALIGNED_VAR_BMP, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/* variable length, left aligned bitmap filling the rest of message
*- when unpacking the _ElementCountField will be set in runtime
* - when packing _ElementCountField contains the size of bitmap
*/
#define M_LEFT_VAR_BMP_1(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
{CSN_LEFT_ALIGNED_VAR_BMP_1, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
{CSN_LEFT_ALIGNED_VAR_BMP_1, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
/* todo: dissect padding bits looking for unexpected extensions */
#define M_PADDING_BITS(_STRUCT)\
{CSN_PADDING_BITS, 0, {0}, 0, TRUE, "Padding", {(StreamSerializeFcn_t)0}}
{CSN_PADDING_BITS, 0, {0}, 0, TRUE, "Padding", 0, NULL}
#define M_NULL(_STRUCT, _MEMBER)\
{CSN_NULL, 0, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
#define M_NULL(_STRUCT, _MEMBER, _SKIP_BITS)\
{CSN_NULL, _SKIP_BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
#define M_THIS_EXIST(_STRUCT)\
{CSN_EXIST, 0, {0}, offsetof(_STRUCT, Exist), FALSE, "Exist", {(StreamSerializeFcn_t)0}}
{CSN_EXIST, 0, {0}, offsetof(_STRUCT, Exist), FALSE, "Exist", 0, NULL}
#define M_THIS_EXIST_LH(_STRUCT)\
{CSN_EXIST_LH, 0, {0}, offsetof(_STRUCT, Exist), FALSE, "Exist", {(StreamSerializeFcn_t)0}}
{CSN_EXIST_LH, 0, {0}, offsetof(_STRUCT, Exist), FALSE, "Exist", 0, NULL}
/* return value 0 if ok else discontionue the unpacking */
typedef gint16 (*CsnCallBackFcn_t)(void* pv ,...);
#define CSNDESCR(_FuncType) CSNDESCR_##_FuncType
#define pvDATA(_pv, _offset) ((void*) ((unsigned char*)_pv + _offset))
#define pui8DATA(_pv, _offset) ((guint8*) pvDATA(_pv, _offset))
#define pui16DATA(_pv, _offset) ((guint16*) pvDATA(_pv, _offset))
#define pui32DATA(_pv, _offset) ((guint32*) pvDATA(_pv, _offset))
#define pui64DATA(_pv, _offset) ((guint64*) pvDATA(_pv, _offset))
/* used to tag existence of next element in variable length lists */
#define STANDARD_TAG 1
#define REVERSED_TAG 0
gint16 ProcessError_impl(const char *file, int line, unsigned *readIndex,
const char* sz, gint16 err, const CSN_DESCR* pDescr);
#define ProcessError(readIndex, sz, err, pDescr) \
ProcessError_impl(__FILE__, __LINE__, readIndex, sz, err, pDescr)
#endif /*_PACKET_CSN1_H_*/

1428
src/csn1_dec.c Normal file

File diff suppressed because it is too large Load Diff

1305
src/csn1_enc.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -77,7 +77,7 @@ static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len,
/* TS 44.060, table 10.4.14a.1, row 3 & 5 */
/* only filling bytes left */
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains "
"only filling bytes with extention octet: LI=%d, E=%d, count=%d\n",
"only filling bytes with extension octet: LI=%d, E=%d, count=%d\n",
li->li, li->e, num_chunks);
break;
} else if (li->li > 0) {
@@ -92,7 +92,7 @@ static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len,
}
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains "
"extention octet: LI=%d, E=%d, count=%d\n",
"extension octet: LI=%d, E=%d, count=%d\n",
li->li, li->e, num_chunks);
num_chunks += 1;
@@ -168,7 +168,7 @@ static int parse_extensions_gprs(const uint8_t *data, unsigned int data_len,
chunks[num_chunks].is_complete = li->li || is_last_block;
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains "
"extention octet: LI=%d, M=%d, E=%d, count=%d\n",
"extension octet: LI=%d, M=%d, E=%d, count=%d\n",
li->li, li->m, li->e, num_chunks);
num_chunks += 1;
@@ -189,7 +189,7 @@ static int parse_extensions_gprs(const uint8_t *data, unsigned int data_len,
}
int Decoding::rlc_data_from_ul_data(
const struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
const struct gprs_rlc_data_block_info *rdbi, enum CodingScheme cs,
const uint8_t *data, RlcData *chunks, unsigned int chunks_size,
uint32_t *tlli)
{
@@ -215,7 +215,7 @@ int Decoding::rlc_data_from_ul_data(
"but no more chunks possible\n");
return -ENOSPC;
}
} else if (cs.isEgprs()) {
} else if (mcs_is_edge(cs)) {
/* if E is not set (LI follows), EGPRS */
num_chunks = parse_extensions_egprs(data, data_len, &offs,
is_last_block,
@@ -240,7 +240,7 @@ int Decoding::rlc_data_from_ul_data(
}
memcpy(&tlli_enc, data + offs, sizeof(tlli_enc));
if (cs.isGprs())
if (mcs_is_gprs(cs))
/* The TLLI is encoded in big endian for GPRS (see
* TS 44.060, figure 10.2.2.1, note) */
*tlli = be32toh(tlli_enc);
@@ -282,8 +282,8 @@ int Decoding::rlc_data_from_ul_data(
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);
"border, chunk idx: %d, offset: %u, size: %d, data_len: %u\n",
i, offs, chunks[i].length, data_len);
return -EINVAL;
}
}
@@ -291,7 +291,7 @@ int Decoding::rlc_data_from_ul_data(
return num_chunks;
}
uint8_t Decoding::get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
{
int i;
@@ -306,7 +306,7 @@ uint8_t Decoding::get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
return 0;
}
uint8_t Decoding::get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
uint8_t get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
{
int i;
@@ -326,18 +326,6 @@ uint8_t Decoding::get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t
* 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;
@@ -351,26 +339,26 @@ void Decoding::extract_rbb(const struct bitvec *rbb, char *show_rbb)
}
int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
const uint8_t *data, GprsCodingScheme cs)
const uint8_t *data, enum CodingScheme cs)
{
unsigned int cur_bit = 0;
switch(cs.headerTypeData()) {
case GprsCodingScheme::HEADER_GPRS_DATA :
switch(mcs_header_type(cs)) {
case HEADER_GPRS_DATA :
cur_bit = rlc_parse_ul_data_header_gprs(rlc, data, cs);
break;
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3 :
case HEADER_EGPRS_DATA_TYPE_3 :
cur_bit = rlc_parse_ul_data_header_egprs_type_3(rlc, data, cs);
break;
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2 :
case HEADER_EGPRS_DATA_TYPE_2 :
cur_bit = rlc_parse_ul_data_header_egprs_type_2(rlc, data, cs);
break;
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1 :
case HEADER_EGPRS_DATA_TYPE_1 :
cur_bit = rlc_parse_ul_data_header_egprs_type_1(rlc, data, cs);
break;
default:
LOGP(DRLCMACDL, LOGL_ERROR,
"Decoding of uplink %s data blocks not yet supported.\n",
cs.name());
mcs_name(cs));
return -ENOTSUP;
};
@@ -380,7 +368,7 @@ int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
int Decoding::rlc_parse_ul_data_header_egprs_type_3(
struct gprs_rlc_data_info *rlc,
const uint8_t *data,
const GprsCodingScheme &cs)
const enum CodingScheme &cs)
{
int punct, punct2, with_padding, cps;
unsigned int e_ti_header, offs, cur_bit = 0;
@@ -414,7 +402,7 @@ int Decoding::rlc_parse_ul_data_header_egprs_type_3(
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
cur_bit += 2;
/* skip data area */
cur_bit += cs.maxDataBlockBytes() * 8;
cur_bit += mcs_max_data_block_bytes(cs) * 8;
return cur_bit;
}
@@ -422,7 +410,7 @@ int Decoding::rlc_parse_ul_data_header_egprs_type_3(
int Decoding::rlc_parse_ul_data_header_egprs_type_2(
struct gprs_rlc_data_info *rlc,
const uint8_t *data,
const GprsCodingScheme &cs)
const enum CodingScheme &cs)
{
const struct gprs_rlc_ul_header_egprs_2 *egprs2;
unsigned int e_ti_header, offs, cur_bit = 0;
@@ -458,14 +446,14 @@ int Decoding::rlc_parse_ul_data_header_egprs_type_2(
cur_bit += 2;
/* skip data area */
cur_bit += cs.maxDataBlockBytes() * 8;
cur_bit += mcs_max_data_block_bytes(cs) * 8;
return cur_bit;
}
int Decoding::rlc_parse_ul_data_header_egprs_type_1(
struct gprs_rlc_data_info *rlc,
const uint8_t *data, const GprsCodingScheme &cs)
const uint8_t *data, const enum CodingScheme &cs)
{
struct gprs_rlc_ul_header_egprs_1 *egprs1;
unsigned int e_ti_header, cur_bit = 0, offs;
@@ -517,13 +505,13 @@ int Decoding::rlc_parse_ul_data_header_egprs_type_1(
rlc->block_info[1].ti = !!(e_ti_header & 0x02);
cur_bit += 2;
/* skip data area */
cur_bit += cs.maxDataBlockBytes() * 8;
cur_bit += mcs_max_data_block_bytes(cs) * 8;
return cur_bit;
}
int Decoding::rlc_parse_ul_data_header_gprs(struct gprs_rlc_data_info *rlc,
const uint8_t *data, const GprsCodingScheme &cs)
const uint8_t *data, const enum CodingScheme &cs)
{
const struct rlc_ul_header *gprs;
unsigned int cur_bit = 0;
@@ -547,7 +535,7 @@ int Decoding::rlc_parse_ul_data_header_gprs(struct gprs_rlc_data_info *rlc,
rlc->block_info[0].spb = 0;
cur_bit += rlc->data_offs_bits[0];
/* skip data area */
cur_bit += cs.maxDataBlockBytes() * 8;
cur_bit += mcs_max_data_block_bytes(cs) * 8;
return cur_bit;
}

View File

@@ -19,13 +19,23 @@
*/
#pragma once
#include <gsm_rlcmac.h>
#include "rlc.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "gsm_rlcmac.h"
#include "coding_scheme.h"
#ifdef __cplusplus
}
#endif
#include <stdint.h>
struct bitvec;
#ifdef __cplusplus
class Decoding {
public:
/* represents (parts) LLC PDUs within one RLC Data block */
@@ -37,31 +47,28 @@ public:
static int rlc_data_from_ul_data(
const struct gprs_rlc_data_block_info *rdbi,
GprsCodingScheme cs, const uint8_t *data, RlcData *chunks,
enum CodingScheme cs, const uint8_t *data, RlcData *chunks,
unsigned int chunks_size, uint32_t *tlli);
static uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
static uint8_t get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
static void extract_rbb(const uint8_t *rbb, char *extracted_rbb);
static void extract_rbb(const struct bitvec *rbb, char *show_rbb);
static int rlc_parse_ul_data_header_egprs_type_3(
struct gprs_rlc_data_info *rlc,
const uint8_t *data,
const GprsCodingScheme &cs);
const enum CodingScheme &cs);
static int rlc_parse_ul_data_header_egprs_type_2(
struct gprs_rlc_data_info *rlc,
const uint8_t *data,
const GprsCodingScheme &cs);
const enum CodingScheme &cs);
static int rlc_parse_ul_data_header_egprs_type_1(
struct gprs_rlc_data_info *rlc,
const uint8_t *data,
const GprsCodingScheme &cs);
const enum CodingScheme &cs);
static int rlc_parse_ul_data_header_gprs(
struct gprs_rlc_data_info *rlc,
const uint8_t *data,
const GprsCodingScheme &cs);
const enum CodingScheme &cs);
static int rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
const uint8_t *data, GprsCodingScheme cs);
const uint8_t *data, enum CodingScheme cs);
static unsigned int rlc_copy_to_aligned_buffer(
const struct gprs_rlc_data_info *rlc,
unsigned int data_block_idx,
@@ -79,3 +86,16 @@ public:
bitvec *bits, int *bsn_begin, int *bsn_end,
gprs_rlc_dl_window *window);
};
#endif /* #ifdef __cplusplus */
#ifdef __cplusplus
extern "C" {
#endif
uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
uint8_t get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
#ifdef __cplusplus
}
#endif

View File

@@ -449,6 +449,7 @@ const char *zero_run_len_code_list[EGPRS_CODEWORDS] = {
/* Calculate runlength of a codeword
* \param root[in] Root of Ones or Zeros tree
* \param bmbuf[in] Received compressed bitmap buf
* \param length[in] Length of bitmap buf in bits
* \param bit_pos[in] The start bit pos to read codeword
* \param len_codewd[in] Length of code word
* \param rlen[out] Calculated run length
@@ -456,6 +457,7 @@ const char *zero_run_len_code_list[EGPRS_CODEWORDS] = {
static int search_runlen(
egprs_compress_node *root,
const uint8_t *bmbuf,
uint8_t length,
uint8_t bit_pos,
uint8_t *len_codewd,
uint16_t *rlen)
@@ -469,6 +471,9 @@ static int search_runlen(
while (iter->run_length == -1) {
if ((!iter->left) && (!iter->right))
return -1;
if (bit_pos >= length)
return -1;
/* get the bit value at the bitpos and put it in right most of dir */
dir = (bmbuf[bit_pos/8] >> (7 - (bit_pos & 0x07))) & 0x01;
bit_pos++;
@@ -498,7 +503,7 @@ int egprs_compress::decompress_crbb(
const uint8_t *orig_crbb_buf,
bitvec *dest)
{
int8_t remaining_bmap_len = compress_bmap_len;
uint8_t bit_pos = 0;
uint8_t data;
egprs_compress_node *list = NULL;
@@ -509,7 +514,7 @@ int egprs_compress::decompress_crbb(
int rc = 0;
egprs_compress *compress = instance();
while (compress_bmap_len > 0) {
while (remaining_bmap_len > 0) {
if (start) {
data = 0xff;
list = compress->ones_list;
@@ -517,7 +522,7 @@ int egprs_compress::decompress_crbb(
data = 0x00;
list = compress->zeros_list;
}
rc = search_runlen(list, orig_crbb_buf,
rc = search_runlen(list, orig_crbb_buf, compress_bmap_len,
bit_pos, &nbits, &run_length);
if (rc == -1)
return -1;
@@ -525,6 +530,7 @@ int egprs_compress::decompress_crbb(
if (run_length < 64)
start = !start;
cbmaplen = cbmaplen + run_length;
/* put run length of Ones in uncompressed bitmap */
while (run_length != 0) {
if (run_length > 8) {
@@ -536,7 +542,7 @@ int egprs_compress::decompress_crbb(
}
}
bit_pos = bit_pos + nbits;
compress_bmap_len = compress_bmap_len - nbits;
remaining_bmap_len = remaining_bmap_len - nbits;
}
return 0;
}
@@ -618,7 +624,7 @@ int egprs_compress::compress_rbb(
struct bitvec *urbb_vec,
struct bitvec *crbb_vec,
uint8_t *uclen_crbb, /* Uncompressed bitmap len in CRBB */
uint8_t max_bits) /* max remaining bits */
uint8_t max_bits) /* max remaining bits */
{
bool run_len_bit;
int buflen = urbb_vec->cur_bit;
@@ -684,8 +690,7 @@ int egprs_compress::compress_rbb(
if (clen >= uclen)
/* No Gain is observed, So no need to compress */
return 0;
else
LOGP(DRLCMACUL, LOGL_DEBUG, "CRBB bitmap = %s\n", osmo_hexdump(crbb_vec->data, (crbb_vec->cur_bit+7)/8));
/* Add compressed bitmap to final buffer */
return 1;
LOGP(DRLCMACUL, LOGL_DEBUG, "CRBB bitmap = %s\n", osmo_hexdump(crbb_vec->data, (crbb_vec->cur_bit+7)/8));
/* Add compressed bitmap to final buffer */
return 1;
}

View File

@@ -17,7 +17,7 @@ public:
egprs_compress();
int osmo_t4_compress(struct bitvec *bv);
static int compress_rbb(struct bitvec *urbb_vec, struct bitvec *crbb_vec,
uint8_t *uclen_crbb, uint8_t max_bits);
uint8_t *uclen_crbb, uint8_t max_bits);
private:
egprs_compress_node *ones_list;

File diff suppressed because it is too large Load Diff

View File

@@ -21,18 +21,23 @@
#pragma once
#include <stdint.h>
#include <gsm_rlcmac.h>
#include <gprs_coding_scheme.h>
extern "C" {
#include <osmocom/gsm/l1sap.h>
}
struct gprs_rlcmac_bts;
#ifdef __cplusplus
extern "C" {
#endif
#include <osmocom/gsm/l1sap.h>
#include "coding_scheme.h"
#include "gsm_rlcmac.h"
#ifdef __cplusplus
}
#endif
struct gprs_rlcmac_tbf;
struct bitvec;
struct gprs_llc;
struct gprs_rlc_data_block_info;
#ifdef __cplusplus
/**
* I help with encoding data into CSN1 messages.
* TODO: Nobody can remember a function signature like this. One should
@@ -42,47 +47,25 @@ struct gprs_rlc_data_block_info;
class Encoding {
public:
static int write_immediate_assignment(
const struct gprs_rlcmac_pdch *pdch,
struct gprs_rlcmac_tbf *tbf,
bitvec * dest, uint8_t downlink, uint16_t ra,
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts,
uint8_t tsc, uint8_t usf, uint8_t polling,
bitvec * dest, bool downlink, uint16_t ra,
uint32_t ref_fn, uint8_t ta,
uint8_t usf, bool polling,
uint32_t fn, uint8_t alpha, uint8_t gamma,
int8_t ta_idx,
enum ph_burst_type burst_type =
GSM_L1_BURST_TYPE_ACCESS_0,
uint8_t sb = 1);
enum ph_burst_type burst_type);
static int write_immediate_assignment_reject(
bitvec *dest, uint16_t ra,
uint32_t ref_fn,
enum ph_burst_type burst_type
enum ph_burst_type burst_type,
uint8_t t3142
);
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 encode_rbb(const char *show_rbb, bitvec *rbb);
static void write_packet_downlink_assignment(RlcMacDownlink_t * block,
bool old_tfi_is_valid, uint8_t old_tfi, uint8_t old_downlink,
struct gprs_rlcmac_dl_tbf *tbf, uint8_t poll, uint8_t rrbp,
uint8_t alpha, uint8_t gamma,
int8_t ta_idx, uint8_t ta_ts, bool use_egprs);
static void encode_rbb(const char *show_rbb, uint8_t *rbb);
static void write_packet_access_reject(
bitvec * dest, uint32_t tlli);
static void write_packet_uplink_ack(
struct gprs_rlcmac_bts *bts, bitvec * dest,
struct gprs_rlcmac_ul_tbf *tbf, bool is_final,
uint8_t rrbp);
static int write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len);
static int write_paging_request(bitvec * dest, const struct osmo_mobile_identity *mi);
static unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
uint8_t *identity, uint8_t chan_needed);
@@ -104,7 +87,45 @@ public:
};
static AppendResult rlc_data_to_dl_append(
struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
struct gprs_rlc_data_block_info *rdbi, enum CodingScheme cs,
gprs_llc *llc, int *offset, int *num_chunks,
uint8_t *data, bool is_final, int *count_payload);
static void rlc_data_to_dl_append_egprs_li_padding(
const struct gprs_rlc_data_block_info *rdbi,
int *offset, int *num_chunks, uint8_t *data_block);
};
#endif /* ifdef __cplusplus */
#ifdef __cplusplus
extern "C" {
#endif
void write_packet_access_reject(struct bitvec *dest, uint32_t tlli, unsigned long t3172_ms);
void write_packet_uplink_assignment(RlcMacDownlink_t *block, uint8_t old_tfi,
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
const struct gprs_rlcmac_ul_tbf *tbf, uint8_t poll,
uint8_t rrbp, uint8_t alpha, uint8_t gamma, int8_t ta_idx,
bool use_egprs);
void write_packet_downlink_assignment(RlcMacDownlink_t * block, bool old_tfi_is_valid,
uint8_t old_tfi, uint8_t old_downlink,
const struct gprs_rlcmac_dl_tbf *tbf, uint8_t poll,
uint8_t rrbp, uint8_t alpha, uint8_t gamma,
int8_t ta_idx, uint8_t ta_ts, bool use_egprs,
uint8_t control_ack);
void write_packet_uplink_ack(struct bitvec *dest, struct gprs_rlcmac_ul_tbf *tbf,
bool is_final, uint8_t rrbp);
void write_packet_neighbour_cell_data(RlcMacDownlink_t *block,
bool tfi_is_dl, uint8_t tfi, uint8_t container_id,
uint8_t container_idx, PNCDContainer_t *container);
void write_packet_cell_change_continue(RlcMacDownlink_t *block, uint8_t poll, uint8_t rrbp,
bool tfi_is_dl, uint8_t tfi, bool exist_id,
uint16_t arfcn, uint8_t bsic, uint8_t container_id);
#ifdef __cplusplus
}
#endif

1362
src/gprs_bssgp_pcu.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -21,43 +21,57 @@
#ifndef GPRS_BSSGP_PCU_H
#define GPRS_BSSGP_PCU_H
#ifdef __cplusplus
extern "C" {
#endif
#include <osmocom/core/talloc.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/signal.h>
#include <osmocom/core/application.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_ns2.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_bssgp_bss.h>
#include <osmocom/gprs/gprs_msgb.h>
struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
}
#include <gprs_debug.h>
#include <time.h>
#include <unistd.h>
#define QOS_PROFILE 4
#define BSSGP_HDR_LEN 53
#define NS_HDR_LEN 4
#define IE_LLC_PDU 14
enum sgsn_counter_id {
SGSN_CTR_RX_PAGING_CS,
SGSN_CTR_RX_PAGING_PS,
};
struct gprs_bssgp_pcu {
struct gprs_nsvc *nsvc;
struct bssgp_bvc_ctx *bctx;
struct gprs_rlcmac_bts *bts;
struct osmo_timer_list bvc_timer;
struct rate_ctr_group *ctrs;
/* state: is the NSVC unblocked? */
int nsvc_unblocked;
/* state: true if bvc signalling needs to be reseted or waiting for reset ack */
int bvc_sig_reset;
/* state: true if bvc ptp needs to be reseted or waiting for reset ack */
int bvc_reset;
/* state: true if bvc ptp is unblocked */
int bvc_unblocked;
/* Flow control */
struct timeval queue_delay_sum;
struct timespec queue_delay_sum;
unsigned queue_delay_count;
uint8_t fc_tag;
unsigned queue_frames_sent;
@@ -74,20 +88,28 @@ struct gprs_bssgp_pcu {
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, bool mnc_3_digits,
uint16_t lac, uint16_t rac, uint16_t cell_id);
void gprs_bssgp_destroy(void);
int gprs_ns_reconnect(struct gprs_nsvc *nsvc);
struct bssgp_bvc_ctx *gprs_bssgp_pcu_current_bctx(void);
void gprs_bssgp_update_queue_delay(const struct timeval *tv_recv,
const struct timeval *tv_now);
int gprs_gp_send_cb(void *ctx, struct msgb *msg);
int gprs_ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx);
void gprs_bssgp_update_queue_delay(const struct timespec *tv_recv,
const struct timespec *tv_now);
void gprs_bssgp_update_frames_sent();
void gprs_bssgp_update_bytes_received(unsigned bytes_recv, unsigned frames_recv);
struct gprs_bssgp_pcu *gprs_bssgp_init(
struct gprs_rlcmac_bts *bts,
uint16_t nsei, uint16_t bvci,
uint16_t mcc, uint16_t mnc, bool mnc_3_digits,
uint16_t lac, uint16_t rac, uint16_t cell_id);
int gprs_ns_update_config(struct gprs_rlcmac_bts *bts, uint16_t nsei,
const struct osmo_sockaddr *local,
const struct osmo_sockaddr *remote,
uint16_t *nsvci, uint16_t valid);
void gprs_bssgp_destroy(struct gprs_rlcmac_bts *bts);
#ifdef __cplusplus
}
#endif
#endif // GPRS_BSSGP_PCU_H

289
src/gprs_bssgp_rim.c Normal file
View File

@@ -0,0 +1,289 @@
/* gprs_bssgp_pcu.cpp
*
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_bssgp_rim.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/core/prim.h>
#include <pcu_l1_if.h>
#include <gprs_rlcmac.h>
#include <bts.h>
#include "gprs_debug.h"
#include "gprs_pcu.h"
#include "bts.h"
#include "gprs_ms.h"
#include "nacc_fsm.h"
#define LOGPRIM(nsei, level, fmt, args...) \
LOGP(DRIM, level, "(NSEI=%u) " fmt, nsei, ## args)
static inline void gprs_ra_id_ci_to_cgi_ps(struct osmo_cell_global_id_ps *cgi_ps,
const struct gprs_ra_id *raid, uint16_t cid)
{
*cgi_ps = (struct osmo_cell_global_id_ps) {
.rai.lac.plmn.mcc = raid->mcc,
.rai.lac.plmn.mnc = raid->mnc,
.rai.lac.plmn.mnc_3_digits = raid->mnc_3_digits,
.rai.lac.lac = raid->lac,
.rai.rac = raid->rac,
.cell_identity = cid,
};
}
/* Mirror RIM routing information of a given PDU, see also 3GPP TS 48.018, section 8c.1.4.3 */
static void mirror_rim_routing_info(struct bssgp_ran_information_pdu *to_pdu,
const struct bssgp_ran_information_pdu *from_pdu)
{
memcpy(&to_pdu->routing_info_dest, &from_pdu->routing_info_src, sizeof(to_pdu->routing_info_dest));
memcpy(&to_pdu->routing_info_src, &from_pdu->routing_info_dest, sizeof(to_pdu->routing_info_src));
}
/* Fill NACC application container with data (cell identifier, sysinfo) */
#define SI_HDR_LEN 2
static void fill_app_cont_nacc(struct bssgp_ran_inf_app_cont_nacc *app_cont, const struct gprs_rlcmac_bts *bts)
{
struct bssgp_bvc_ctx *bctx = the_pcu->bssgp.bctx;
gprs_ra_id_ci_to_cgi_ps(&app_cont->reprt_cell, &bctx->ra_id, bctx->cell_id);
app_cont->num_si = 0;
/* Note: The BTS struct stores the system information including its pseudo header. The RIM application
* container defines the system information without pseudo header, so we need to chop it off. */
if (bts->si1_is_set) {
app_cont->si[app_cont->num_si] = bts->si1 + SI_HDR_LEN;
app_cont->num_si++;
}
if (bts->si3_is_set) {
app_cont->si[app_cont->num_si] = bts->si3 + SI_HDR_LEN;
app_cont->num_si++;
}
if (bts->si13_is_set) {
app_cont->si[app_cont->num_si] = bts->si13 + SI_HDR_LEN;
app_cont->num_si++;
}
/* Note: It is possible that the resulting PDU will not contain any system information, even if this is
* an unlikely case since the BTS immediately updates the system information after startup. The
* specification permits to send zero system information, see also: 3GPP TS 48.018 section 11.3.63.2.1 */
if (!bts->si1_is_set || !bts->si3_is_set || !bts->si13_is_set)
LOGP(DNACC, LOGL_INFO, "TX RAN INFO RESPONSE (NACC) %s: Some SI are missing:%s%s%s\n",
osmo_cgi_ps_name(&app_cont->reprt_cell),
bts->si1_is_set ? "" : " SI1",
bts->si3_is_set ? "" : " SI3",
bts->si13_is_set ? "" : " SI13");
}
/* Format a RAN INFORMATION PDU that contains the requested system information */
static void format_response_pdu(struct bssgp_ran_information_pdu *resp_pdu,
const struct bssgp_ran_information_pdu *req_pdu,
const struct gprs_rlcmac_bts *bts)
{
memset(resp_pdu, 0, sizeof(*resp_pdu));
mirror_rim_routing_info(resp_pdu, req_pdu);
resp_pdu->decoded.rim_cont = (struct bssgp_ran_inf_rim_cont) {
.app_id = BSSGP_RAN_INF_APP_ID_NACC,
.seq_num = 1, /* single report has only one message in response */
.pdu_ind = {
.pdu_type_ext = RIM_PDU_TYPE_SING_REP,
},
.prot_ver = 1,
};
fill_app_cont_nacc(&resp_pdu->decoded.rim_cont.u.app_cont_nacc, bts);
resp_pdu->decoded_present = true;
resp_pdu->rim_cont_iei = BSSGP_IE_RI_RIM_CONTAINER;
}
/* Format a RAN INFORMATION ERROR PDU */
static void format_response_pdu_err(struct bssgp_ran_information_pdu *resp_pdu,
const struct bssgp_ran_information_pdu *req_pdu)
{
memset(resp_pdu, 0, sizeof(*resp_pdu));
mirror_rim_routing_info(resp_pdu, req_pdu);
resp_pdu->decoded.err_rim_cont = (struct bssgp_ran_inf_err_rim_cont) {
.app_id = BSSGP_RAN_INF_APP_ID_NACC,
.prot_ver = 1,
.err_pdu = req_pdu->rim_cont,
.err_pdu_len = req_pdu->rim_cont_len,
};
resp_pdu->decoded_present = true;
resp_pdu->rim_cont_iei = BSSGP_IE_RI_ERROR_RIM_COINTAINER;
}
static int handle_ran_info_response_nacc(const struct bssgp_ran_inf_app_cont_nacc *nacc, struct gprs_rlcmac_bts *bts)
{
struct si_cache_value val;
struct si_cache_entry *entry;
struct llist_head *tmp;
int i;
LOGP(DRIM, LOGL_INFO, "Rx RAN-INFO cell=%s type=%sBCCH num_si=%d\n",
osmo_cgi_ps_name(&nacc->reprt_cell),
nacc->type_psi ? "P" : "", nacc->num_si);
val.type_psi = nacc->type_psi;
val.si_len = 0;
for (i = 0; i < nacc->num_si; i++) {
size_t len = val.type_psi ? BSSGP_RIM_PSI_LEN : BSSGP_RIM_SI_LEN;
memcpy(&val.si_buf[val.si_len], nacc->si[i], len);
val.si_len += len;
}
entry = si_cache_add(bts->pcu->si_cache, &nacc->reprt_cell, &val);
llist_for_each(tmp, bts_ms_list(bts)) {
struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
if (ms->nacc && nacc_fsm_is_waiting_si_resolution(ms->nacc, &nacc->reprt_cell))
osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_SI, entry);
}
return 0;
}
static int handle_ran_info_request(const struct bssgp_ran_information_pdu *pdu,
struct gprs_rlcmac_bts *bts, uint16_t nsei)
{
const struct bssgp_ran_inf_req_rim_cont *ri_req = &pdu->decoded.req_rim_cont;
const struct bssgp_ran_inf_req_app_cont_nacc *nacc_req;
struct bssgp_ran_information_pdu resp_pdu;
int rc;
/* Check if we support the application ID */
if (ri_req->app_id != BSSGP_RAN_INF_APP_ID_NACC) {
LOGPRIM(nsei, LOGL_ERROR,
"Rx RAN-INFO-REQ for cell %s with unknown/wrong application ID %s -- reject.\n",
osmo_cgi_ps_name(&bts->cgi_ps), bssgp_ran_inf_app_id_str(ri_req->app_id));
format_response_pdu_err(&resp_pdu, pdu);
rc = -ENOTSUP;
goto tx_ret;
}
nacc_req = &ri_req->u.app_cont_nacc;
rc = osmo_cgi_ps_cmp(&bts->cgi_ps, &nacc_req->reprt_cell);
if (rc != 0) {
LOGPRIM(nsei, LOGL_ERROR, "reporting cell in RIM application container %s "
"does not match destination cell in RIM routing info %s -- rejected.\n",
osmo_cgi_ps_name(&nacc_req->reprt_cell),
osmo_cgi_ps_name2(&bts->cgi_ps));
format_response_pdu_err(&resp_pdu, pdu);
} else {
LOGPRIM(nsei, LOGL_INFO, "Responding to RAN INFORMATION REQUEST %s ...\n",
osmo_cgi_ps_name(&nacc_req->reprt_cell));
format_response_pdu(&resp_pdu, pdu, bts);
}
tx_ret:
bssgp_tx_rim(&resp_pdu, nsei);
return rc;
}
static int handle_ran_info_response(const struct bssgp_ran_information_pdu *pdu, struct gprs_rlcmac_bts *bts)
{
const struct bssgp_ran_inf_rim_cont *ran_info = &pdu->decoded.rim_cont;
char ri_src_str[64];
/* Check if we support the application ID */
if (ran_info->app_id != BSSGP_RAN_INF_APP_ID_NACC) {
LOGP(DRIM, LOGL_ERROR,
"Rx RAN-INFO for cell %s with unknown/wrong application ID %s received -- reject.\n",
osmo_cgi_ps_name(&bts->cgi_ps), bssgp_ran_inf_app_id_str(ran_info->app_id));
return -1;
}
if (ran_info->app_err) {
LOGP(DRIM, LOGL_ERROR,
"%s Rx RAN-INFO with an app error! cause: %s\n",
bssgp_rim_ri_name_buf(ri_src_str, sizeof(ri_src_str), &pdu->routing_info_src),
bssgp_nacc_cause_str(ran_info->u.app_err_cont_nacc.nacc_cause));
return -1;
}
handle_ran_info_response_nacc(&ran_info->u.app_cont_nacc, bts);
return 0;
}
int handle_rim(struct osmo_bssgp_prim *bp)
{
struct msgb *msg = bp->oph.msg;
uint16_t nsei = msgb_nsei(msg);
struct bssgp_ran_information_pdu *pdu = &bp->u.rim_pdu;
struct bssgp_ran_information_pdu resp_pdu;
struct osmo_cell_global_id_ps dst_addr;
struct gprs_rlcmac_bts *bts;
OSMO_ASSERT (bp->oph.sap == SAP_BSSGP_RIM);
/* At the moment we only support GERAN, so we block all other network
* types here. */
if (pdu->routing_info_dest.discr != BSSGP_RIM_ROUTING_INFO_GERAN) {
LOGPRIM(nsei, LOGL_ERROR,
"Only GERAN supported, destination cell is not a GERAN cell -- rejected.\n");
return bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
}
/* Check if the RIM pdu is really addressed to this PCU. In case we
* receive a RIM PDU for a cell that is not parented by this PCU we
* are supposed to reject it with a BSSGP STATUS.
* see also: 3GPP TS 48.018, section 8c.3.1.2 */
gprs_ra_id_ci_to_cgi_ps(&dst_addr, &pdu->routing_info_dest.geran.raid,
pdu->routing_info_dest.geran.cid);
bts = gprs_pcu_get_bts_by_cgi_ps(the_pcu, &dst_addr);
if (!bts) {
LOGPRIM(nsei, LOGL_ERROR, "Destination cell %s unknown to this pcu\n",
osmo_cgi_ps_name(&dst_addr));
return bssgp_tx_status(BSSGP_CAUSE_UNKN_DST, NULL, msg);
}
/* Check if the incoming RIM PDU is parseable, if not we must report
* an error to the controlling BSS 3GPP TS 48.018, 8c.3.4 and 8c.3.4.2 */
if (!pdu->decoded_present) {
LOGPRIM(nsei, LOGL_ERROR, "Erroneous RIM PDU received for cell %s -- reject.\n",
osmo_cgi_ps_name(&dst_addr));
format_response_pdu_err(&resp_pdu, pdu);
return 0;
}
/* Handle incoming RIM container */
switch (pdu->rim_cont_iei) {
case BSSGP_IE_RI_REQ_RIM_CONTAINER:
return handle_ran_info_request(pdu, bts, nsei);
case BSSGP_IE_RI_RIM_CONTAINER:
return handle_ran_info_response(pdu, bts);
case BSSGP_IE_RI_APP_ERROR_RIM_CONT:
case BSSGP_IE_RI_ACK_RIM_CONTAINER:
case BSSGP_IE_RI_ERROR_RIM_COINTAINER:
LOGPRIM(nsei, LOGL_ERROR, "RIM PDU not handled by this application\n");
return -EINVAL;
default:
/* This should never happen. If the RIM PDU is parsed correctly, then the rim_cont_iei will
* be set to one of the cases above and if parsing fails this switch statement is guarded
* by the check on decoded_present above */
OSMO_ASSERT(false);
}
return 0;
}

24
src/gprs_bssgp_rim.h Normal file
View File

@@ -0,0 +1,24 @@
/* gprs_bssgp_rim.h
*
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
struct osmo_bssgp_prim;
int handle_rim(struct osmo_bssgp_prim *bp);

View File

@@ -22,12 +22,13 @@
#include "gprs_debug.h"
#include <osmocom/core/utils.h>
#include <osmocom/core/timer_compat.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
static void control_law(struct gprs_codel *state, struct timeval *delta)
static void control_law(struct gprs_codel *state, struct timespec *delta)
{
/* 256 / sqrt(x), limited to 255 */
static uint8_t inv_sqrt_tab[] = {255,
@@ -57,12 +58,12 @@ static void control_law(struct gprs_codel *state, struct timeval *delta)
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 = state->interval.tv_sec * 1000000 + state->interval.tv_nsec/1000;
delta_usecs = delta_usecs * inv_sqrt / 256;
q = div(delta_usecs, 1000000);
delta->tv_sec = q.quot;
delta->tv_usec = q.rem;
delta->tv_nsec = q.rem * 1000;
}
void gprs_codel_init(struct gprs_codel *state)
@@ -83,12 +84,12 @@ void gprs_codel_set_interval(struct gprs_codel *state, int interval_ms)
q = div(interval_ms, 1000);
state->interval.tv_sec = q.quot;
state->interval.tv_usec = q.rem * 1000;
state->interval.tv_nsec = q.rem * 1000000;
/* target ~ 5% of interval */
q = div(interval_ms * 13 / 256, 1000);
state->target.tv_sec = q.quot;
state->target.tv_usec = q.rem * 1000;
state->target.tv_nsec = q.rem * 1000000;
}
void gprs_codel_set_maxpacket(struct gprs_codel *state, int maxpacket)
@@ -104,29 +105,29 @@ void gprs_codel_set_maxpacket(struct gprs_codel *state, int 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)
int gprs_codel_control(struct gprs_codel *state, const struct timespec *recv,
const struct timespec *now, int bytes)
{
struct timeval sojourn_time;
struct timeval delta;
struct timespec sojourn_time;
struct timespec delta;
if (recv == NULL)
goto stop_dropping;
timersub(now, recv, &sojourn_time);
timespecsub(now, recv, &sojourn_time);
if (timercmp(&sojourn_time, &state->target, <))
if (timespeccmp(&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);
if (!timespecisset(&state->first_above_time)) {
timespecadd(now, &state->interval, &state->first_above_time);
goto not_ok_to_drop;
}
if (timercmp(now, &state->first_above_time, <))
if (timespeccmp(now, &state->first_above_time, <))
goto not_ok_to_drop;
/* Ok to drop */
@@ -134,14 +135,14 @@ int gprs_codel_control(struct gprs_codel *state, const struct timeval *recv,
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, <);
if (timespecisset(&state->drop_next)) {
timespecsub(now, &state->drop_next, &delta);
in_drop_cycle = timespeccmp(&delta, &state->interval, <);
recently = in_drop_cycle;
}
if (!recently) {
timersub(now, &state->first_above_time, &delta);
recently = !timercmp(&delta, &state->interval, <);
timespecsub(now, &state->first_above_time, &delta);
recently = !timespeccmp(&delta, &state->interval, <);
};
if (!recently)
return 0;
@@ -155,24 +156,24 @@ int gprs_codel_control(struct gprs_codel *state, const struct timeval *recv,
state->drop_next = *now;
} else {
if (timercmp(now, &state->drop_next, <))
if (timespeccmp(now, &state->drop_next, <))
return 0;
state->count += 1;
}
control_law(state, &delta);
timeradd(&state->drop_next, &delta, &state->drop_next);
timespecadd(&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);
(int)delta.tv_sec, (int)(delta.tv_nsec / 1000000), state->count);
#endif
return 1;
stop_dropping:
timerclear(&state->first_above_time);
timespecclear(&state->first_above_time);
not_ok_to_drop:
state->dropping = 0;
return 0;

View File

@@ -27,7 +27,7 @@
#pragma once
#include <sys/time.h>
#include <time.h>
/* Spec default values */
#define GPRS_CODEL_DEFAULT_INTERVAL_MS 100
@@ -40,10 +40,10 @@ extern "C" {
struct gprs_codel {
int dropping;
unsigned count;
struct timeval first_above_time;
struct timeval drop_next;
struct timeval target;
struct timeval interval;
struct timespec first_above_time;
struct timespec drop_next;
struct timespec target;
struct timespec interval;
unsigned maxpacket;
};
@@ -66,8 +66,8 @@ struct gprs_codel {
*
* \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);
int gprs_codel_control(struct gprs_codel *state, const struct timespec *recv,
const struct timespec *now, int bytes);
/*!
* \brief Initialise CoDel state

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
/* gprs_debug.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2019 Harald Welte <laforge@gnumonks.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -17,27 +18,121 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
extern "C" {
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
}
#include <gprs_debug.h>
/* default categories */
static const struct log_info_cat default_categories[] = {
{"DCSN1", "\033[1;31m", "Concrete Syntax Notation One (CSN1)", LOGL_INFO, 0},
{"DL1IF", "\033[1;32m", "GPRS PCU L1 interface (L1IF)", LOGL_INFO, 1},
{"DRLCMAC", "\033[0;33m", "GPRS RLC/MAC layer (RLCMAC)", LOGL_NOTICE, 1},
{"DRLCMACDATA", "\033[0;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_NOTICE, 1},
{"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},
{"DRLCMACMEAS", "\033[1;31m", "GPRS RLC/MAC layer Measurements (RLCMAC)", LOGL_INFO, 1},
{"DTBF","\033[1;34m", "Temporary Block Flow (TBF)", LOGL_INFO , 1},
{"DTBFDL","\033[1;34m", "Temporary Block Flow (TBF) Downlink", LOGL_INFO , 1},
{"DTBFUL","\033[1;34m", "Temporary Block Flow (TBF) Uplink", LOGL_INFO , 1},
{"DNS","\033[1;34m", "GPRS Network Service Protocol (NS)", LOGL_INFO , 1},
{"DBSSGP","\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_INFO , 1},
{"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1},
[DCSN1] = {
.name = "DCSN1",
.color = "\033[1;31m",
.description = "Concrete Syntax Notation One (CSN1)",
.loglevel = LOGL_NOTICE,
.enabled = 0,
},
[DL1IF] = {
.name = "DL1IF",
.color = "\033[1;32m",
.description = "GPRS PCU L1 interface (L1IF)",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DRLCMAC] = {
.name = "DRLCMAC",
.color = "\033[0;33m",
.description = "GPRS RLC/MAC layer (RLCMAC)",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DRLCMACDATA] = {
.name = "DRLCMACDATA",
.color = "\033[0;33m",
.description = "GPRS RLC/MAC layer Data (RLCMAC)",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DRLCMACDL] = {
.name = "DRLCMACDL",
.color = "\033[1;33m",
.description = "GPRS RLC/MAC layer Downlink (RLCMAC)",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DRLCMACUL] = {
.name = "DRLCMACUL",
.color = "\033[1;36m",
.description = "GPRS RLC/MAC layer Uplink (RLCMAC)",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DRLCMACSCHED] = {
.name = "DRLCMACSCHED",
.color = "\033[0;36m",
.description = "GPRS RLC/MAC layer Scheduling (RLCMAC)",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DRLCMACMEAS] = {
.name = "DRLCMACMEAS",
.color = "\033[1;31m",
.description = "GPRS RLC/MAC layer Measurements (RLCMAC)",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DTBF] = {
.name = "DTBF",
.color = "\033[1;34m",
.description = "Temporary Block Flow (TBF)",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DTBFDL] = {
.name = "DTBFDL",
.color = "\033[1;34m",
.description = "Temporary Block Flow (TBF) Downlink",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DTBFUL] = {
.name = "DTBFUL",
.color = "\033[1;34m",
.description = "Temporary Block Flow (TBF) Uplink",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DNS] = {
.name = "DNS",
.color = "\033[1;34m",
.description = "GPRS Network Service Protocol (NS)",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DPCU] = {
.name = "DPCU",
.color = "\033[1;35m",
.description = "GPRS Packet Control Unit (PCU)",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DNACC] = {
.name = "DNACC",
.color = "\033[1;37m",
.description = "Network Assisted Cell Change (NACC)",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DRIM] = {
.name = "DRIM",
.color = "\033[1;38m",
.description = "RAN Information Management (RIM)",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
};
static int filter_fn(const struct log_context *ctx,

View File

@@ -27,6 +27,10 @@ extern "C" {
};
#endif
/* we used to have DBSSGP definded in each application, and applications telling
* libosmogb which sub-system to use. That creates problems and has been deprecated */
#define DBSSGP DLBSSGP
/* Debug Areas of the code */
enum {
DCSN1,
@@ -41,8 +45,9 @@ enum {
DTBFDL,
DTBFUL,
DNS,
DBSSGP,
DPCU,
DNACC,
DRIM,
aDebug_LastEntry
};

988
src/gprs_ms.c Normal file
View File

@@ -0,0 +1,988 @@
/* gprs_ms.c
*
* Copyright (C) 2015-2020 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 "tbf_ul.h"
#include "gprs_debug.h"
#include "gprs_codel.h"
#include "pcu_utils.h"
#include "nacc_fsm.h"
#include <time.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/stats.h>
#include "coding_scheme.h"
#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
extern void *tall_pcu_ctx;
static unsigned int next_ms_ctr_group_id;
static const struct rate_ctr_desc ms_ctr_description[] = {
[MS_CTR_DL_CTRL_MSG_SCHED] = { "ms:dl_ctrl_msg_sched", "Amount of DL CTRL messages scheduled" },
};
static const struct rate_ctr_group_desc ms_ctrg_desc = {
.group_name_prefix = "pcu:ms",
.group_description = "MS Statistics",
.class_id = OSMO_STATS_CLASS_SUBSCRIBER,
.num_ctr = ARRAY_SIZE(ms_ctr_description),
.ctr_desc = ms_ctr_description,
};
static int64_t now_msec()
{
struct timespec ts;
osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
return (int64_t)(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
}
void gprs_default_cb_ms_idle(struct GprsMs *ms)
{
talloc_free(ms);
}
void gprs_default_cb_ms_active(struct GprsMs *ms)
{
/* do nothing */
}
static struct gpr_ms_callback gprs_default_cb = {
.ms_idle = gprs_default_cb_ms_idle,
.ms_active = gprs_default_cb_ms_active,
};
static void ms_release_timer_cb(void *data)
{
struct GprsMs *ms = (struct GprsMs *) data;
LOGPMS(ms, DRLCMAC, LOGL_INFO, "Release timer expired\n");
if (ms->timer.data) {
ms->timer.data = NULL;
ms_unref(ms);
}
}
static int ms_talloc_destructor(struct GprsMs *ms);
struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli)
{
struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
talloc_set_destructor(ms, ms_talloc_destructor);
ms->bts = bts;
ms->cb = gprs_default_cb;
ms->tlli = tlli;
ms->new_ul_tlli = GSM_RESERVED_TMSI;
ms->new_dl_tlli = GSM_RESERVED_TMSI;
ms->ta = GSM48_TA_INVALID;
ms->current_cs_ul = UNKNOWN;
ms->current_cs_dl = UNKNOWN;
ms->is_idle = true;
INIT_LLIST_HEAD(&ms->list);
INIT_LLIST_HEAD(&ms->old_tbfs);
int codel_interval = LLC_CODEL_USE_DEFAULT;
LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
ms->imsi[0] = '\0';
memset(&ms->timer, 0, sizeof(ms->timer));
ms->timer.cb = ms_release_timer_cb;
llc_queue_init(&ms->llc_queue);
ms_set_mode(ms, GPRS);
if (ms->bts)
codel_interval = the_pcu->vty.llc_codel_interval_msec;
if (codel_interval) {
if (codel_interval == LLC_CODEL_USE_DEFAULT)
codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
ms->codel_state = talloc(ms, struct gprs_codel);
gprs_codel_init(ms->codel_state);
gprs_codel_set_interval(ms->codel_state, codel_interval);
}
ms->last_cs_not_low = now_msec();
ms->app_info_pending = false;
ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
if (!ms->ctrs)
goto free_ret;
return ms;
free_ret:
talloc_free(ms);
return NULL;
}
static int ms_talloc_destructor(struct GprsMs *ms)
{
struct llist_item *pos, *tmp;
LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
ms_set_reserved_slots(ms, NULL, 0, 0);
if (osmo_timer_pending(&ms->timer))
osmo_timer_del(&ms->timer);
if (ms->ul_tbf) {
tbf_set_ms((struct gprs_rlcmac_tbf *)ms->ul_tbf, NULL);
ms->ul_tbf = NULL;
}
if (ms->dl_tbf) {
tbf_set_ms((struct gprs_rlcmac_tbf *)ms->dl_tbf, NULL);
ms->dl_tbf = NULL;
}
llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
tbf_set_ms(tbf, NULL);
}
llc_queue_clear(&ms->llc_queue, ms->bts);
if (ms->ctrs)
rate_ctr_group_free(ms->ctrs);
return 0;
}
void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
{
if (cb)
ms->cb = *cb;
else
ms->cb = gprs_default_cb;
}
static void ms_update_status(struct GprsMs *ms)
{
if (ms->ref > 0)
return;
if (ms_is_idle(ms) && !ms->is_idle) {
ms->is_idle = true;
ms->cb.ms_idle(ms);
/* this can be deleted by now, do not access it */
return;
}
if (!ms_is_idle(ms) && ms->is_idle) {
ms->is_idle = false;
ms->cb.ms_active(ms);
}
}
struct GprsMs *ms_ref(struct GprsMs *ms)
{
ms->ref += 1;
return ms;
}
void ms_unref(struct GprsMs *ms)
{
OSMO_ASSERT(ms->ref >= 0);
ms->ref -= 1;
if (ms->ref == 0)
ms_update_status(ms);
}
static void ms_release_timer_start(struct GprsMs *ms)
{
if (ms->delay == 0)
return;
LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
if (!ms->timer.data)
ms->timer.data = ms_ref(ms);
osmo_timer_schedule(&ms->timer, ms->delay, 0);
}
static void ms_release_timer_stop(struct GprsMs *ms)
{
if (!ms->timer.data)
return;
LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
osmo_timer_del(&ms->timer);
ms->timer.data = NULL;
ms_unref(ms);
}
void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
{
ms->mode = mode;
switch (ms->mode) {
case GPRS:
if (!mcs_is_gprs(ms->current_cs_ul)) {
ms->current_cs_ul = mcs_get_gprs_by_num(
ms->bts->initial_cs_ul);
if (!mcs_is_valid(ms->current_cs_ul))
ms->current_cs_ul = CS1;
}
if (!mcs_is_gprs(ms->current_cs_dl)) {
ms->current_cs_dl = mcs_get_gprs_by_num(
ms->bts->initial_cs_dl);
if (!mcs_is_valid(ms->current_cs_dl))
ms->current_cs_dl = CS1;
}
break;
case EGPRS_GMSK:
if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
ms->current_cs_ul = mcs_get_egprs_by_num(
ms->bts->initial_mcs_ul);
if (!mcs_is_valid(ms->current_cs_ul))
ms->current_cs_ul = MCS1;
}
if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
ms->current_cs_dl = mcs_get_egprs_by_num(
ms->bts->initial_mcs_dl);
if (!mcs_is_valid(ms->current_cs_dl))
ms->current_cs_dl = MCS1;
}
break;
case EGPRS:
if (!mcs_is_edge(ms->current_cs_ul)) {
ms->current_cs_ul = mcs_get_egprs_by_num(
ms->bts->initial_mcs_ul);
if (!mcs_is_valid(ms->current_cs_ul))
ms->current_cs_ul = MCS1;
}
if (!mcs_is_edge(ms->current_cs_dl)) {
ms->current_cs_dl = mcs_get_egprs_by_num(
ms->bts->initial_mcs_dl);
if (!mcs_is_valid(ms->current_cs_dl))
ms->current_cs_dl = MCS1;
}
break;
}
}
static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
{
if (ms->ul_tbf == tbf)
return;
LOGPMS(ms, DRLCMAC, LOGL_INFO, "Attaching UL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
ms_ref(ms);
if (ms->ul_tbf)
llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->ul_tbf), &ms->old_tbfs);
ms->ul_tbf = tbf;
if (tbf)
ms_release_timer_stop(ms);
ms_unref(ms);
}
static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
{
if (ms->dl_tbf == tbf)
return;
LOGPMS(ms, DRLCMAC, LOGL_INFO, "Attaching DL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
ms_ref(ms);
if (ms->dl_tbf)
llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->dl_tbf), &ms->old_tbfs);
ms->dl_tbf = tbf;
if (tbf)
ms_release_timer_stop(ms);
ms_unref(ms);
}
void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
{
if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
ms_attach_dl_tbf(ms, as_dl_tbf(tbf));
else
ms_attach_ul_tbf(ms, as_ul_tbf(tbf));
}
void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
{
if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
ms->ul_tbf = NULL;
} else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
ms->dl_tbf = NULL;
} else {
bool found = false;
struct llist_item *pos, *tmp;
llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
if (tmp_tbf == tbf) {
llist_del(&pos->list);
found = true;
break;
}
}
/* Protect against recursive calls via set_ms() */
if (!found)
return;
}
LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
tbf_name(tbf));
if (tbf_ms(tbf) == ms)
tbf_set_ms(tbf, NULL);
if (!ms->dl_tbf && !ms->ul_tbf) {
ms_set_reserved_slots(ms, NULL, 0, 0);
if (ms_tlli(ms) != 0)
ms_release_timer_start(ms);
}
ms_update_status(ms);
}
void ms_reset(struct GprsMs *ms)
{
LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
ms_release_timer_stop(ms);
ms->tlli = GSM_RESERVED_TMSI;
ms->new_dl_tlli = ms->tlli;
ms->new_ul_tlli = ms->tlli;
ms->imsi[0] = '\0';
}
static void ms_merge_old_ms(struct GprsMs *ms, struct GprsMs *old_ms)
{
OSMO_ASSERT(old_ms != ms);
if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
if (!ms_ms_class(ms) && ms_ms_class(old_ms))
ms_set_ms_class(ms, ms_ms_class(old_ms));
if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
ms_reset(old_ms);
}
void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
{
OSMO_ASSERT(old_ms != ms);
ms_ref(old_ms);
/* Clean up the old MS object */
/* TODO: Use timer? */
if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
ms_merge_old_ms(ms, old_ms);
ms_unref(old_ms);
}
void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
{
if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
return;
if (tlli != ms->new_dl_tlli) {
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
"not yet confirmed\n",
ms_tlli(ms), tlli);
ms->new_ul_tlli = tlli;
return;
}
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
"already confirmed partly\n",
ms->tlli, tlli);
ms->tlli = tlli;
ms->new_dl_tlli = GSM_RESERVED_TMSI;
ms->new_ul_tlli = GSM_RESERVED_TMSI;
}
bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
{
if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
return false;
if (tlli != ms->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() */
ms->new_dl_tlli = tlli;
return false;
}
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
ms->tlli = tlli;
ms->new_dl_tlli = GSM_RESERVED_TMSI;
ms->new_ul_tlli = GSM_RESERVED_TMSI;
return true;
}
void ms_set_imsi(struct GprsMs *ms, 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, ms->imsi) == 0)
return;
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
ms_tlli(ms), ms->imsi, imsi);
struct GprsMs *old_ms = bts_ms_by_imsi(ms->bts, imsi);
/* Check if we are going to store a different MS object with already
existing IMSI. This is probably a bug in code calling this function,
since it should take care of this explicitly */
if (old_ms) {
/* We cannot find ms->ms by IMSI since we know that it has a
* different IMSI */
OSMO_ASSERT(old_ms != ms);
LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
"IMSI '%s' was already assigned to another "
"MS object: TLLI = 0x%08x, that IMSI will be removed\n",
imsi, ms_tlli(old_ms));
ms_merge_and_clear_ms(ms, old_ms);
}
osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
}
uint16_t ms_paging_group(struct GprsMs *ms)
{
uint16_t pgroup;
if (!ms_imsi_is_valid(ms))
return 0; /* 000 is the special "all paging" group */
if ((pgroup = imsi2paging_group(ms_imsi(ms))) > 999) {
LOGPMS(ms, DRLCMAC, LOGL_ERROR, "IMSI to paging group failed!\n");
return 0;
}
return pgroup;
}
void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
{
if (ta_ == ms->ta)
return;
if (gsm48_ta_is_valid(ta_)) {
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
ms_tlli(ms), ms->ta, ta_);
ms->ta = ta_;
} else
LOGP(DRLCMAC, LOGL_NOTICE,
"MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
"value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
}
void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
{
if (ms_class_ == ms->ms_class)
return;
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
ms_tlli(ms), ms->ms_class, ms_class_);
ms->ms_class = ms_class_;
}
void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
{
if (ms_class_ == ms->egprs_ms_class)
return;
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
ms_tlli(ms), ms->egprs_ms_class, ms_class_);
ms->egprs_ms_class = ms_class_;
if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
"Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
return;
}
if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
ms_mode(ms) != EGPRS)
{
ms_set_mode(ms, EGPRS_GMSK);
} else {
ms_set_mode(ms, EGPRS);
}
LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
}
void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
{
int64_t now;
enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
OSMO_ASSERT(max_cs_dl);
if (error_rate < 0)
return;
now = now_msec();
/* TODO: Check for TBF direction */
/* TODO: Support different CS values for UL and DL */
ms->nack_rate_dl = error_rate;
if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
if (mcs_chan_code(ms->current_cs_dl) > 0) {
mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
LOGP(DRLCMACDL, LOGL_INFO,
"MS (IMSI %s): High error rate %d%%, "
"reducing CS level to %s\n",
ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
ms->last_cs_not_low = now;
}
} else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
if (ms->current_cs_dl < max_cs_dl) {
if (now - ms->last_cs_not_low > 1000) {
mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
LOGP(DRLCMACDL, LOGL_INFO,
"MS (IMSI %s): Low error rate %d%%, "
"increasing DL CS level to %s\n",
ms_imsi(ms), error_rate,
mcs_name(ms->current_cs_dl));
ms->last_cs_not_low = now;
} else {
LOGP(DRLCMACDL, LOGL_DEBUG,
"MS (IMSI %s): Low error rate %d%%, "
"ignored (within blocking period)\n",
ms_imsi(ms), error_rate);
}
}
} else {
LOGP(DRLCMACDL, LOGL_DEBUG,
"MS (IMSI %s): Medium error rate %d%%, ignored\n",
ms_imsi(ms), error_rate);
ms->last_cs_not_low = now;
}
}
enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
{
enum CodingScheme cs;
OSMO_ASSERT(ms->bts != NULL);
if (mcs_is_gprs(ms->current_cs_ul)) {
if (!bts_max_cs_ul(ms->bts)) {
return CS4;
}
return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
}
cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
cs = MCS4;
return cs;
}
void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
{
ms->current_cs_dl = scheme;
}
enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
{
enum CodingScheme cs;
OSMO_ASSERT(ms->bts != NULL);
if (mcs_is_gprs(ms->current_cs_dl)) {
if (!bts_max_cs_dl(ms->bts)) {
return CS4;
}
return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
}
cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
cs = MCS4;
return cs;
}
void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
{
enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
int old_link_qual;
int low;
int high;
enum CodingScheme new_cs_ul = ms->current_cs_ul;
uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
if (!max_cs_ul) {
LOGP(DRLCMACMEAS, LOGL_ERROR,
"max_cs_ul cannot be derived (current UL CS: %s)\n",
mcs_name(ms->current_cs_ul));
return;
}
if (!ms->current_cs_ul) {
LOGP(DRLCMACMEAS, LOGL_ERROR,
"Unable to update UL (M)CS because it's not set: %s\n",
mcs_name(ms->current_cs_ul));
return;
}
if (!meas->have_link_qual) {
LOGP(DRLCMACMEAS, LOGL_ERROR,
"Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
mcs_name(ms->current_cs_ul));
return;
}
if (mcs_is_gprs(ms->current_cs_ul)) {
if (current_cs >= MAX_GPRS_CS)
current_cs = MAX_GPRS_CS - 1;
low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
} else if (mcs_is_edge(ms->current_cs_ul)) {
if (current_cs >= MAX_EDGE_MCS)
current_cs = MAX_EDGE_MCS - 1;
low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
} else {
LOGP(DRLCMACMEAS, LOGL_ERROR,
"Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
mcs_name(ms->current_cs_ul));
return;
}
/* To avoid rapid changes of the coding scheme, we also take
* the old link quality value into account (if present). */
if (ms->l1_meas.have_link_qual)
old_link_qual = ms->l1_meas.link_qual;
else
old_link_qual = meas->link_qual;
if (meas->link_qual < low && old_link_qual < low)
mcs_dec_kind(&new_cs_ul, ms_mode(ms));
else if (meas->link_qual > high && old_link_qual > high &&
ms->current_cs_ul < max_cs_ul)
mcs_inc_kind(&new_cs_ul, ms_mode(ms));
if (ms->current_cs_ul != new_cs_ul) {
LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
"Link quality %ddB (old %ddB) left window [%d, %d], "
"modifying uplink CS level: %s -> %s\n",
meas->link_qual, old_link_qual,
low, high,
mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
ms->current_cs_ul = new_cs_ul;
}
}
void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
{
unsigned i;
ms_update_cs_ul(ms, meas);
if (meas->have_rssi)
pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
if (meas->have_bto)
pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
if (meas->have_ber)
pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
if (meas->have_link_qual)
pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
if (meas->have_ms_rx_qual)
pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
if (meas->have_ms_c_value)
pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
if (meas->have_ms_sign_var)
pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, 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)
pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
else
ms->l1_meas.ts[i].have_ms_i_level = 0;
}
}
}
/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
{
enum CodingScheme orig_cs = ms->current_cs_dl;
struct gprs_rlcmac_bts *bts = ms->bts;
size_t unencoded_octets;
enum CodingScheme cs;
/* It could be that a TBF requests a GPRS CS despite the MS currently
being upgraded to EGPRS (hence reporting MCS). That could happen
because the TBF was created early in the process where we didn't have
yet enough information about the MS, and only AFTER it was created we
upgraded the MS to be EGPRS capable.
As a result, when the MS is queried for the target CS here, we could be
returning an MCS despite the current TBF being established as GPRS,
but we rather stick to the TBF type we assigned to the MS rather than
magically sending EGPRS data blocks to a GPRS TBF.
It could also be that the caller requests specific MCS kind
explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
MCS1;
} else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
int i;
cs = orig_cs > MCS4 ? MCS4 : orig_cs;
cs -= (MCS1 - CS1); /* MCSx -> CSx */
/* Find suitable CS starting from equivalent MCS which is supported by BTS: */
for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
cs = CS1 + i;
} else {
cs = orig_cs;
}
if (orig_cs != cs)
LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
"DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
unencoded_octets = llc_queue_octets(&ms->llc_queue);
/* If the DL TBF is active, add number of unencoded chunk octets */
if (ms->dl_tbf)
unencoded_octets += llc_chunk_size(tbf_llc((struct gprs_rlcmac_tbf *)ms->dl_tbf));
/* There are many unencoded octets, don't reduce */
if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
return cs;
/* RF conditions are good, don't reduce */
if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
return cs;
/* The throughput would probably be better if the CS level was reduced */
mcs_dec_kind(&cs, ms_mode(ms));
/* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
if (cs == CS2)
mcs_dec_kind(&cs, ms_mode(ms));
return cs;
}
int ms_first_common_ts(const struct GprsMs *ms)
{
if (ms->dl_tbf)
return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->dl_tbf);
if (ms->ul_tbf)
return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->ul_tbf);
return -1;
}
uint8_t ms_dl_slots(const struct GprsMs *ms)
{
uint8_t slots = 0;
if (ms->dl_tbf)
slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
if (ms->ul_tbf)
slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
return slots;
}
uint8_t ms_ul_slots(const struct GprsMs *ms)
{
uint8_t slots = 0;
if (ms->dl_tbf)
slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
if (ms->ul_tbf)
slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
return slots;
}
uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
{
uint8_t slots = 0;
bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->dl_tbf);
bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->ul_tbf);
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 = tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
else if (!is_dl_active && is_ul_active)
slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
else
slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf) &
tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
/* Assume a multislot class 1 device */
/* TODO: For class 2 devices, this could be removed */
slots = pcu_lsb(slots);
return slots;
}
void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
uint8_t ul_slots, uint8_t dl_slots)
{
if (ms->current_trx) {
bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
ms->reserved_dl_slots);
bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
ms->reserved_ul_slots);
ms->reserved_dl_slots = 0;
ms->reserved_ul_slots = 0;
}
ms->current_trx = trx;
if (trx) {
ms->reserved_dl_slots = dl_slots;
ms->reserved_ul_slots = ul_slots;
bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
ms->reserved_dl_slots);
bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
ms->reserved_ul_slots);
}
}
struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
{
switch (dir) {
case GPRS_RLCMAC_DL_TBF: return (struct gprs_rlcmac_tbf *)ms->dl_tbf;
case GPRS_RLCMAC_UL_TBF: return (struct gprs_rlcmac_tbf *)ms->ul_tbf;
}
return NULL;
}
int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
{
if (!ms->nacc)
ms->nacc = nacc_fsm_alloc(ms);
if (!ms->nacc)
return -EINVAL;
return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
}
bool ms_nacc_rts(const struct GprsMs *ms)
{
if (!ms->nacc)
return false;
if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
return true;
return false;
}
struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts)
{
int rc;
struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
.tbf = tbf,
.fn = fn,
.ts = ts,
.msg = NULL,
};
rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
if (rc != 0 || !data_ctx.msg)
return NULL;
return data_ctx.msg;
}

View File

@@ -1,834 +0,0 @@
/* 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 <gprs_coding_scheme.h>
#include "bts.h"
#include "tbf.h"
#include "gprs_debug.h"
#include "gprs_codel.h"
#include "pcu_utils.h"
#include <time.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/core/logging.h>
}
#define GPRS_CODEL_SLOW_INTERVAL_MS 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(GSM48_TA_INVALID),
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),
m_dl_ctrl_msg(0)
{
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_ul.isValid())
m_current_cs_ul = GprsCodingScheme::MCS1;
}
if (!m_current_cs_dl.isEgprs()) {
m_current_cs_dl = GprsCodingScheme::getEgprsByNum(
m_bts->bts_data()->initial_mcs_dl);
if (!m_current_cs_dl.isValid())
m_current_cs_dl = GprsCodingScheme::MCS1;
}
break;
}
}
void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
{
if (tbf->direction == GPRS_RLCMAC_DL_TBF)
attach_dl_tbf(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;
if (gsm48_ta_is_valid(ta_)) {
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
tlli(), m_ta, ta_);
m_ta = ta_;
} else
LOGP(DRLCMAC, LOGL_NOTICE,
"MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
"value %d kept)\n", tlli(), ta_, m_ta);
}
void GprsMs::set_ms_class(uint8_t ms_class_)
{
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);
}
void GprsMs::set_current_cs_dl(GprsCodingScheme::Scheme scheme)
{
m_current_cs_dl = scheme;
}
GprsCodingScheme GprsMs::max_cs_dl() const
{
struct gprs_rlcmac_bts *bts_data;
OSMO_ASSERT(m_bts != NULL);
bts_data = m_bts->bts_data();
if (m_current_cs_dl.isGprs()) {
if (!bts_data->max_cs_dl)
return GprsCodingScheme(GprsCodingScheme::CS4);
return GprsCodingScheme::getGprsByNum(bts_data->max_cs_dl);
}
if (!m_current_cs_dl.isEgprs())
return GprsCodingScheme(); /* UNKNOWN */
if (bts_data->max_mcs_dl)
return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_dl);
else if (bts_data->max_cs_dl)
return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_dl);
return GprsCodingScheme(GprsCodingScheme::MCS4);
}
void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
{
struct gprs_rlcmac_bts *bts_data;
GprsCodingScheme max_cs_ul = this->max_cs_ul();
int old_link_qual;
int low;
int high;
GprsCodingScheme new_cs_ul = m_current_cs_ul;
unsigned current_cs_num = m_current_cs_ul.to_num();
bts_data = m_bts->bts_data();
if (!max_cs_ul) {
LOGP(DRLCMACMEAS, LOGL_ERROR,
"max_cs_ul cannot be derived (current UL CS: %s)\n",
m_current_cs_ul.name());
return;
}
OSMO_ASSERT(current_cs_num > 0);
if (!m_current_cs_ul)
return;
if (!meas->have_link_qual)
return;
old_link_qual = meas->link_qual;
if (m_current_cs_ul.isGprs()) {
low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
} else if (m_current_cs_ul.isEgprs()) {
if (current_cs_num > MAX_GPRS_CS)
current_cs_num = MAX_GPRS_CS;
low = bts_data->mcs_lqual_ranges[current_cs_num-1].low;
high = bts_data->mcs_lqual_ranges[current_cs_num-1].high;
} else {
return;
}
if (m_l1_meas.have_link_qual)
old_link_qual = m_l1_meas.link_qual;
if (meas->link_qual < low && old_link_qual < low)
new_cs_ul.dec(mode());
else if (meas->link_qual > high && old_link_qual > high &&
m_current_cs_ul < max_cs_ul)
new_cs_ul.inc(mode());
if (m_current_cs_ul != new_cs_ul) {
LOGP(DRLCMACMEAS, LOGL_INFO,
"MS (IMSI %s): "
"Link quality %ddB (%ddB) left window [%d, %d], "
"modifying uplink CS level: %s -> %s\n",
imsi(), meas->link_qual, old_link_qual,
low, high,
m_current_cs_ul.name(), new_cs_ul.name());
m_current_cs_ul = new_cs_ul;
}
}
void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
{
unsigned i;
update_cs_ul(meas);
if (meas->have_rssi)
m_l1_meas.set_rssi(meas->rssi);
if (meas->have_bto)
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;
}

View File

@@ -1,6 +1,6 @@
/* gprs_ms.h
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
* Copyright (C) 2015-2020 by Sysmocom s.f.m.c. GmbH
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
@@ -22,272 +22,238 @@
struct gprs_codel;
#include <gprs_coding_scheme.h>
#include "cxx_linuxlist.h"
#include "llc.h"
#include "tbf.h"
#include "tbf_ul.h"
#include "tbf_dl.h"
#include "pcu_l1_if.h"
#include <gprs_coding_scheme.h>
#ifdef __cplusplus
extern "C" {
#include <osmocom/core/timer.h>
#include <osmocom/core/linuxlist.h>
}
#endif
#include <osmocom/core/timer.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gsm/gsm48.h>
#include "coding_scheme.h"
#include <gsm_rlcmac.h>
#include <stdint.h>
#include <stddef.h>
#include <inttypes.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);
void set_current_cs_dl(GprsCodingScheme::Scheme scheme);
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;
unsigned dl_ctrl_msg() const;
void update_dl_ctrl_msg();
/* 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;
unsigned m_dl_ctrl_msg;
enum ms_counter_id {
MS_CTR_DL_CTRL_MSG_SCHED,
};
inline bool GprsMs::is_idle() const
struct gprs_rlcmac_bts;
struct gprs_rlcmac_trx;
struct GprsMs;
struct gpr_ms_callback {
void (*ms_idle)(struct GprsMs *);
void (*ms_active)(struct GprsMs *);
};
struct GprsMs {
struct llist_head list; /* list of all GprsMs */
struct gpr_ms_callback cb;
bool app_info_pending;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_ul_tbf *ul_tbf;
struct gprs_rlcmac_dl_tbf *dl_tbf;
struct llist_head old_tbfs; /* list of gprs_rlcmac_tbf */
uint32_t tlli;
uint32_t new_ul_tlli;
uint32_t new_dl_tlli;
/* store IMSI for look-up and PCH retransmission */
char imsi[OSMO_IMSI_BUF_SIZE];
uint8_t ta;
uint8_t ms_class;
uint8_t egprs_ms_class;
/* current coding scheme */
enum CodingScheme current_cs_ul;
enum CodingScheme current_cs_dl;
struct gprs_llc_queue llc_queue;
bool is_idle;
int ref;
struct osmo_timer_list timer;
unsigned delay;
int64_t last_cs_not_low;
struct pcu_l1_meas l1_meas;
unsigned nack_rate_dl;
uint8_t reserved_dl_slots;
uint8_t reserved_ul_slots;
struct gprs_rlcmac_trx *current_trx;
struct gprs_codel *codel_state;
enum mcs_kind mode;
struct rate_ctr_group *ctrs;
struct nacc_fsm_ctx *nacc;
};
struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli);
int ms_first_common_ts(const struct GprsMs *ms);
void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
uint8_t ul_slots, uint8_t dl_slots);
struct GprsMs *ms_ref(struct GprsMs *ms);
void ms_unref(struct GprsMs *ms);
void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode);
void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_);
void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_);
void ms_set_ta(struct GprsMs *ms, uint8_t ta_);
enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind);
enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms);
enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms);
void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme);
void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate);
uint8_t ms_current_pacch_slots(const struct GprsMs *ms);
void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms);
void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf);
void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf);
void ms_set_tlli(struct GprsMs *ms, uint32_t tlli);
bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli);
void ms_set_imsi(struct GprsMs *ms, const char *imsi);
uint16_t ms_paging_group(struct GprsMs *ms);
void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas);
struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir);
static inline struct gprs_rlcmac_ul_tbf *ms_ul_tbf(const struct GprsMs *ms) {return ms->ul_tbf;}
static inline struct gprs_rlcmac_dl_tbf *ms_dl_tbf(const struct GprsMs *ms) {return ms->dl_tbf;}
void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb);
int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif);
bool ms_nacc_rts(const struct GprsMs *ms);
struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
static inline bool ms_is_idle(const struct GprsMs *ms)
{
return !m_ul_tbf && !m_dl_tbf && !m_ref && llist_empty(&m_old_tbfs);
return !ms->ul_tbf && !ms->dl_tbf && !ms->ref && llist_empty(&ms->old_tbfs);
}
inline bool GprsMs::need_dl_tbf() const
static inline struct gprs_llc_queue *ms_llc_queue(struct GprsMs *ms)
{
if (dl_tbf() != NULL && dl_tbf()->state_is_not(GPRS_RLCMAC_WAIT_RELEASE))
return &ms->llc_queue;
}
static inline bool ms_need_dl_tbf(struct GprsMs *ms)
{
if (ms_dl_tbf(ms) != NULL &&
tbf_state((const struct gprs_rlcmac_tbf *)ms_dl_tbf(ms)) != TBF_ST_WAIT_RELEASE)
return false;
return llc_queue()->size() > 0;
return llc_queue_size(ms_llc_queue(ms)) > 0;
}
inline uint32_t GprsMs::tlli() const
static inline uint32_t ms_tlli(const struct GprsMs *ms)
{
return m_new_ul_tlli ? m_new_ul_tlli :
m_tlli ? m_tlli :
m_new_dl_tlli;
if (ms->new_ul_tlli != GSM_RESERVED_TMSI)
return ms->new_ul_tlli;
if (ms->tlli != GSM_RESERVED_TMSI)
return ms->tlli;
return ms->new_dl_tlli;
}
inline bool GprsMs::check_tlli(uint32_t tlli)
static inline bool ms_check_tlli(struct GprsMs *ms, uint32_t tlli)
{
return tlli != 0 &&
(tlli == m_tlli || tlli == m_new_ul_tlli || tlli == m_new_dl_tlli);
return tlli != GSM_RESERVED_TMSI &&
(tlli == ms->tlli || tlli == ms->new_ul_tlli || tlli == ms->new_dl_tlli);
}
inline const char *GprsMs::imsi() const
static inline const char *ms_imsi(const struct GprsMs *ms)
{
return m_imsi;
return ms->imsi;
}
inline uint8_t GprsMs::ta() const
static inline bool ms_imsi_is_valid(const struct GprsMs *ms)
{
return m_ta;
return ms->imsi[0] != '\0';
}
inline uint8_t GprsMs::ms_class() const
static inline uint8_t ms_ta(const struct GprsMs *ms)
{
return m_ms_class;
return ms->ta;
}
inline uint8_t GprsMs::egprs_ms_class() const
static inline uint8_t ms_ms_class(const struct GprsMs *ms)
{
return m_egprs_ms_class;
return ms->ms_class;
}
inline GprsCodingScheme GprsMs::current_cs_ul() const
static inline uint8_t ms_egprs_ms_class(const struct GprsMs *ms)
{
return m_current_cs_ul;
return ms->egprs_ms_class;
}
inline GprsCodingScheme::Mode GprsMs::mode() const
static inline enum CodingScheme ms_current_cs_ul(const struct GprsMs *ms)
{
return m_mode;
return ms->current_cs_ul;
}
inline void GprsMs::set_timeout(unsigned secs)
static inline enum mcs_kind ms_mode(const struct GprsMs *ms)
{
m_delay = secs;
return ms->mode;
}
inline gprs_llc_queue *GprsMs::llc_queue()
static inline void ms_set_timeout(struct GprsMs *ms, unsigned secs)
{
return &m_llc_queue;
ms->delay = secs;
}
inline const gprs_llc_queue *GprsMs::llc_queue() const
static inline struct gprs_codel *ms_codel_state(const struct GprsMs *ms)
{
return &m_llc_queue;
return ms->codel_state;
}
inline gprs_codel *GprsMs::codel_state() const
static inline unsigned ms_nack_rate_dl(const struct GprsMs *ms)
{
return m_codel_state;
return ms->nack_rate_dl;
}
inline unsigned GprsMs::nack_rate_dl() const
static inline uint8_t ms_reserved_dl_slots(const struct GprsMs *ms)
{
return m_nack_rate_dl;
return ms->reserved_dl_slots;
}
inline unsigned GprsMs::dl_ctrl_msg() const
static inline uint8_t ms_reserved_ul_slots(const struct GprsMs *ms)
{
return m_dl_ctrl_msg;
return ms->reserved_ul_slots;
}
inline void GprsMs::update_dl_ctrl_msg()
static inline struct gprs_rlcmac_trx *ms_current_trx(const struct GprsMs *ms)
{
m_dl_ctrl_msg++;
return ms->current_trx;
}
inline uint8_t GprsMs::reserved_dl_slots() const
{
return m_reserved_dl_slots;
}
#define LOGPMS(ms, category, level, fmt, args...) \
LOGP(category, level, "MS(TLLI=0x%08x, IMSI=%s, TA=%" PRIu8 ", %" PRIu8 "/%" PRIu8 ",%s%s) " fmt, \
ms_tlli(ms), ms_imsi(ms), ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms), \
ms_ul_tbf(ms) ? " UL": "", \
ms_dl_tbf(ms) ? " DL": "", \
## args)
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;
#ifdef __cplusplus
}
#endif

View File

@@ -26,13 +26,32 @@
extern "C" {
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/gsm48.h>
}
#define GPRS_UNDEFINED_IMSI "000"
static void ms_storage_ms_idle_cb(struct GprsMs *ms)
{
llist_del(&ms->list);
if (ms->bts)
bts_stat_item_add(ms->bts, STAT_MS_PRESENT, -1);
if (ms_is_idle(ms))
talloc_free(ms);
}
GprsMsStorage::GprsMsStorage(BTS *bts) :
static void ms_storage_ms_active_cb(struct GprsMs *ms)
{
/* Nothing to do */
}
static struct gpr_ms_callback ms_storage_ms_cb = {
.ms_idle = ms_storage_ms_idle_cb,
.ms_active = ms_storage_ms_active_cb,
};
GprsMsStorage::GprsMsStorage(struct gprs_rlcmac_bts *bts) :
m_bts(bts)
{
INIT_LLIST_HEAD(&m_list);
}
GprsMsStorage::~GprsMsStorage()
@@ -42,50 +61,36 @@ GprsMsStorage::~GprsMsStorage()
void GprsMsStorage::cleanup()
{
LListHead<GprsMs> *pos, *tmp;
struct llist_head *pos, *tmp;
llist_for_each_safe(pos, tmp, &m_list) {
GprsMs *ms = pos->entry();
ms->set_callback(NULL);
ms_idle(ms);
struct GprsMs *ms = llist_entry(pos, typeof(*ms), list);
ms_set_callback(ms, NULL);
ms_storage_ms_idle_cb(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
{
struct llist_head *tmp;
GprsMs *ms;
LListHead<GprsMs> *pos;
if (tlli || old_tlli) {
llist_for_each(pos, &m_list) {
ms = pos->entry();
if (ms->check_tlli(tlli))
if (tlli != GSM_RESERVED_TMSI || old_tlli != GSM_RESERVED_TMSI) {
llist_for_each(tmp, &m_list) {
ms = llist_entry(tmp, typeof(*ms), list);
if (ms_check_tlli(ms, tlli))
return ms;
if (ms->check_tlli(old_tlli))
if (ms_check_tlli(ms, 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)
if (imsi && imsi[0] != '\0') {
llist_for_each(tmp, &m_list) {
ms = llist_entry(tmp, typeof(*ms), list);
if (ms_imsi_is_valid(ms) && strcmp(imsi, ms_imsi(ms)) == 0)
return ms;
}
}
@@ -97,29 +102,12 @@ GprsMs *GprsMsStorage::create_ms()
{
GprsMs *ms;
ms = new GprsMs(m_bts, 0);
ms = ms_alloc(m_bts, GSM_RESERVED_TMSI);
ms->set_callback(this);
llist_add(&ms->list(), &m_list);
ms_set_callback(ms, &ms_storage_ms_cb);
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);
bts_stat_item_add(m_bts, STAT_MS_PRESENT, 1);
return ms;
}

View File

@@ -21,29 +21,24 @@
#pragma once
#include "gprs_ms.h"
#include "cxx_linuxlist.h"
#include "tbf.h"
#include <stdint.h>
#include <stddef.h>
struct BTS;
struct gprs_rlcmac_bts;
class GprsMsStorage : public GprsMs::Callback {
struct GprsMsStorage {
public:
GprsMsStorage(BTS *bts);
GprsMsStorage(struct gprs_rlcmac_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 *get_ms(uint32_t tlli, uint32_t old_tlli = GSM_RESERVED_TMSI, const char *imsi = NULL) const;
GprsMs *create_ms();
const LListHead<GprsMs>& ms_list() const {return m_list;}
const struct llist_head* ms_list() const {return &m_list;}
private:
BTS *m_bts;
LListHead<GprsMs> m_list;
struct gprs_rlcmac_bts *m_bts;
struct llist_head m_list; /* list of struct GprsMs */
};

212
src/gprs_pcu.c Normal file
View File

@@ -0,0 +1,212 @@
/*
* Copyright (C) 2013 by Holger Hans Peter Freyther
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/ctrl/ports.h>
#include "gprs_pcu.h"
#include "bts.h"
struct gprs_pcu *the_pcu;
static struct osmo_tdef T_defs_pcu[] = {
{ .T=3113, .default_val=7, .unit=OSMO_TDEF_S, .desc="Timeout for paging", .val=0 },
{ .T=3190, .default_val=5, .unit=OSMO_TDEF_S, .desc="Return to packet idle mode after Packet DL Assignment on CCCH (s)", .val=0},
{ .T=3141, .default_val=10, .unit=OSMO_TDEF_S, .desc="Timeout for contention resolution procedure (s)", .val=0 },
{ .T=3172, .default_val=5000, .unit=OSMO_TDEF_MS, .desc="Wait Indication used in Imm Ass Reject during TBF Establishment (PACCH)", .val=0, .min_val = 0, .max_val = 255000 }, /* TS 44.060 7.1.3.2.1 */
{ .T=PCU_TDEF_NEIGH_RESOLVE_TO, .default_val=1000, .unit=OSMO_TDEF_MS, .desc="[ARFCN+BSIC]->[RAC+CI] resolution timeout (ms)", .val=0 },
{ .T=PCU_TDEF_SI_RESOLVE_TO, .default_val=1000, .unit=OSMO_TDEF_MS, .desc="RIM RAN-INFO response timeout (ms)", .val=0 },
{ .T=PCU_TDEF_NEIGH_CACHE_ALIVE, .default_val=5, .unit=OSMO_TDEF_S, .desc="[ARFCN+BSIC]->[RAC+CI] resolution cache entry storage timeout (s)", .val=0 },
{ .T=PCU_TDEF_SI_CACHE_ALIVE, .default_val=5, .unit=OSMO_TDEF_S, .desc="[RAC+CI]->[SI] resolution cache entry storage timeout (s)", .val=0 },
{ .T=-101, .default_val=30, .unit=OSMO_TDEF_S, .desc="BSSGP (un)blocking procedures timer (s)", .val=0 },
{ .T=-102, .default_val=30, .unit=OSMO_TDEF_S, .desc="BSSGP reset procedure timer (s)", .val=0 },
{ .T=-2000, .default_val=0, .unit=OSMO_TDEF_MS, .desc="Delay release of UL TBF after tx Packet Access Reject (PACCH) (ms)", .val=0 },
{ .T=-2001, .default_val=2, .unit=OSMO_TDEF_S, .desc="PACCH assignment timeout (s)", .val=0 },
{ .T=-2002, .default_val=200, .unit=OSMO_TDEF_MS, .desc="Waiting after IMM.ASS confirm timer (ms)", .val=0 },
{ .T=-2030, .default_val=60, .unit=OSMO_TDEF_S, .desc="Time to keep an idle MS object alive (s)", .val=0 }, /* slightly above T3314 (default 44s, 24.008, 11.2.2) */
{ .T=-2031, .default_val=2000, .unit=OSMO_TDEF_MS, .desc="Time to keep an idle DL TBF alive (ms)", .val=0 },
{ .T=0, .default_val=0, .unit=OSMO_TDEF_S, .desc=NULL, .val=0 } /* empty item at the end */
};
static void _update_stats_timer_cb(void *data)
{
struct gprs_pcu *pcu = (struct gprs_pcu *)data;
struct gprs_rlcmac_bts *bts;
llist_for_each_entry(bts, &pcu->bts_list, list)
osmo_time_cc_set_flag(&bts->all_allocated_pdch, bts_all_pdch_allocated(bts));
osmo_timer_schedule(&pcu->update_stats_timer, 1, 0);
}
static int gprs_pcu_talloc_destructor(struct gprs_pcu *pcu)
{
if (osmo_timer_pending(&pcu->update_stats_timer))
osmo_timer_del(&pcu->update_stats_timer);
neigh_cache_free(pcu->neigh_cache);
si_cache_free(pcu->si_cache);
return 0;
}
struct gprs_pcu *gprs_pcu_alloc(void *ctx)
{
struct gprs_pcu *pcu;
pcu = (struct gprs_pcu *)talloc_zero(ctx, struct gprs_pcu);
OSMO_ASSERT(pcu);
talloc_set_destructor(pcu, gprs_pcu_talloc_destructor);
pcu->vty.fc_interval = 1;
pcu->vty.max_cs_ul = MAX_GPRS_CS;
pcu->vty.max_cs_dl = MAX_GPRS_CS;
pcu->vty.max_mcs_ul = MAX_EDGE_MCS;
pcu->vty.max_mcs_dl = MAX_EDGE_MCS;
pcu->vty.force_alpha = (uint8_t)-1; /* don't force by default, use BTS SI13 provided value */
pcu->vty.dl_tbf_preemptive_retransmission = true;
/* By default resegmentation is supported in DL can also be configured
* through VTY */
pcu->vty.dl_arq_type = EGPRS_ARQ1;
pcu->vty.cs_adj_enabled = true;
pcu->vty.cs_adj_upper_limit = 33; /* Decrease CS if the error rate is above */
pcu->vty.cs_adj_lower_limit = 10; /* Increase CS if the error rate is below */
pcu->vty.cs_downgrade_threshold = 200;
/* CS-1 to CS-4 */
pcu->vty.cs_lqual_ranges[0].low = -256;
pcu->vty.cs_lqual_ranges[0].high = 6;
pcu->vty.cs_lqual_ranges[1].low = 5;
pcu->vty.cs_lqual_ranges[1].high = 8;
pcu->vty.cs_lqual_ranges[2].low = 7;
pcu->vty.cs_lqual_ranges[2].high = 13;
pcu->vty.cs_lqual_ranges[3].low = 12;
pcu->vty.cs_lqual_ranges[3].high = 256;
/* MCS-1 to MCS-9 */
/* Default thresholds are referenced from literature */
/* Fig. 2.3, Chapter 2, Optimizing Wireless Communication Systems, Springer (2009) */
pcu->vty.mcs_lqual_ranges[0].low = -256;
pcu->vty.mcs_lqual_ranges[0].high = 6;
pcu->vty.mcs_lqual_ranges[1].low = 5;
pcu->vty.mcs_lqual_ranges[1].high = 8;
pcu->vty.mcs_lqual_ranges[2].low = 7;
pcu->vty.mcs_lqual_ranges[2].high = 13;
pcu->vty.mcs_lqual_ranges[3].low = 12;
pcu->vty.mcs_lqual_ranges[3].high = 15;
pcu->vty.mcs_lqual_ranges[4].low = 14;
pcu->vty.mcs_lqual_ranges[4].high = 17;
pcu->vty.mcs_lqual_ranges[5].low = 16;
pcu->vty.mcs_lqual_ranges[5].high = 18;
pcu->vty.mcs_lqual_ranges[6].low = 17;
pcu->vty.mcs_lqual_ranges[6].high = 20;
pcu->vty.mcs_lqual_ranges[7].low = 19;
pcu->vty.mcs_lqual_ranges[7].high = 24;
pcu->vty.mcs_lqual_ranges[8].low = 23;
pcu->vty.mcs_lqual_ranges[8].high = 256;
pcu->vty.ns_dialect = GPRS_NS2_DIALECT_IPACCESS;
pcu->vty.ns_ip_dscp = -1;
pcu->vty.ns_priority = -1;
/* TODO: increase them when CRBB decoding is implemented */
pcu->vty.ws_base = 64;
pcu->vty.ws_pdch = 0;
pcu->vty.llc_codel_interval_msec = LLC_CODEL_USE_DEFAULT;
pcu->vty.llc_idle_ack_csec = 10;
pcu->vty.neigh_ctrl_addr = NULL; /* don't use CTRL iface for Neigh Addr Resolution */
pcu->vty.neigh_ctrl_port = OSMO_CTRL_PORT_BSC_NEIGH;
pcu->T_defs = T_defs_pcu;
osmo_tdefs_reset(pcu->T_defs);
INIT_LLIST_HEAD(&pcu->bts_list);
pcu->neigh_cache = neigh_cache_alloc(pcu, osmo_tdef_get(pcu->T_defs, PCU_TDEF_NEIGH_CACHE_ALIVE, OSMO_TDEF_S, -1));
pcu->si_cache = si_cache_alloc(pcu, osmo_tdef_get(pcu->T_defs, PCU_TDEF_SI_CACHE_ALIVE, OSMO_TDEF_S, -1));
osmo_timer_setup(&pcu->update_stats_timer, _update_stats_timer_cb, pcu);
osmo_timer_schedule(&pcu->update_stats_timer, 1, 0);
return pcu;
}
struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_nr(struct gprs_pcu *pcu, uint8_t bts_nr)
{
struct gprs_rlcmac_bts *pos;
llist_for_each_entry(pos, &pcu->bts_list, list) {
if (pos->nr == bts_nr)
return pos;
}
return NULL;
}
struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_cgi_ps(struct gprs_pcu *pcu, struct osmo_cell_global_id_ps *cgi_ps)
{
struct gprs_rlcmac_bts *pos;
llist_for_each_entry(pos, &pcu->bts_list, list) {
if (osmo_cgi_ps_cmp(&pos->cgi_ps, cgi_ps) == 0)
return pos;
}
return NULL;
}
void gprs_pcu_set_initial_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul)
{
struct gprs_rlcmac_bts *bts;
the_pcu->vty.initial_cs_dl = cs_dl;
the_pcu->vty.initial_cs_ul = cs_ul;
llist_for_each_entry(bts, &pcu->bts_list, list) {
bts_recalc_initial_cs(bts);
}
}
void gprs_pcu_set_initial_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul)
{
struct gprs_rlcmac_bts *bts;
the_pcu->vty.initial_mcs_dl = mcs_dl;
the_pcu->vty.initial_mcs_ul = mcs_ul;
llist_for_each_entry(bts, &pcu->bts_list, list) {
bts_recalc_initial_mcs(bts);
}
}
void gprs_pcu_set_max_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul)
{
struct gprs_rlcmac_bts *bts;
the_pcu->vty.max_cs_dl = cs_dl;
the_pcu->vty.max_cs_ul = cs_ul;
llist_for_each_entry(bts, &pcu->bts_list, list) {
bts_recalc_max_cs(bts);
}
}
void gprs_pcu_set_max_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul)
{
struct gprs_rlcmac_bts *bts;
the_pcu->vty.max_mcs_dl = mcs_dl;
the_pcu->vty.max_mcs_ul = mcs_ul;
llist_for_each_entry(bts, &pcu->bts_list, list) {
bts_recalc_max_mcs(bts);
}
}

150
src/gprs_pcu.h Normal file
View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2013 by Holger Hans Peter Freyther
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/timer.h>
#include "gprs_bssgp_pcu.h"
#include "coding_scheme.h"
#include "neigh_cache.h"
#define LLC_CODEL_DISABLE 0
#define LLC_CODEL_USE_DEFAULT (-1)
#define MAX_EDGE_MCS 9
#define MAX_GPRS_CS 4
#define PCU_TDEF_NEIGH_RESOLVE_TO (-1)
#define PCU_TDEF_SI_RESOLVE_TO (-2)
#define PCU_TDEF_NEIGH_CACHE_ALIVE (-10)
#define PCU_TDEF_SI_CACHE_ALIVE (-11)
/* see bts->gsmtap_categ_mask */
enum pcu_gsmtap_category {
PCU_GSMTAP_C_DL_UNKNOWN = 0, /* unknown or undecodable downlink blocks */
PCU_GSMTAP_C_DL_DUMMY = 1, /* downlink dummy blocks */
PCU_GSMTAP_C_DL_CTRL = 2, /* downlink control blocks */
PCU_GSMTAP_C_DL_DATA_GPRS = 3, /* downlink GPRS data blocks */
PCU_GSMTAP_C_DL_DATA_EGPRS = 4, /* downlink EGPRS data blocks */
PCU_GSMTAP_C_DL_PTCCH = 5, /* downlink PTCCH blocks */
PCU_GSMTAP_C_DL_AGCH = 6, /* downlink AGCH blocks */
PCU_GSMTAP_C_DL_PCH = 7, /* downlink PCH blocks */
PCU_GSMTAP_C_UL_UNKNOWN = 15, /* unknown or undecodable uplink blocks */
PCU_GSMTAP_C_UL_DUMMY = 16, /* uplink dummy blocks */
PCU_GSMTAP_C_UL_CTRL = 17, /* uplink control blocks */
PCU_GSMTAP_C_UL_DATA_GPRS = 18, /* uplink GPRS data blocks */
PCU_GSMTAP_C_UL_DATA_EGPRS = 19, /* uplink EGPRS data blocks */
PCU_GSMTAP_C_UL_RACH = 20, /* uplink RACH bursts */
PCU_GSMTAP_C_UL_PTCCH = 21, /* uplink PTCCH bursts */
};
struct gprs_rlcmac_bts;
struct GprsMs;
struct gprs_rlcmac_tbf;
typedef int (*alloc_algorithm_func_t)(struct gprs_rlcmac_bts *bts,
struct gprs_rlcmac_tbf *tbf,
bool single, int8_t use_tbf);
struct gprs_pcu {
/* Path to be used for the pcu-bts socket */
char *pcu_sock_path;
struct { /* Config Values set by VTY */
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;
bool force_initial_cs; /* false=use from BTS true=use from VTY */
bool force_initial_mcs; /* false=use from BTS true=use from VTY */
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_two_phase;
uint8_t force_alpha, gamma;
bool dl_tbf_preemptive_retransmission;
enum egprs_arq_type dl_arq_type; /* EGPRS_ARQ1 to support resegmentation in DL, EGPRS_ARQ2 for no reseg */
bool cs_adj_enabled; /* whether cs_adj_{upper,lower}_limit are used to adjust DL CS */
uint8_t cs_adj_upper_limit; /* downgrade DL CS if error rate above its value */
uint8_t cs_adj_lower_limit; /* upgrade DL CS if error rate below its value */
/* downgrade DL CS when less than specified octets are left in tx queue. Optimization, see paper:
"Theoretical Analysis of GPRS Throughput and Delay" */
uint16_t cs_downgrade_threshold;
/* Link quality range for each UL (M)CS. Below or above, next/prev (M)CS is selected. */
struct {int16_t low; int16_t high; } cs_lqual_ranges[MAX_GPRS_CS];
struct {int16_t low; int16_t high; } mcs_lqual_ranges[MAX_EDGE_MCS];
enum gprs_ns2_dialect ns_dialect; /* Are we talking Gb with IP-SNS (true) or classic Gb? */
int ns_ip_dscp;
int ns_priority;
uint16_t ws_base;
uint16_t ws_pdch; /* increase WS by this value per PDCH */
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 */
/* Remote BSS resolution sevice (CTRL iface) */
char *neigh_ctrl_addr;
uint16_t neigh_ctrl_port;
} vty;
struct gsmtap_inst *gsmtap;
uint32_t gsmtap_categ_mask;
char *gsmtap_remote_host;
struct llist_head bts_list; /* list of gprs_rlcmac_bts */
struct gprs_ns2_inst *nsi;
alloc_algorithm_func_t alloc_algorithm;
struct gprs_bssgp_pcu bssgp;
struct osmo_tdef *T_defs; /* timers controlled by PCU */
struct neigh_cache *neigh_cache; /* ARFC+BSIC -> CGI PS cache */
struct si_cache *si_cache; /* ARFC+BSIC -> CGI PS cache */
struct osmo_timer_list update_stats_timer; /* Used to update some time_cc stats periodically */
};
extern struct gprs_pcu *the_pcu;
struct gprs_pcu *gprs_pcu_alloc(void *ctx);
struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_nr(struct gprs_pcu *pcu, uint8_t bts_nr);
struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_cgi_ps(struct gprs_pcu *pcu, struct osmo_cell_global_id_ps *cgi_ps);
void gprs_pcu_set_initial_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul);
void gprs_pcu_set_initial_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul);
void gprs_pcu_set_max_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul);
void gprs_pcu_set_max_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul);

View File

@@ -18,7 +18,11 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
extern "C" {
#include <osmocom/gsm/gsm48.h>
}
#include <pcu_l1_if.h>
#include <gprs_rlcmac.h>
#include <bts.h>
@@ -28,17 +32,47 @@
extern void *tall_pcu_ctx;
int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
const char *imsi)
int gprs_rlcmac_paging_request(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi,
uint16_t pgroup)
{
LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] Paging Request (CCCH)\n");
bitvec *paging_request = bitvec_alloc(23, tall_pcu_ctx);
bitvec_unhex(paging_request, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
int plen = Encoding::write_paging_request(paging_request, ptmsi, ptmsi_len);
pcu_l1if_tx_pch(paging_request, plen, (char *)imsi);
if (log_check_level(DRLCMAC, LOGL_NOTICE)) {
char str[64];
osmo_mobile_identity_to_str_buf(str, sizeof(str), mi);
LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] Paging Request (CCCH) MI=%s\n", str);
}
bitvec *paging_request = bitvec_alloc(22, tall_pcu_ctx);
bitvec_unhex(paging_request, DUMMY_VEC);
int plen = Encoding::write_paging_request(paging_request, mi);
if (plen <= 0) {
LOGP(DRLCMAC, LOGL_ERROR, "TX: [PCU -> BTS] Failed to encode Paging Request\n");
return -1;
}
bts_do_rate_ctr_inc(bts, CTR_PCH_REQUESTS);
pcu_l1if_tx_pch(bts, paging_request, plen, pgroup);
bitvec_free(paging_request);
return 0;
}
/* Encode Application Information Request to Packet Application Information (3GPP TS 44.060 11.2.47) */
struct msgb *gprs_rlcmac_app_info_msg(const struct gsm_pcu_if_app_info_req *req) {
struct msgb *msg;
uint16_t msgb_len = req->len + 1;
struct bitvec bv = {0, msgb_len, NULL};
const enum bit_value page_mode[] = {ZERO, ZERO}; /* Normal Paging (3GPP TS 44.060 12.20) */
if (!req->len) {
LOGP(DRLCMAC, LOGL_ERROR, "Application Information Request with zero length received!\n");
return NULL;
}
msg = msgb_alloc(msgb_len, "app_info_msg");
if (!msg)
return NULL;
bv.data = msgb_put(msg, msgb_len);
bitvec_set_bits(&bv, page_mode, 2);
bitvec_set_uint(&bv, req->application_type, 4);
bitvec_set_bytes(&bv, req->data, req->len);
return msg;
}

View File

@@ -17,20 +17,25 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef GPRS_RLCMAC_H
#define GPRS_RLCMAC_H
#include <stdbool.h>
#include <stdint.h>
#include <pcu_l1_if.h>
#ifdef __cplusplus
#include <gsm_rlcmac.h>
#include <gsm_timer.h>
extern "C" {
#endif
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/pcu/pcuif_proto.h>
#include "gsm_rlcmac.h"
#ifdef __cplusplus
}
#endif
@@ -40,6 +45,7 @@ extern "C" {
*/
//#define DEBUG_DL_ASS_IDLE
#define DUMMY_VEC "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"
struct gprs_rlcmac_tbf;
struct gprs_rlcmac_bts;
@@ -72,7 +78,7 @@ int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf);
int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr);
int gprs_rlcmac_meas_rep(GprsMs *ms, Packet_Measurement_Report_t *pmr);
int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi);
@@ -83,35 +89,35 @@ 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 {
GPRS_RLCMAC_DATA_BLOCK = 0x0,
GPRS_RLCMAC_CONTROL_BLOCK = 0x1,
GPRS_RLCMAC_CONTROL_BLOCK = 0x1,
GPRS_RLCMAC_CONTROL_BLOCK_OPT = 0x2,
GPRS_RLCMAC_RESERVED = 0x3
};
int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf);
int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
const char *imsi);
struct msgb *gprs_rlcmac_app_info_msg(const struct gsm_pcu_if_app_info_req *req);
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts,
uint32_t fn, uint8_t block_nr);
int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts,
uint8_t ms_class = 0);
extern "C" {
#endif
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, bool single,
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
int8_t use_trx);
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, bool single,
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
int8_t use_trx);
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, bool single,
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
int8_t use_trx);
int gprs_rlcmac_paging_request(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi, uint16_t pgroup);
int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms_class);
#ifdef __cplusplus
}
#endif
#endif // GPRS_RLCMAC_H

View File

@@ -16,13 +16,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
extern "C" {
#include <osmocom/core/timer_compat.h>
}
#include <gprs_rlcmac.h>
#include <gprs_debug.h>
#include <pcu_l1_if.h>
#include <tbf.h>
#include <tbf_dl.h>
#include <gprs_ms.h>
#include <string.h>
#include <errno.h>
@@ -33,14 +37,13 @@
/* 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)
int gprs_rlcmac_meas_rep(GprsMs *ms, 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);
LOGPMS(ms, DRLCMACMEAS, LOGL_INFO, "Rx Measurement Report:");
switch (pmr->UnionType) {
case 0:
@@ -102,8 +105,8 @@ int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf)
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);
LOGPMS(tbf->ms(), DRLCMACMEAS, LOGL_INFO, "UL RSSI: %d dBm\n",
tbf->meas.rssi_sum / tbf->meas.rssi_num);
return 0;
}
@@ -131,6 +134,7 @@ int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
tbf->m_bw.dl_loss_received += received;
tbf->m_bw.dl_loss_lost += lost;
osmo_clock_gettime(CLOCK_MONOTONIC, &now_tv);
timespecsub(&now_tv, loss_tv, &elapsed);
if (elapsed.tv_sec < 1)
return 0;
@@ -188,4 +192,3 @@ int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets)
return 0;
}

View File

@@ -16,12 +16,13 @@
* 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_bssgp_pcu.h>
#include <gprs_rlcmac.h>
#include <pcu_l1_if.h>
#include <bts.h>
#include <tbf.h>
#include <tbf_ul.h>
#include <gprs_debug.h>
#include <gprs_ms.h>
#include <rlc.h>
@@ -33,66 +34,57 @@ extern "C" {
#include <osmocom/core/gsmtap.h>
}
static uint32_t sched_poll(BTS *bts,
uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
struct gprs_rlcmac_tbf **poll_tbf,
struct gprs_rlcmac_tbf **ul_ass_tbf,
struct gprs_rlcmac_tbf **dl_ass_tbf,
struct gprs_rlcmac_ul_tbf **ul_ack_tbf)
struct tbf_sched_candidates {
struct gprs_rlcmac_tbf *ul_ass;
struct gprs_rlcmac_tbf *dl_ass;
struct gprs_rlcmac_tbf *nacc;
struct gprs_rlcmac_ul_tbf *ul_ack;
};
static void get_ctrl_msg_tbf_candidates(const struct gprs_rlcmac_pdch *pdch,
struct tbf_sched_candidates *tbf_cand)
{
struct gprs_rlcmac_ul_tbf *ul_tbf;
struct gprs_rlcmac_dl_tbf *dl_tbf;
LListHead<gprs_rlcmac_tbf> *pos;
uint32_t poll_fn;
struct llist_item *pos;
/* check special TBF for events */
poll_fn = fn + 4;
if ((block_nr % 3) == 2)
poll_fn ++;
poll_fn = poll_fn % GSM_MAX_FN;
llist_for_each(pos, &bts->ul_tbfs()) {
ul_tbf = as_ul_tbf(pos->entry());
llist_for_each_entry(pos, &pdch->trx->ul_tbfs, list) {
ul_tbf = as_ul_tbf((struct gprs_rlcmac_tbf *)pos->entry);
OSMO_ASSERT(ul_tbf);
/* this trx, this ts */
if (ul_tbf->trx->trx_no != trx || !ul_tbf->is_control_ts(ts))
if (!ul_tbf->is_control_ts(pdch->ts_no))
continue;
/* polling for next uplink block */
if (ul_tbf->poll_scheduled() && ul_tbf->poll_fn == poll_fn)
*poll_tbf = ul_tbf;
if (ul_tbf->ul_ack_state_is(GPRS_RLCMAC_UL_ACK_SEND_ACK))
*ul_ack_tbf = ul_tbf;
if (ul_tbf->dl_ass_state_is(GPRS_RLCMAC_DL_ASS_SEND_ASS))
*dl_ass_tbf = ul_tbf;
if (ul_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS)
|| ul_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
*ul_ass_tbf = ul_tbf;
if (tbf_ul_ack_rts(ul_tbf))
tbf_cand->ul_ack = ul_tbf;
if (tbf_dl_ass_rts(ul_tbf))
tbf_cand->dl_ass = ul_tbf;
if (tbf_ul_ass_rts(ul_tbf))
tbf_cand->ul_ass = ul_tbf;
/* NACC ready to send. TFI assigned is needed to send messages */
if (ul_tbf->is_tfi_assigned() && ms_nacc_rts(ul_tbf->ms()))
tbf_cand->nacc = ul_tbf;
/* FIXME: Is this supposed to be fair? The last TBF for each wins? Maybe use llist_add_tail and skip once we have all
states? */
}
llist_for_each(pos, &bts->dl_tbfs()) {
dl_tbf = as_dl_tbf(pos->entry());
llist_for_each_entry(pos, &pdch->trx->dl_tbfs, list) {
dl_tbf = as_dl_tbf((struct gprs_rlcmac_tbf *)pos->entry);
OSMO_ASSERT(dl_tbf);
/* this trx, this ts */
if (dl_tbf->trx->trx_no != trx || !dl_tbf->is_control_ts(ts))
if (!dl_tbf->is_control_ts(pdch->ts_no))
continue;
/* polling for next uplink block */
if (dl_tbf->poll_scheduled() && dl_tbf->poll_fn == poll_fn)
*poll_tbf = dl_tbf;
if (dl_tbf->dl_ass_state_is(GPRS_RLCMAC_DL_ASS_SEND_ASS))
*dl_ass_tbf = dl_tbf;
if (dl_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS)
|| dl_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
*ul_ass_tbf = dl_tbf;
if (tbf_dl_ass_rts(dl_tbf))
tbf_cand->dl_ass = dl_tbf;
if (tbf_ul_ass_rts(dl_tbf))
tbf_cand->ul_ass = dl_tbf;
/* NACC ready to send. TFI assigned is needed to send messages */
if (dl_tbf->is_tfi_assigned() && ms_nacc_rts(dl_tbf->ms()))
tbf_cand->nacc = dl_tbf;
}
return poll_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)
static struct gprs_rlcmac_ul_tbf *sched_select_uplink(struct gprs_rlcmac_pdch *pdch, bool require_gprs_only)
{
struct gprs_rlcmac_ul_tbf *tbf;
uint8_t usf = 0x07;
uint8_t i, tfi;
/* select uplink resource */
@@ -106,65 +98,94 @@ static uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
/* 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_is_not(GPRS_RLCMAC_FLOW))
if (tbf->state_is_not(TBF_ST_FLOW))
continue;
/* use this USF */
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 resource of UL TFI=%d\n", trx, ts, fn,
block_nr, usf, tfi);
if (require_gprs_only && tbf->is_egprs_enabled())
continue;
/* use this USF (tbf) */
/* next TBF to handle resource is the next one */
pdch->next_ul_tfi = (tfi + 1) & 31;
break;
return tbf;
}
return usf;
return NULL;
}
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_ul_tbf *ul_ack_tbf)
struct msgb *sched_app_info(struct gprs_rlcmac_tbf *tbf) {
struct gprs_rlcmac_bts *bts;
struct msgb *msg = NULL;
if (!tbf || !tbf->ms()->app_info_pending)
return NULL;
bts = tbf->bts;
if (bts->app_info) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Sending Packet Application Information message\n");
msg = msgb_copy(bts->app_info, "app_info_msg_sched");
} else
LOGP(DRLCMACSCHED, LOGL_ERROR, "MS has app_info_pending flag set, but no Packet Application Information"
" message stored in BTS!\n");
tbf->ms()->app_info_pending = false;
bts->app_info_pending--;
if (!bts->app_info_pending) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Packet Application Information successfully sent to all MS with active"
" TBF\n");
msgb_free(bts->app_info);
bts->app_info = NULL;
}
return msg;
}
static struct msgb *sched_select_ctrl_msg(struct gprs_rlcmac_pdch *pdch, uint32_t fn,
uint8_t block_nr, struct tbf_sched_candidates *tbfs)
{
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 };
struct gprs_rlcmac_tbf *next_list[] = { tbfs->ul_ass,
tbfs->dl_ass,
tbfs->ul_ack,
tbfs->nacc };
uint8_t ts = pdch->ts_no;
for (size_t i = 0; i < ARRAY_SIZE(next_list); ++i) {
tbf = next_list[(pdch->next_ctrl_prio + i) % 3];
if (!tbf)
continue;
/* Send Packet Application Information first (ETWS primary notifications) */
msg = sched_app_info(tbfs->dl_ass);
/*
* Assignments for the same direction have lower precedence,
* because they may kill the TBF when the CONTROL ACK is
* received, thus preventing the others from being processed.
*/
if (tbf == ul_ass_tbf && tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
msg = ul_ass_tbf->create_packet_access_reject();
else if (tbf == ul_ass_tbf && tbf->direction ==
GPRS_RLCMAC_DL_TBF)
if (tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
msg = ul_ass_tbf->create_packet_access_reject();
else
msg = ul_ass_tbf->create_ul_ass(fn, ts);
else if (tbf == dl_ass_tbf && tbf->direction == GPRS_RLCMAC_UL_TBF)
msg = dl_ass_tbf->create_dl_ass(fn, ts);
else if (tbf == ul_ack_tbf)
msg = ul_ack_tbf->create_ul_ack(fn, ts);
if (!msg) {
for (size_t i = 0; i < ARRAY_SIZE(next_list); ++i) {
tbf = next_list[(pdch->next_ctrl_prio + i) % ARRAY_SIZE(next_list)];
if (!tbf)
continue;
if (!msg) {
tbf = NULL;
continue;
/*
* Assignments for the same direction have lower precedence,
* because they may kill the TBF when the CONTROL ACK is
* received, thus preventing the others from being processed.
*/
if (tbf == tbfs->ul_ass && tbf->ul_ass_state_is(TBF_UL_ASS_SEND_ASS_REJ))
msg = tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, fn, ts);
else if (tbf == tbfs->ul_ass && tbf->direction == GPRS_RLCMAC_DL_TBF)
msg = tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, fn, ts);
else if (tbf == tbfs->dl_ass && tbf->direction == GPRS_RLCMAC_UL_TBF)
msg = tbf_dl_ass_create_rlcmac_msg(tbfs->dl_ass, fn, ts);
else if (tbf == tbfs->ul_ack)
msg = tbf_ul_ack_create_rlcmac_msg(tbfs->ul_ack, fn, ts);
else if (tbf == tbfs->nacc) {
msg = ms_nacc_create_rlcmac_msg(tbf->ms(), tbf, fn, ts);
}
if (!msg) {
tbf = NULL;
continue;
}
pdch->next_ctrl_prio = (pdch->next_ctrl_prio + 1) % ARRAY_SIZE(next_list);
break;
}
pdch->next_ctrl_prio += 1;
pdch->next_ctrl_prio %= 3;
break;
}
if (!msg) {
@@ -173,37 +194,36 @@ static struct msgb *sched_select_ctrl_msg(
* 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);
if (tbfs->dl_ass) {
tbf = tbfs->dl_ass;
msg = tbf_dl_ass_create_rlcmac_msg(tbfs->dl_ass, fn, ts);
} else if (tbfs->ul_ass) {
tbf = tbfs->ul_ass;
msg = tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, fn, ts);
}
}
/* any message */
if (msg) {
if (!tbf) {
LOGP(DRLCMACSCHED, LOGL_ERROR,
"Control message to be scheduled, but no TBF (TRX=%d, TS=%d)\n", trx, ts);
LOGPDCH(pdch, DRLCMACSCHED, LOGL_ERROR, "FN=%" PRIu32
" Control message to be scheduled, but no TBF\n", fn);
msgb_free(msg);
return NULL;
}
tbf->rotate_in_list();
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
"message at RTS for %s (TRX=%d, TS=%d)\n",
tbf_name(tbf), trx, ts);
/* Updates the dl ctrl msg counter for ms */
tbf->ms()->update_dl_ctrl_msg();
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "FN=%" PRIu32
" Scheduling control message at RTS for %s\n",
fn, tbf_name(tbf));
rate_ctr_inc(rate_ctr_group_get_ctr(tbf->ms()->ctrs, MS_CTR_DL_CTRL_MSG_SCHED));
return msg;
}
/* schedule PACKET PAGING REQUEST, if any are pending */
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);
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "FN=%" PRIu32
" Scheduling paging request message at RTS\n", fn);
return msg;
}
@@ -213,17 +233,19 @@ static struct msgb *sched_select_ctrl_msg(
static inline enum tbf_dl_prio tbf_compute_priority(const struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_dl_tbf *tbf,
uint8_t ts, uint32_t fn, int age)
{
const gprs_rlc_dl_window *w = tbf->window();
int age_thresh1 = msecs_to_frames(200),
age_thresh2 = msecs_to_frames(OSMO_MIN(BTS::TIMER_T3190_MSEC/2, bts->dl_tbf_idle_msec));
const gprs_rlc_dl_window *w = static_cast<gprs_rlc_dl_window *>(tbf->window());
unsigned long msecs_t3190 = osmo_tdef_get(the_pcu->T_defs, 3190, OSMO_TDEF_MS, -1);
unsigned long dl_tbf_idle_msec = osmo_tdef_get(the_pcu->T_defs, -2031, OSMO_TDEF_MS, -1);
int age_thresh1 = msecs_to_frames(200);
int age_thresh2 = msecs_to_frames(OSMO_MIN(msecs_t3190/2, dl_tbf_idle_msec));
if (tbf->is_control_ts(ts) && tbf->need_control_ts())
if (tbf->is_control_ts(ts) && tbf->need_poll_for_dl_ack_nack())
return DL_PRIO_CONTROL;
if (tbf->is_control_ts(ts) && age > age_thresh2 && age_thresh2 > 0)
return DL_PRIO_HIGH_AGE;
if ((tbf->state_is(GPRS_RLCMAC_FLOW) && tbf->have_data()) || w->resend_needed() >= 0)
if ((tbf->state_is(TBF_ST_FLOW) && tbf->have_data()) || w->resend_needed() >= 0)
return DL_PRIO_NEW_DATA;
if (tbf->is_control_ts(ts) && age > age_thresh1 && tbf->keep_open(fn))
@@ -235,13 +257,39 @@ static inline enum tbf_dl_prio tbf_compute_priority(const struct gprs_rlcmac_bts
return DL_PRIO_NONE;
}
static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts, uint32_t fn,
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
/* Check if next data block of a TBF can be encoded in GMSK [(M)CS1-4]. */
static bool can_produce_gmsk_data_block_next(struct gprs_rlcmac_dl_tbf *tbf, enum tbf_dl_prio prio)
{
const gprs_rlc_dl_window *w;
/* GPRS TBFs can always send GMSK */
if (!tbf->is_egprs_enabled())
return true;
switch (prio) {
case DL_PRIO_CONTROL:
/* Control blocks are always CS-1 */
return true;
case DL_PRIO_NEW_DATA:
/* We can send any new data (no block generated yet) using any
* MCS. However, we don't (yet) support resegmenting already
* sent blocks (NACKed blocks in this case) into lower MCS of
* the same family. See OS#4966 */
w = static_cast<gprs_rlc_dl_window *>(tbf->window());
return w->resend_needed() < 0;
default:
return false;
}
}
static struct msgb *sched_select_dl_data_msg(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_pdch *pdch,
uint32_t fn, uint8_t block_nr, enum mcs_kind req_mcs_kind,
bool *is_egprs)
{
struct msgb *msg = NULL;
struct gprs_rlcmac_dl_tbf *tbf, *prio_tbf = NULL;
enum tbf_dl_prio prio, max_prio = DL_PRIO_NONE;
uint8_t ts = pdch->ts_no;
uint8_t i, tfi, prio_tfi;
int age;
@@ -257,12 +305,12 @@ static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
if (tbf->direction != GPRS_RLCMAC_DL_TBF)
continue;
/* no DL resources needed, go next */
if (tbf->state_is_not(GPRS_RLCMAC_FLOW)
&& tbf->state_is_not(GPRS_RLCMAC_FINISHED))
if (tbf->state_is_not(TBF_ST_FLOW)
&& tbf->state_is_not(TBF_ST_FINISHED))
continue;
/* waiting for CCCH IMM.ASS confirm */
if (tbf->m_wait_confirm)
/* If a GPRS (CS1-4) Dl block is required, skip EGPRS(_GSMK) tbfs: */
if (req_mcs_kind == GPRS && tbf->is_egprs_enabled())
continue;
age = tbf->frames_since_last_poll(fn);
@@ -272,6 +320,16 @@ static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
if (prio == DL_PRIO_NONE)
continue;
/* If a GPRS (CS1-4/MCS1-4) Dl block is required, downgrade MCS
* below instead of skipping. However, downgrade can only be
* done on new data BSNs (not yet sent) and control blocks. */
if (req_mcs_kind == EGPRS_GMSK && !can_produce_gmsk_data_block_next(tbf, prio)) {
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "FN=%" PRIu32
" Cannot downgrade EGPRS TBF with prio %d for %s\n",
fn, prio, tbf_name(tbf));
continue;
}
/* get the TBF with the highest priority */
if (prio > max_prio) {
prio_tfi = tfi;
@@ -281,13 +339,14 @@ static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
}
if (prio_tbf) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data message at "
"RTS for DL TFI=%d (TRX=%d, TS=%d) prio=%d\n",
prio_tfi, trx, ts, max_prio);
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "FN=%" PRIu32
" Scheduling data message at RTS for DL TFI=%d prio=%d mcs_mode_restrict=%s\n",
fn, prio_tfi, max_prio, mode_name(req_mcs_kind));
/* next TBF to handle resource is the next one */
pdch->next_dl_tfi = (prio_tfi + 1) & 31;
/* generate DL data block */
msg = prio_tbf->create_dl_acked_block(fn, ts);
msg = prio_tbf->create_dl_acked_block(fn, ts, req_mcs_kind);
*is_egprs = prio_tbf->is_egprs_enabled();
}
return msg;
@@ -313,7 +372,7 @@ static struct msgb *sched_dummy(void)
return msg;
}
static inline void tap_n_acc(const struct msgb *msg, const struct gprs_rlcmac_bts *bts, uint8_t trx, uint8_t ts,
static inline void tap_n_acc(const struct msgb *msg, struct gprs_rlcmac_bts *bts, uint8_t trx, uint8_t ts,
uint32_t fn, enum pcu_gsmtap_category cat)
{
if (!msg)
@@ -321,19 +380,19 @@ static inline void tap_n_acc(const struct msgb *msg, const struct gprs_rlcmac_bt
switch(cat) {
case PCU_GSMTAP_C_DL_CTRL:
bts->bts->rlc_sent_control();
bts->bts->send_gsmtap(PCU_GSMTAP_C_DL_CTRL, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
bts_do_rate_ctr_inc(bts, CTR_RLC_SENT_CONTROL);
bts_send_gsmtap(bts, PCU_GSMTAP_C_DL_CTRL, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
msg->len);
break;
case PCU_GSMTAP_C_DL_DATA_GPRS:
bts->bts->rlc_sent();
/* FIXME: distinguish between GPRS and EGPRS */
bts->bts->send_gsmtap(PCU_GSMTAP_C_DL_DATA_GPRS, false, trx, ts, GSMTAP_CHANNEL_PDTCH, fn, msg->data,
case PCU_GSMTAP_C_DL_DATA_EGPRS:
bts_do_rate_ctr_inc(bts, CTR_RLC_SENT);
bts_send_gsmtap(bts, cat, false, trx, ts, GSMTAP_CHANNEL_PDTCH, fn, msg->data,
msg->len);
break;
case PCU_GSMTAP_C_DL_DUMMY:
bts->bts->rlc_sent_dummy();
bts->bts->send_gsmtap(PCU_GSMTAP_C_DL_DUMMY, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
bts_do_rate_ctr_inc(bts, CTR_RLC_SENT_DUMMY);
bts_send_gsmtap(bts, PCU_GSMTAP_C_DL_DUMMY, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
msg->len);
break;
default:
@@ -346,79 +405,138 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
uint32_t fn, uint8_t block_nr)
{
struct gprs_rlcmac_pdch *pdch;
struct gprs_rlcmac_tbf *poll_tbf = NULL, *dl_ass_tbf = NULL,
*ul_ass_tbf = NULL;
struct gprs_rlcmac_ul_tbf *ul_ack_tbf = NULL;
uint8_t usf = 0x7;
struct tbf_sched_candidates tbf_cand = {0};
struct gprs_rlcmac_tbf *poll_tbf;
struct gprs_rlcmac_ul_tbf *usf_tbf = NULL;
struct gprs_rlcmac_sba *sba;
uint8_t usf;
struct msgb *msg = NULL;
uint32_t poll_fn, sba_fn;
uint32_t poll_fn;
enum pcu_gsmtap_category gsmtap_cat = PCU_GSMTAP_C_DL_DUMMY; /* init: make gcc happy */
bool tx_is_egprs = false;
bool require_gprs_only;
enum mcs_kind req_mcs_kind; /* Restrict CS/MCS if DL Data block is to be sent */
if (trx >= 8 || ts >= 8)
return -EINVAL;
pdch = &bts->trx[trx].pdch[ts];
if (!pdch->is_enabled()) {
LOGP(DRLCMACSCHED, LOGL_ERROR, "Received RTS on disabled PDCH: "
"TRX=%d TS=%d\n", trx, ts);
LOGPDCH(pdch, DRLCMACSCHED, LOGL_INFO, "Received RTS on disabled TS\n");
return -EIO;
}
/* store last frame number of RTS */
pdch->last_rts_fn = fn;
poll_fn = sched_poll(bts->bts, trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
&dl_ass_tbf, &ul_ack_tbf);
/* check uplink resource for polling */
if (poll_tbf)
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\n", trx, ts, fn,
block_nr, poll_fn,
tbf_name(poll_tbf));
/* use free USF */
/* else. check for sba */
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 resource */
else
usf = sched_select_uplink(trx, ts, fn, block_nr, pdch);
/* require_gprs_only: Prioritize USF for GPRS-only MS here,
* since anyway we'll need to tx a Dl block with CS1-4 due to
* synchronization requirements. See 3GPP TS 03.64 version
* 8.12.0
*/
require_gprs_only = (pdch->fn_without_cs14 == MS_RESYNC_NUM_FRAMES - 1);
if (require_gprs_only) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "TRX=%d TS=%d FN=%d "
"synchronization frame (every 18 frames), only CS1-4 allowed",
trx, ts, fn);
req_mcs_kind = GPRS; /* only GPRS CS1-4 allowed, all MS need to be able to decode it */
} else {
req_mcs_kind = EGPRS; /* all kinds are fine */
}
/* polling for next uplink block */
poll_fn = rts_next_fn(fn, block_nr);
/* check for sba */
if ((sba = pdch_ulc_get_sba(pdch->ulc, poll_fn))) {
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: "
"FN=%d block_nr=%d scheduling free USF for "
"single block allocation at FN=%d\n", fn, block_nr, sba->fn);
/* else, check uplink resource for polling */
} else if ((poll_tbf = pdch_ulc_get_tbf_poll(pdch->ulc, poll_fn))) {
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: FN=%d "
"block_nr=%d scheduling free USF for polling at FN=%d of %s\n",
fn, block_nr, poll_fn, tbf_name(poll_tbf));
/* If POLL TBF is UL and already has a USF assigned on this TS,
* let's set its USF in the DL msg. This is not really needed,
* but it helps understand better the flow when looking at
* pcaps. */
if (poll_tbf->direction == GPRS_RLCMAC_UL_TBF && as_ul_tbf(poll_tbf)->m_usf[ts] != USF_INVALID)
usf_tbf = as_ul_tbf(poll_tbf);
/* else, search for uplink tbf */
} else if ((usf_tbf = sched_select_uplink(pdch, require_gprs_only))) {
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: FN=%d "
"block_nr=%d scheduling USF=%d for %s, expect answer on UL FN=%d\n",
fn, block_nr, usf_tbf->m_usf[pdch->ts_no], tbf_name(usf_tbf), poll_fn);
pdch_ulc_reserve_tbf_usf(pdch->ulc, poll_fn, usf_tbf);
}
/* If MS selected for USF is GPRS-only, then it will only be
* able to read USF if dl block uses GMSK (CS1-4, MCS1-4) */
if (usf_tbf && req_mcs_kind == EGPRS && ms_mode(usf_tbf->ms()) != EGPRS)
req_mcs_kind = EGPRS_GMSK;
get_ctrl_msg_tbf_candidates(pdch, &tbf_cand);
/* Prio 1: select control message */
msg = sched_select_ctrl_msg(trx, ts, fn, block_nr, pdch, ul_ass_tbf,
dl_ass_tbf, ul_ack_tbf);
tap_n_acc(msg, bts, trx, ts, fn, PCU_GSMTAP_C_DL_CTRL);
if ((msg = sched_select_ctrl_msg(pdch, fn, block_nr, &tbf_cand))) {
gsmtap_cat = PCU_GSMTAP_C_DL_CTRL;
}
/* Prio 2: select data message for downlink */
if (!msg) {
msg = sched_select_downlink(bts, trx, ts, fn, block_nr, pdch);
tap_n_acc(msg, bts, trx, ts, fn, PCU_GSMTAP_C_DL_DATA_GPRS);
else if((msg = sched_select_dl_data_msg(bts, pdch, fn, block_nr, req_mcs_kind, &tx_is_egprs))) {
gsmtap_cat = tx_is_egprs ? PCU_GSMTAP_C_DL_DATA_EGPRS :
PCU_GSMTAP_C_DL_DATA_GPRS;
}
/* Prio 3: send dummy control message if need to poll or USF */
else {
/* If there's no TBF attached to this PDCH, we can submit an empty
* data_req since there's nothing to transmit nor to poll/USF. This
* way we help BTS energy saving (on TRX!=C0) by sending nothing
* instead of a dummy block. The early return is done here and
* not at the start of the function because the condition below
* (num_tbfs==0) may not be enough, because temporary dummy TBFs
* created to send Imm Ass Rej (see handle_tbf_reject()) don't
* have a TFI assigned and hence are not attached to the PDCH
* TS, so they don't show up in the count below.
*/
const unsigned num_tbfs = pdch->num_tbfs(GPRS_RLCMAC_DL_TBF)
+ pdch->num_tbfs(GPRS_RLCMAC_UL_TBF);
bool skip_idle = (num_tbfs == 0);
#ifdef ENABLE_DIRECT_PHY
/* In DIRECT_PHY mode we want to always submit something to L1 in
* TRX0, since BTS is not preparing dummy bursts on idle TS for us */
skip_idle = skip_idle && trx != 0;
#endif
if (!skip_idle && (msg = sched_dummy())) {
/* increase counter */
gsmtap_cat = PCU_GSMTAP_C_DL_DUMMY;
} else {
msg = NULL; /* submit empty frame */
}
}
/* Prio 3: send dummy contol message */
if (!msg) {
/* increase counter */
msg = sched_dummy();
tap_n_acc(msg, bts, trx, ts, fn, PCU_GSMTAP_C_DL_DUMMY);
if (tx_is_egprs && pdch->has_gprs_only_tbf_attached()) {
pdch->fn_without_cs14 += 1;
} else {
pdch->fn_without_cs14 = 0;
}
if (!msg)
return -ENOMEM;
/* msg is now available */
bts->bts->rlc_dl_bytes(msg->data_len);
/* set USF */
OSMO_ASSERT(msgb_length(msg) > 0);
msg->data[0] = (msg->data[0] & 0xf8) | usf;
/* Used to measure the leak rate, count all blocks */
gprs_bssgp_update_frames_sent();
/* send PDTCH/PACCH to L1 */
pcu_l1if_tx_pdtch(msg, trx, ts, bts->trx[trx].arfcn, fn, block_nr);
if (msg) {
/* msg is now available */
bts_do_rate_ctr_add(bts, CTR_RLC_DL_BYTES, msgb_length(msg));
/* set USF */
OSMO_ASSERT(msgb_length(msg) > 0);
usf = usf_tbf ? usf_tbf->m_usf[ts] : USF_UNUSED;
msg->data[0] = (msg->data[0] & 0xf8) | usf;
/* Send to GSMTAP */
tap_n_acc(msg, bts, trx, ts, fn, gsmtap_cat);
}
/* send PDTCH/PACCH to L1. msg=NULL accepted too (idle block) */
pcu_l1if_tx_pdtch(msg, bts, trx, ts, bts->trx[trx].arfcn, fn, block_nr);
return 0;
}

View File

@@ -23,6 +23,7 @@
#include <gprs_debug.h>
#include <bts.h>
#include <tbf.h>
#include <tbf_ul.h>
#include <pdch.h>
#include <gprs_ms.h>
#include <pcu_utils.h>
@@ -40,6 +41,13 @@ extern "C" {
/* Consider a PDCH as idle if has at most this number of TBFs assigned to it */
#define PDCH_IDLE_TBF_THRESH 1
#define LOGPSL(tbf, level, fmt, args...) LOGP(DRLCMAC, level, "[%s] " fmt, \
(tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL", ## args)
#define LOGPAL(tbf, kind, single, trx_n, level, fmt, args...) LOGPSL(tbf, level, \
"algo %s <%s> (suggested TRX: %d): " fmt, \
kind, single ? "single" : "multi", trx_n, ## args)
static char *set_flag_chars(char *buf, uint8_t val, char set_char, char unset_char = 0)
{
int i;
@@ -139,7 +147,7 @@ static int compute_usage_for_algo_a(const struct gprs_rlcmac_pdch *pdch, enum gp
*/
static int find_least_busy_pdch(const struct gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction dir, uint8_t mask,
int (*fn)(const struct gprs_rlcmac_pdch *, enum gprs_rlcmac_tbf_direction dir),
int *free_tfi = 0, int *free_usf = 0)
int *free_tfi = NULL, int *free_usf = NULL)
{
unsigned ts;
int min_used = INT_MAX;
@@ -231,21 +239,21 @@ static void assign_dlink_tbf(struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_d
attach_tbf_to_pdch(pdch, tbf);
}
static int find_trx(const struct gprs_rlcmac_bts *bts_data, const GprsMs *ms, int8_t use_trx)
static int find_trx(const struct gprs_rlcmac_bts *bts, const GprsMs *ms, int8_t use_trx)
{
unsigned trx_no;
unsigned ts;
/* We must use the TRX currently actively used by an MS */
if (ms && ms->current_trx())
return ms->current_trx()->trx_no;
if (ms && ms_current_trx(ms))
return ms_current_trx(ms)->trx_no;
if (use_trx >= 0 && use_trx < 8)
return use_trx;
/* Find the first TRX that has a PDCH with a free UL and DL TFI */
for (trx_no = 0; trx_no < ARRAY_SIZE(bts_data->trx); trx_no += 1) {
const struct gprs_rlcmac_trx *trx = &bts_data->trx[trx_no];
for (trx_no = 0; trx_no < ARRAY_SIZE(bts->trx); trx_no += 1) {
const struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
if (!pdch->is_enabled())
@@ -264,14 +272,14 @@ static int find_trx(const struct gprs_rlcmac_bts *bts_data, const GprsMs *ms, in
return -EBUSY;
}
static bool idle_pdch_avail(const struct gprs_rlcmac_bts *bts_data)
static bool idle_pdch_avail(const struct gprs_rlcmac_bts *bts)
{
unsigned trx_no;
unsigned ts;
/* Find the first PDCH with an unused DL TS */
for (trx_no = 0; trx_no < ARRAY_SIZE(bts_data->trx); trx_no += 1) {
const struct gprs_rlcmac_trx *trx = &bts_data->trx[trx_no];
for (trx_no = 0; trx_no < ARRAY_SIZE(bts->trx); trx_no += 1) {
const struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
if (!pdch->is_enabled())
@@ -290,20 +298,21 @@ static bool idle_pdch_avail(const struct gprs_rlcmac_bts *bts_data)
/*! Return free TFI
*
* \param[in] bts Pointer to BTS struct
* \param[in] trx Optional pointer to TRX struct
* \param[in] ms Pointer to MS object
* \param[in] dir DL or UL direction
* \param[in] use_trx which TRX to use or -1 if it should be selected based on what MS uses
* \param[out] trx_no_ TRX number on which TFI was found
* \returns negative error code or 0 on success
*/
static int tfi_find_free(const BTS *bts, const gprs_rlcmac_trx *trx, const GprsMs *ms,
static int tfi_find_free(const struct gprs_rlcmac_bts *bts, const GprsMs *ms,
enum gprs_rlcmac_tbf_direction dir, int8_t use_trx, uint8_t *trx_no_)
{
const struct gprs_rlcmac_trx *trx;
int tfi;
uint8_t trx_no;
if (trx) {
/* If MS is already doing stuff on a TRX, set use_trx to it: */
if ((trx = ms_current_trx(ms))) {
if (use_trx >= 0 && use_trx != trx->trx_no) {
LOGP(DRLCMAC, LOGL_ERROR, "- Requested incompatible TRX %d (current is %d)\n",
use_trx, trx->trx_no);
@@ -312,10 +321,7 @@ static int tfi_find_free(const BTS *bts, const gprs_rlcmac_trx *trx, const GprsM
use_trx = trx->trx_no;
}
if (use_trx == -1 && ms->current_trx())
use_trx = ms->current_trx()->trx_no;
tfi = bts->tfi_find_free(dir, &trx_no, use_trx);
tfi = bts_tfi_find_free(bts, dir, &trx_no, use_trx);
if (tfi < 0)
return -EBUSY;
@@ -330,13 +336,12 @@ static int tfi_find_free(const BTS *bts, const gprs_rlcmac_trx *trx, const GprsM
* Assign single slot for uplink and downlink
*
* \param[in,out] bts Pointer to BTS struct
* \param[in,out] ms_ Pointer to MS object
* \param[in,out] tbf_ Pointer to TBF struct
* \param[in,out] tbf Pointer to TBF struct
* \param[in] single flag indicating if we should force single-slot allocation
* \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
* \returns negative error code or 0 on success
*/
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcmac_tbf *tbf_, bool single,
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
int8_t use_trx)
{
struct gprs_rlcmac_pdch *pdch;
@@ -347,26 +352,24 @@ int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcm
int usf = -1;
uint8_t mask = 0xff;
const char *mask_reason = NULL;
const GprsMs *ms = ms_;
const gprs_rlcmac_tbf *tbf = tbf_;
gprs_rlcmac_trx *trx = ms->current_trx();
struct GprsMs *ms = tbf->ms();
gprs_rlcmac_trx *trx = ms_current_trx(ms);
LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm A) for class "
"%d\n", tbf->ms_class());
LOGPAL(tbf, "A", single, use_trx, LOGL_DEBUG, "Alloc start\n");
trx_no = find_trx(bts, ms, use_trx);
if (trx_no < 0) {
LOGP(DRLCMAC, LOGL_NOTICE,
"- Failed to find a usable TRX (TFI exhausted)\n");
LOGPAL(tbf, "A", single, use_trx, LOGL_NOTICE,
"failed to find a usable TRX (TFI exhausted)\n");
return trx_no;
}
if (!trx)
trx = &bts->trx[trx_no];
dl_slots = ms->reserved_dl_slots();
ul_slots = ms->reserved_ul_slots();
dl_slots = ms_reserved_dl_slots(ms);
ul_slots = ms_reserved_ul_slots(ms);
ts = ms->first_common_ts();
ts = ms_first_common_ts(ms);
if (ts >= 0) {
mask_reason = "need to reuse TS";
@@ -385,40 +388,38 @@ int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcm
&tfi, &usf);
if (tbf->direction == GPRS_RLCMAC_UL_TBF && usf < 0) {
LOGP(DRLCMAC, LOGL_NOTICE, "- Failed "
"to allocate a TS, no USF available\n");
LOGPAL(tbf, "A", single, use_trx, LOGL_NOTICE,
"failed to allocate a TS, no USF available\n");
return -EBUSY;
}
if (ts < 0) {
LOGP(DRLCMAC, LOGL_NOTICE, "- Failed "
"to allocate a TS, no TFI available\n");
LOGPAL(tbf, "A", single, use_trx, LOGL_NOTICE,
"failed to allocate a TS, no TFI available\n");
return -EBUSY;
}
pdch = &trx->pdch[ts];
/* The allocation will be successful, so the system state and tbf_/ms_
/* The allocation will be successful, so the system state and tbf/ms
* may be modified from now on. */
if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
struct gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf_);
LOGP(DRLCMAC, LOGL_DEBUG, "- Assign uplink TS=%d TFI=%d USF=%d\n",
ts, tfi, usf);
struct gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf);
LOGPSL(tbf, LOGL_DEBUG, "Assign uplink TS=%d TFI=%d USF=%d\n", ts, tfi, usf);
assign_uplink_tbf_usf(pdch, ul_tbf, tfi, usf);
} else {
struct gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf_);
LOGP(DRLCMAC, LOGL_DEBUG, "- Assign downlink TS=%d TFI=%d\n",
ts, tfi);
struct gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf);
LOGPSL(tbf, LOGL_DEBUG, "Assign downlink TS=%d TFI=%d\n", ts, tfi);
assign_dlink_tbf(pdch, dl_tbf, tfi);
}
tbf_->trx = trx;
tbf->trx = trx;
/* the only one TS is the common TS */
tbf_->first_ts = tbf_->first_common_ts = ts;
ms_->set_reserved_slots(trx, 1 << ts, 1 << ts);
tbf->first_ts = tbf->first_common_ts = ts;
ms_set_reserved_slots(ms, trx, 1 << ts, 1 << ts);
tbf_->upgrade_to_multislot = 0;
bts->bts->tbf_alloc_algo_a();
tbf->upgrade_to_multislot = false;
bts_do_rate_ctr_inc(bts, CTR_TBF_ALLOC_ALGO_A);
return 0;
}
@@ -511,9 +512,11 @@ static bool skip_slot(uint8_t mslot_class, bool check_tr,
*/
int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *ul_slots, uint8_t *dl_slots)
{
uint8_t Tx = mslot_class_get_tx(mslot_class), /* Max number of Tx slots */
Sum = mslot_class_get_sum(mslot_class), /* Max number of Tx + Rx slots */
max_slots, num_tx, mask_sel, pdch_slots, ul_ts, dl_ts;
const uint8_t Rx = mslot_class_get_rx(mslot_class), /* Max number of Rx slots */
Tx = mslot_class_get_tx(mslot_class), /* Max number of Tx slots */
Sum = mslot_class_get_sum(mslot_class), /* Max number of Tx + Rx slots */
Type = mslot_class_get_type(mslot_class);
uint8_t max_slots, num_rx, num_tx, mask_sel, pdch_slots, ul_ts, dl_ts;
int16_t rx_window, tx_window;
char slot_info[9] = {0};
int max_capacity = -1;
@@ -529,7 +532,7 @@ int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *
return -EINVAL;
}
max_slots = OSMO_MAX(mslot_class_get_rx(mslot_class), Tx);
max_slots = OSMO_MAX(Rx, Tx);
if (*dl_slots == 0)
*dl_slots = 0xff;
@@ -551,81 +554,97 @@ int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *
/* Check for each UL (TX) slot */
/* Iterate through possible numbers of TX slots */
for (num_tx = 1; num_tx <= mslot_class_get_tx(mslot_class); num_tx += 1) {
for (num_tx = 1; num_tx <= Tx; num_tx += 1) {
uint16_t tx_valid_win = (1 << num_tx) - 1;
uint8_t rx_mask[MASK_TR + 1];
mslot_fill_rx_mask(mslot_class, num_tx, rx_mask);
/* Rotate group of TX slots: UUU-----, -UUU----, ..., UU-----U */
for (ul_ts = 0; ul_ts < 8; ul_ts += 1, tx_valid_win <<= 1) {
uint16_t rx_valid_win;
uint32_t checked_rx[256/32] = {0};
/* Rotate group of TX slots: UUU-----, -UUU----, ..., UU-----U */
for (ul_ts = 0; ul_ts < 8; ul_ts += 1, tx_valid_win <<= 1) {
uint16_t rx_valid_win;
uint32_t checked_rx[256/32] = {0};
/* Wrap valid window */
tx_valid_win = mslot_wrap_window(tx_valid_win);
/* Wrap valid window */
tx_valid_win = mslot_wrap_window(tx_valid_win);
tx_window = tx_valid_win;
/* for multislot type 1: don't split the window to wrap around.
* E.g. 'UU-----U' is invalid for a 4 TN window. Except 8 TN window.
* See 45.002 B.1 */
if (Type == 1 && num_tx < 8 &&
tx_valid_win & (1 << 0) && tx_valid_win & (1 << 7))
continue;
/* Filter out unavailable slots */
tx_window &= *ul_slots;
tx_window = tx_valid_win;
/* Skip if the the first TS (ul_ts) is not in the set */
if ((tx_window & (1 << ul_ts)) == 0)
continue;
/* Filter out unavailable slots */
tx_window &= *ul_slots;
/* Skip if the the last TS (ul_ts+num_tx-1) is not in the set */
if ((tx_window & (1 << ((ul_ts+num_tx-1) % 8))) == 0)
continue;
/* Skip if the the first TS (ul_ts) is not in the set */
if ((tx_window & (1 << ul_ts)) == 0)
continue;
rx_valid_win = (1 << OSMO_MIN(mslot_class_get_rx(mslot_class), Sum - num_tx)) - 1;
/* Skip if the the last TS (ul_ts+num_tx-1) is not in the set */
if ((tx_window & (1 << ((ul_ts+num_tx-1) % 8))) == 0)
continue;
/* Rotate group of RX slots: DDD-----, -DDD----, ..., DD-----D */
for (dl_ts = 0; dl_ts < 8; dl_ts += 1, rx_valid_win <<= 1) {
/* Wrap valid window */
rx_valid_win = (rx_valid_win | rx_valid_win >> 8) & 0xff;
num_rx = OSMO_MIN(Rx, Sum - num_tx);
rx_valid_win = (1 << num_rx) - 1;
/* Validate with both Tta/Ttb/Trb and Ttb/Tra/Trb */
for (mask_sel = MASK_TT; mask_sel <= MASK_TR; mask_sel += 1) {
int capacity;
/* Rotate group of RX slots: DDD-----, -DDD----, ..., DD-----D */
for (dl_ts = 0; dl_ts < 8; dl_ts += 1, rx_valid_win <<= 1) {
/* Wrap valid window */
rx_valid_win = (rx_valid_win | rx_valid_win >> 8) & 0xff;
rx_window = mslot_filter_bad(rx_mask[mask_sel], ul_ts, *dl_slots, rx_valid_win);
if (rx_window < 0)
continue;
/* for multislot type 1: don't split the window to wrap around.
* E.g. 'DD-----D' is invalid for a 4 TN window. Except 8 TN window.
* See 45.002 B.1 */
if (Type == 1 && num_rx < 8 &&
(rx_valid_win & (1 << 0)) && (rx_valid_win & (1 << 7)))
continue;
if (skip_slot(mslot_class, mask_sel != MASK_TT, rx_window, tx_window, checked_rx))
continue;
/* Validate with both Tta/Ttb/Trb and Ttb/Tra/Trb */
for (mask_sel = MASK_TT; mask_sel <= MASK_TR; mask_sel += 1) {
int capacity;
/* Compute capacity */
capacity = compute_capacity(trx, rx_window, tx_window);
rx_window = mslot_filter_bad(rx_mask[mask_sel], ul_ts, *dl_slots, rx_valid_win);
if (rx_window < 0)
continue;
if (skip_slot(mslot_class, mask_sel != MASK_TT, rx_window, tx_window, checked_rx))
continue;
/* Compute capacity */
capacity = compute_capacity(trx, rx_window, tx_window);
#ifdef ENABLE_TS_ALLOC_DEBUG
LOGP(DRLCMAC, LOGL_DEBUG,
"- Considering DL/UL slots: (TS=0)\"%s\"(TS=7), "
"capacity = %d\n",
set_flag_chars(set_flag_chars(set_flag_chars(set_flag_chars(
slot_info,
rx_bad, 'x', '.'),
rx_window, 'D'),
tx_window, 'U'),
rx_window & tx_window, 'C'),
capacity);
LOGP(DRLCMAC, LOGL_DEBUG,
"- Considering DL/UL slots: (TS=0)\"%s\"(TS=7), "
"capacity = %d\n",
set_flag_chars(set_flag_chars(set_flag_chars(set_flag_chars(
slot_info,
rx_bad, 'x', '.'),
rx_window, 'D'),
tx_window, 'U'),
rx_window & tx_window, 'C'),
capacity);
#endif
if (capacity <= max_capacity)
continue;
if (capacity <= max_capacity)
continue;
max_capacity = capacity;
max_ul_slots = tx_window;
max_dl_slots = rx_window;
}
}
}
max_capacity = capacity;
max_ul_slots = tx_window;
max_dl_slots = rx_window;
}
}
}
}
if (!max_ul_slots || !max_dl_slots) {
LOGP(DRLCMAC, LOGL_NOTICE,
"No valid UL/DL slot combination found\n");
bts_do_rate_ctr_inc(trx->bts, CTR_TBF_ALLOC_FAIL_NO_SLOT_COMBI);
return -EINVAL;
}
@@ -640,12 +659,12 @@ int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *
* \param[in] slots Timeslots in use
* \param[in] reserved_slots Reserved timeslots
* \param[out] slotcount Number of TS in use
* \param[out] avail_count Number of reserved TS
* \param[out] reserve_count Number of reserved TS
*/
static void update_slot_counters(uint8_t slots, uint8_t reserved_slots, uint8_t *slotcount, uint8_t *avail_count)
static void count_slots(uint8_t slots, uint8_t reserved_slots, uint8_t *slotcount, uint8_t *reserve_count)
{
(*slotcount) = pcu_bitcount(slots);
(*avail_count) = pcu_bitcount(reserved_slots);
(*reserve_count) = pcu_bitcount(reserved_slots);
}
/*! Return slot mask with single TS from a given UL/DL set according to TBF's direction, ts pointer is set to that TS
@@ -689,7 +708,8 @@ static int tbf_select_slot_set(const gprs_rlcmac_tbf *tbf, const gprs_rlcmac_trx
uint8_t reserved_ul_slots, uint8_t reserved_dl_slots,
int8_t first_common_ts)
{
uint8_t sl = tbf->direction != GPRS_RLCMAC_DL_TBF ? ul_slots : dl_slots;
bool is_ul = tbf->direction == GPRS_RLCMAC_UL_TBF;
uint8_t sl = is_ul ? ul_slots : dl_slots;
char slot_info[9] = { 0 };
if (single)
@@ -697,59 +717,63 @@ static int tbf_select_slot_set(const gprs_rlcmac_tbf *tbf, const gprs_rlcmac_trx
if (!sl) {
LOGP(DRLCMAC, LOGL_NOTICE, "No %s slots available\n",
tbf->direction != GPRS_RLCMAC_DL_TBF ? "uplink" : "downlink");
is_ul ? "uplink" : "downlink");
bts_do_rate_ctr_inc(trx->bts, CTR_TBF_ALLOC_FAIL_NO_SLOT_AVAIL);
return -EINVAL;
}
if (tbf->direction != GPRS_RLCMAC_DL_TBF) {
if (is_ul) {
snprintf(slot_info, 9, OSMO_BIT_SPEC, OSMO_BIT_PRINT_EX(reserved_ul_slots, 'u'));
masked_override_with(slot_info, sl, 'U');
LOGPC(DRLCMAC, LOGL_DEBUG, "- Selected UL");
} else {
snprintf(slot_info, 9, OSMO_BIT_SPEC, OSMO_BIT_PRINT_EX(reserved_dl_slots, 'd'));
masked_override_with(slot_info, sl, 'D');
LOGPC(DRLCMAC, LOGL_DEBUG, "- Selected DL");
}
LOGPC(DRLCMAC, LOGL_DEBUG, " slots: (TS=0)\"%s\"(TS=7)%s\n", slot_info, single ? ", single" : "");
LOGPC(DRLCMAC, LOGL_DEBUG, "Selected %s slots: (TS=0)\"%s\"(TS=7), %s\n",
is_ul ? "UL" : "DL",
slot_info, single ? "single" : "multi");
return sl;
}
/*! Allocate USF according to a given UL TS mapping
*
* N. B: this is legacy implementation which ignores given selected_ul_slots
* \param[in] trx Pointer to TRX object
* \param[in] tbf Pointer to TBF object
* \param[in] first_common_ts First TS which is common to both UL and DL
* \param[in] selected_ul_slots set of UL timeslots selected for allocation
* \param[in] dl_slots set of DL timeslots
* \param[out] usf array for allocated USF
* \returns updated UL TS or negative on error
* \returns updated UL TS mask or negative on error
*/
static int allocate_usf(const gprs_rlcmac_trx *trx, int8_t first_common_ts, uint8_t selected_ul_slots, uint8_t dl_slots,
int *usf)
static int allocate_usf(const gprs_rlcmac_trx *trx, uint8_t selected_ul_slots, uint8_t dl_slots,
int *usf_list)
{
int free_usf = -1, ts;
uint8_t ul_slots = selected_ul_slots;
uint8_t ul_slots = selected_ul_slots & dl_slots;
unsigned int ts;
if (first_common_ts >= 0)
ul_slots = 1 << first_common_ts;
else
ul_slots = ul_slots & dl_slots;
for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
int8_t free_usf;
ts = find_least_busy_pdch(trx, GPRS_RLCMAC_UL_TBF, ul_slots, compute_usage_by_num_tbfs, NULL, &free_usf);
if (((1 << ts) & ul_slots) == 0)
continue;
if (free_usf < 0 || ts < 0) {
LOGP(DRLCMAC, LOGL_NOTICE, "No USF available\n");
return -EBUSY;
free_usf = find_free_usf(pdch->assigned_usf());
if (free_usf < 0) {
LOGP(DRLCMAC, LOGL_DEBUG,
"- Skipping TS %d, because "
"no USF available\n", ts);
ul_slots &= (~(1 << ts)) & 0xff;
continue;
}
usf_list[ts] = free_usf;
}
OSMO_ASSERT(ts >= 0 && ts <= 8);
/* We will stick to that single UL slot, unreserve the others */
ul_slots = 1 << ts;
usf[ts] = free_usf;
if (!ul_slots) {
LOGP(DRLCMAC, LOGL_NOTICE, "No USF available\n");
bts_do_rate_ctr_inc(trx->bts, CTR_TBF_ALLOC_FAIL_NO_USF);
return -EBUSY;
}
return ul_slots;
}
@@ -769,11 +793,11 @@ static void update_ms_reserved_slots(gprs_rlcmac_trx *trx, GprsMs *ms, uint8_t r
{
char slot_info[9] = { 0 };
if (res_ul_slots == ms->reserved_ul_slots() && res_dl_slots == ms->reserved_dl_slots())
if (res_ul_slots == ms_reserved_ul_slots(ms) && res_dl_slots == ms_reserved_dl_slots(ms))
return;
/* The reserved slots have changed, update the MS */
ms->set_reserved_slots(trx, res_ul_slots, res_dl_slots);
ms_set_reserved_slots(ms, trx, res_ul_slots, res_dl_slots);
ts_format(slot_info, dl_slots, ul_slots);
LOGP(DRLCMAC, LOGL_DEBUG, "- Reserved DL/UL slots: (TS=0)\"%s\"(TS=7)\n", slot_info);
@@ -829,13 +853,12 @@ static void assign_dl_tbf_slots(struct gprs_rlcmac_dl_tbf *dl_tbf, gprs_rlcmac_t
* Assign one uplink slot. (With free USF)
*
* \param[in,out] bts Pointer to BTS struct
* \param[in,out] ms_ Pointer to MS object
* \param[in,out] tbf_ Pointer to TBF struct
* \param[in,out] tbf Pointer to TBF struct
* \param[in] single flag indicating if we should force single-slot allocation
* \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
* \returns negative error code or 0 on success
*/
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcmac_tbf *tbf_, bool single,
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
int8_t use_trx)
{
uint8_t dl_slots;
@@ -844,46 +867,39 @@ int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcm
uint8_t reserved_ul_slots;
int8_t first_common_ts;
uint8_t slotcount = 0;
uint8_t avail_count = 0, trx_no;
uint8_t reserve_count = 0, trx_no;
int first_ts = -1;
int usf[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
int rc;
int tfi;
const GprsMs *ms = ms_;
const gprs_rlcmac_tbf *tbf = tbf_;
struct GprsMs *ms = tbf->ms();
gprs_rlcmac_trx *trx;
LOGPAL(tbf, "B", single, use_trx, LOGL_DEBUG, "Alloc start\n");
/* Step 1: Get current state from the MS object */
if (!ms) {
LOGP(DRLCMAC, LOGL_ERROR, "MS not set\n");
return -EINVAL;
}
dl_slots = ms->reserved_dl_slots();
ul_slots = ms->reserved_ul_slots();
first_common_ts = ms->first_common_ts();
trx = ms->current_trx();
reserved_dl_slots = ms_reserved_dl_slots(ms);
reserved_ul_slots = ms_reserved_ul_slots(ms);
first_common_ts = ms_first_common_ts(ms);
/* Step 2a: Find usable TRX and TFI */
tfi = tfi_find_free(bts->bts, trx, ms, tbf->direction, use_trx, &trx_no);
tfi = tfi_find_free(bts, ms, tbf->direction, use_trx, &trx_no);
if (tfi < 0) {
LOGP(DRLCMAC, LOGL_NOTICE, "- Failed to allocate a TFI\n");
LOGPAL(tbf, "B", single, use_trx, LOGL_NOTICE, "failed to allocate a TFI\n");
return tfi;
}
/* Step 2b: Reserve slots on the TRX for the MS */
if (!trx)
trx = &bts->trx[trx_no];
trx = &bts->trx[trx_no];
if (!dl_slots || !ul_slots) {
rc = find_multi_slots(trx, ms->ms_class(), &ul_slots, &dl_slots);
if (!reserved_dl_slots || !reserved_ul_slots) {
rc = find_multi_slots(trx, ms_ms_class(ms), &reserved_ul_slots, &reserved_dl_slots);
if (rc < 0)
return rc;
}
reserved_dl_slots = dl_slots;
reserved_ul_slots = ul_slots;
dl_slots = reserved_dl_slots;
ul_slots = reserved_ul_slots;
/* Step 3a: Derive the slot set for the current TBF */
rc = tbf_select_slot_set(tbf, trx, single, ul_slots, dl_slots, reserved_ul_slots, reserved_dl_slots,
@@ -891,63 +907,59 @@ int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcm
if (rc < 0)
return -EINVAL;
first_ts = ffs(rc) - 1;
/* Step 3b: Derive the slot set for a given direction */
if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
dl_slots = rc;
update_slot_counters(dl_slots, reserved_dl_slots, &slotcount, &avail_count);
count_slots(dl_slots, reserved_dl_slots, &slotcount, &reserve_count);
} else {
rc = allocate_usf(trx, first_common_ts, rc, dl_slots, usf);
rc = allocate_usf(trx, rc, dl_slots, usf);
if (rc < 0)
return rc;
/* We will stick to that single UL slot, unreserve the others */
ul_slots = rc;
reserved_ul_slots = ul_slots;
update_slot_counters(ul_slots, reserved_ul_slots, &slotcount, &avail_count);
count_slots(ul_slots, reserved_ul_slots, &slotcount, &reserve_count);
}
first_ts = ffs(rc) - 1;
first_common_ts = ffs(dl_slots & ul_slots) - 1;
if (first_common_ts < 0) {
LOGP(DRLCMAC, LOGL_NOTICE, "No first common slots available\n");
LOGPAL(tbf, "B", single, use_trx, LOGL_NOTICE, "first common slot unavailable\n");
return -EINVAL;
}
if (first_ts < 0) {
LOGP(DRLCMAC, LOGL_NOTICE, "No first slot available\n");
LOGPAL(tbf, "B", single, use_trx, LOGL_NOTICE, "first slot unavailable\n");
return -EINVAL;
}
if (single && slotcount) {
tbf_->upgrade_to_multislot = (avail_count > slotcount);
LOGP(DRLCMAC, LOGL_INFO, "Using single slot at TS %d for %s\n",
first_ts,
(tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL");
tbf->upgrade_to_multislot = (reserve_count > slotcount);
LOGPAL(tbf, "B", single, use_trx, LOGL_INFO, "using single slot at TS %d\n", first_ts);
} else {
tbf_->upgrade_to_multislot = 0;
LOGP(DRLCMAC, LOGL_INFO, "Using %d slots for %s\n", slotcount,
(tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL");
tbf->upgrade_to_multislot = false;
LOGPAL(tbf, "B", single, use_trx, LOGL_INFO, "using %d slots\n", slotcount);
}
/* The allocation will be successful, so the system state and tbf_/ms_
/* The allocation will be successful, so the system state and tbf/ms
* may be modified from now on. */
/* Step 4: Update MS and TBF and really allocate the resources */
update_ms_reserved_slots(trx, ms_, reserved_ul_slots, reserved_dl_slots, ul_slots, dl_slots);
update_ms_reserved_slots(trx, ms, reserved_ul_slots, reserved_dl_slots, ul_slots, dl_slots);
tbf_->trx = trx;
tbf_->first_common_ts = first_common_ts;
tbf_->first_ts = first_ts;
tbf->trx = trx;
tbf->first_common_ts = first_common_ts;
tbf->first_ts = first_ts;
if (tbf->direction == GPRS_RLCMAC_DL_TBF)
assign_dl_tbf_slots(as_dl_tbf(tbf_), trx, dl_slots, tfi);
assign_dl_tbf_slots(as_dl_tbf(tbf), trx, dl_slots, tfi);
else
assign_ul_tbf_slots(as_ul_tbf(tbf_), trx, ul_slots, tfi, usf);
assign_ul_tbf_slots(as_ul_tbf(tbf), trx, ul_slots, tfi, usf);
bts->bts->tbf_alloc_algo_b();
bts_do_rate_ctr_inc(bts, CTR_TBF_ALLOC_ALGO_B);
return 0;
}
@@ -961,13 +973,12 @@ int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcm
* goal is to provide the highest possible bandwidth per MS.
*
* \param[in,out] bts Pointer to BTS struct
* \param[in,out] ms_ Pointer to MS object
* \param[in,out] tbf_ Pointer to TBF struct
* \param[in,out] tbf Pointer to TBF struct
* \param[in] single flag indicating if we should force single-slot allocation
* \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
* \returns negative error code or 0 on success
*/
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcmac_tbf *tbf_, bool single,
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
int8_t use_trx)
{
int rc;
@@ -980,7 +991,7 @@ int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gpr
}
if (!bts->multislot_disabled) {
rc = alloc_algorithm_b(bts, ms_, tbf_, single, use_trx);
rc = alloc_algorithm_b(bts, tbf, single, use_trx);
if (rc >= 0)
return rc;
@@ -989,7 +1000,7 @@ int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gpr
bts->multislot_disabled = 1;
}
return alloc_algorithm_a(bts, ms_, tbf_, single, use_trx);
return alloc_algorithm_a(bts, tbf, single, use_trx);
}
int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms_class)
@@ -999,7 +1010,7 @@ int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms
if (rx == MS_NA)
rx = 4;
if (bts->alloc_algorithm == alloc_algorithm_a)
if (the_pcu->alloc_algorithm == alloc_algorithm_a)
return 1;
if (bts->multislot_disabled)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,238 +0,0 @@
/* gsm_timer.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.
*/
/* These store the amount of frame number that we wait until next timer expires. */
static int nearest;
static int *nearest_p;
/*! \addtogroup gsm_timer
* @{
*/
/*! \file gsm_timer.cpp
*/
#include <assert.h>
#include <string.h>
#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.
*/
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);
struct rb_node *parent = NULL;
while (*new_node) {
struct osmo_gsm_timer_list *this_timer;
this_timer = container_of(*new_node, struct osmo_gsm_timer_list, node);
parent = *new_node;
if (timer->fn < this_timer->fn)
new_node = &((*new_node)->rb_left);
else
new_node = &((*new_node)->rb_right);
}
rb_link_node(&timer->node, parent, new_node);
rb_insert_color(&timer->node, &timer_root);
}
/*! \brief add a new timer to the timer management
* \param[in] timer the timer that should be added
*/
void osmo_gsm_timer_add(struct osmo_gsm_timer_list *timer)
{
osmo_gsm_timer_del(timer);
timer->active = 1;
INIT_LLIST_HEAD(&timer->list);
__add_gsm_timer(timer);
}
/*! \brief schedule a gsm timer at a given future relative time
* \param[in] timer the to-be-added timer
* \param[in] number of frames from now
*
* This function can be used to (re-)schedule a given timer at a
* specified number of frames in the future. It will
* internally add it to the timer management data structures, thus
* osmo_timer_add() is automatically called.
*/
void
osmo_gsm_timer_schedule(struct osmo_gsm_timer_list *timer, int fn)
{
int current_fn;
current_fn = get_current_fn();
timer->fn = current_fn + fn;
osmo_gsm_timer_add(timer);
}
/*! \brief delete a gsm timer from timer management
* \param[in] timer the to-be-deleted timer
*
* This function can be used to delete a previously added/scheduled
* timer from the timer management code.
*/
void osmo_gsm_timer_del(struct osmo_gsm_timer_list *timer)
{
if (timer->active) {
timer->active = 0;
rb_erase(&timer->node, &timer_root);
/* make sure this is not already scheduled for removal. */
if (!llist_empty(&timer->list))
llist_del_init(&timer->list);
}
}
/*! \brief check if given timer is still pending
* \param[in] timer the to-be-checked timer
* \return 1 if pending, 0 otherwise
*
* This function can be used to determine whether a given timer
* has alredy expired (returns 0) or is still pending (returns 1)
*/
int osmo_gsm_timer_pending(struct osmo_gsm_timer_list *timer)
{
return timer->active;
}
/*
* if we have a nearest frame number return the delta between the current
* FN and the FN of the nearest timer.
* If the nearest timer timed out return NULL and then we will
* dispatch everything after the select
*/
int *osmo_gsm_timers_nearest(void)
{
/* nearest_p is exactly what we need already: NULL if nothing is
* waiting, {0,0} if we must dispatch immediately, and the correct
* delay if we need to wait */
return nearest_p;
}
static void update_nearest(int *cand, int *current)
{
if (*cand != LONG_MAX) {
if (*cand > *current)
nearest = *cand - *current;
else {
/* loop again inmediately */
nearest = 0;
}
nearest_p = &nearest;
} else {
nearest_p = NULL;
}
}
/*
* Find the nearest FN and update s_nearest_time
*/
void osmo_gsm_timers_prepare(void)
{
struct rb_node *node;
int current_fn;
current_fn = get_current_fn();
node = rb_first(&timer_root);
if (node) {
struct osmo_gsm_timer_list *this_timer;
this_timer = container_of(node, struct osmo_gsm_timer_list, node);
update_nearest(&this_timer->fn, &current_fn);
} else {
nearest_p = NULL;
}
}
/*
* fire all timers... and remove them
*/
int osmo_gsm_timers_update(void)
{
int current_fn;
struct rb_node *node;
struct llist_head timer_eviction_list;
struct osmo_gsm_timer_list *this_timer;
int work = 0;
current_fn = get_current_fn();
INIT_LLIST_HEAD(&timer_eviction_list);
for (node = rb_first(&timer_root); node; node = rb_next(node)) {
this_timer = container_of(node, struct osmo_gsm_timer_list, node);
if (this_timer->fn > current_fn)
break;
llist_add(&this_timer->list, &timer_eviction_list);
}
/*
* The callbacks might mess with our list and in this case
* even llist_for_each_entry_safe is not safe to use. To allow
* osmo_gsm_timer_del to be called from within the callback we need
* to restart the iteration for each element scheduled for removal.
*
* The problematic scenario is the following: Given two timers A
* and B that have expired at the same time. Thus, they are both
* in the eviction list in this order: A, then B. If we remove
* timer B from the A's callback, we continue with B in the next
* iteration step, leading to an access-after-release.
*/
restart:
llist_for_each_entry(this_timer, &timer_eviction_list, list) {
osmo_gsm_timer_del(this_timer);
this_timer->cb(this_timer->data);
work = 1;
goto restart;
}
return work;
}
int osmo_gsm_timers_check(void)
{
struct rb_node *node;
int i = 0;
for (node = rb_first(&timer_root); node; node = rb_next(node)) {
i++;
}
return i;
}
/*! }@ */

View File

@@ -1,90 +0,0 @@
/* gsm_timer.h
*
* Copyright (C) 2012 Ivan Klyuchnikov
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*! \defgroup timer GSM timers
* @{
*/
/*! \file gsm_timer.h
* \brief GSM timer handling routines
*/
#ifndef GSM_TIMER_H
#define GSM_TIMER_H
extern "C" {
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/linuxrbtree.h>
}
/**
* Timer management:
* - Create a struct osmo_gsm_timer_list
* - Fill out timeout and use add_gsm_timer or
* use schedule_gsm_timer to schedule a timer in
* x frames from now...
* - Use del_gsm_timer to remove the timer
*
* Internally:
* - We hook into select.c to give a frame number of the
* nearest timer. On already passed timers we give
* it a 0 to immediately fire after the select.
* - update_gsm_timers will call the callbacks and remove
* the timers.
*
*/
/*! \brief A structure representing a single instance of a gsm timer */
struct osmo_gsm_timer_list {
struct rb_node node; /*!< \brief rb-tree node header */
struct llist_head list; /*!< \brief internal list header */
int fn; /*!< \brief expiration frame number */
unsigned int active : 1; /*!< \brief is it active? */
void (*cb)(void*); /*!< \brief call-back called at timeout */
void *data; /*!< \brief user data for callback */
};
/**
* timer management
*/
void osmo_gsm_timer_add(struct osmo_gsm_timer_list *timer);
void osmo_gsm_timer_schedule(struct osmo_gsm_timer_list *timer, int fn);
void osmo_gsm_timer_del(struct osmo_gsm_timer_list *timer);
int osmo_gsm_timer_pending(struct osmo_gsm_timer_list *timer);
/*
* internal timer list management
*/
int *osmo_gsm_timers_nearest(void);
void osmo_gsm_timers_prepare(void);
int osmo_gsm_timers_update(void);
int osmo_gsm_timers_check(void);
/*
* Get Current Frame Number
*/
int get_current_fn();
/*! }@ */
#endif // GSM_TIMER_H

View File

@@ -27,6 +27,8 @@ extern "C" {
#include <osmocom/core/msgb.h>
}
#include "pcu_utils.h"
/* reset LLC frame */
void gprs_llc::reset()
{
@@ -57,8 +59,10 @@ void gprs_llc::put_dummy_frame(size_t req_len)
/* Add further stuffing, if the requested length exceeds the minimum
* dummy command length */
while (m_length < req_len)
frame[m_length++] = 0x2b;
if (m_length < req_len) {
memset(&frame[m_length], 0x2b, req_len - m_length);
m_length = req_len;
}
}
void gprs_llc::put_frame(const uint8_t *data, size_t len)
@@ -95,45 +99,46 @@ bool gprs_llc::is_user_data_frame(uint8_t *data, size_t len)
return true;
}
void gprs_llc_queue::init()
void llc_queue_init(struct gprs_llc_queue *q)
{
INIT_LLIST_HEAD(&m_queue);
m_queue_size = 0;
m_queue_octets = 0;
m_avg_queue_delay = 0;
INIT_LLIST_HEAD(&q->m_queue);
q->m_queue_size = 0;
q->m_queue_octets = 0;
q->m_avg_queue_delay = 0;
}
void gprs_llc_queue::enqueue(struct msgb *llc_msg, const MetaInfo *info)
void gprs_llc_queue::enqueue(struct msgb *llc_msg, const struct timespec *expire_time)
{
static const MetaInfo def_meta = {{0}};
MetaInfo *meta_storage;
osmo_static_assert(sizeof(*info) <= sizeof(llc_msg->cb), info_does_not_fit);
osmo_static_assert(sizeof(*meta_storage) <= 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;
osmo_clock_gettime(CLOCK_MONOTONIC, &meta_storage->recv_time);
meta_storage->expire_time = *expire_time;
msgb_enqueue(&m_queue, llc_msg);
}
void gprs_llc_queue::clear(BTS *bts)
void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts)
{
struct msgb *msg;
while ((msg = msgb_dequeue(&m_queue))) {
while ((msg = msgb_dequeue(&q->m_queue))) {
if (bts)
bts->llc_dropped_frame();
bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
msgb_free(msg);
}
m_queue_size = 0;
m_queue_octets = 0;
q->m_queue_size = 0;
q->m_queue_octets = 0;
}
void gprs_llc_queue::move_and_merge(gprs_llc_queue *o)
void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o)
{
struct msgb *msg, *msg1 = NULL, *msg2 = NULL;
struct llist_head new_queue;
@@ -143,7 +148,7 @@ void gprs_llc_queue::move_and_merge(gprs_llc_queue *o)
while (1) {
if (msg1 == NULL)
msg1 = msgb_dequeue(&m_queue);
msg1 = msgb_dequeue(&q->m_queue);
if (msg2 == NULL)
msg2 = msgb_dequeue(&o->m_queue);
@@ -161,7 +166,7 @@ void gprs_llc_queue::move_and_merge(gprs_llc_queue *o)
const MetaInfo *mi1 = (MetaInfo *)&msg1->cb[0];
const MetaInfo *mi2 = (MetaInfo *)&msg2->cb[0];
if (timercmp(&mi2->recv_time, &mi1->recv_time, >)) {
if (timespeccmp(&mi2->recv_time, &mi1->recv_time, >)) {
msg = msg1;
msg1 = NULL;
} else {
@@ -175,15 +180,15 @@ void gprs_llc_queue::move_and_merge(gprs_llc_queue *o)
queue_octets += msgb_length(msg);
}
OSMO_ASSERT(llist_empty(&m_queue));
OSMO_ASSERT(llist_empty(&q->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;
llist_splice_init(&new_queue, &q->m_queue);
q->m_queue_size = queue_size;
q->m_queue_octets = queue_octets;
}
#define ALPHA 0.5f
@@ -191,7 +196,7 @@ void gprs_llc_queue::move_and_merge(gprs_llc_queue *o)
struct msgb *gprs_llc_queue::dequeue(const MetaInfo **info)
{
struct msgb *msg;
struct timeval *tv, tv_now, tv_result;
struct timespec *tv, tv_now, tv_result;
uint32_t lifetime;
const MetaInfo *meta_storage;
@@ -208,21 +213,21 @@ struct msgb *gprs_llc_queue::dequeue(const MetaInfo **info)
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);
osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
tv = (struct timespec *)&msg->data[sizeof(*tv)];
timespecsub(&tv_now, &meta_storage->recv_time, &tv_result);
lifetime = tv_result.tv_sec*1000 + tv_result.tv_usec/1000;
lifetime = tv_result.tv_sec*1000 + tv_result.tv_nsec/1000000;
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)
void gprs_llc_queue::calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec, struct timespec *tv)
{
uint16_t delay_csec;
if (bts->bts_data()->force_llc_lifetime)
delay_csec = bts->bts_data()->force_llc_lifetime;
if (bts->pcu->vty.force_llc_lifetime)
delay_csec = bts->pcu->vty.force_llc_lifetime;
else
delay_csec = pdu_delay_csec;
@@ -233,20 +238,19 @@ void gprs_llc_queue::calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec,
}
/* calculate timestamp of timeout */
struct timeval now, csec;
gettimeofday(&now, NULL);
csec.tv_usec = (delay_csec % 100) * 10000;
csec.tv_sec = delay_csec / 100;
struct timespec now, csec;
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
csecs_to_timespec(delay_csec, &csec);
timeradd(&now, &csec, tv);
timespecadd(&now, &csec, tv);
}
bool gprs_llc_queue::is_frame_expired(const struct timeval *tv_now,
const struct timeval *tv)
bool gprs_llc_queue::is_frame_expired(const struct timespec *tv_now,
const struct timespec *tv)
{
/* Timeout is infinite */
if (tv->tv_sec == 0 && tv->tv_usec == 0)
if (tv->tv_sec == 0 && tv->tv_nsec == 0)
return false;
return timercmp(tv_now, tv, >);
return timespeccmp(tv_now, tv, >);
}

View File

@@ -18,22 +18,28 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <osmocom/core/linuxlist.h>
#ifdef __cplusplus
}
#endif
#include <stdint.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#define LLC_MAX_LEN 1543
struct BTS;
struct gprs_rlcmac_bts;
/**
* I represent the LLC data to a MS
*/
struct gprs_llc {
#ifdef __cplusplus
static bool is_user_data_frame(uint8_t *data, size_t len);
void init();
@@ -43,92 +49,86 @@ struct gprs_llc {
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;
#endif
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 */
};
struct MetaInfo {
struct timespec recv_time;
struct timespec expire_time;
};
/**
* 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);
#ifdef __cplusplus
static void calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec,
struct timespec *tv);
static bool is_frame_expired(const struct timespec *now,
const struct timespec *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);
void enqueue(struct msgb *llc_msg, const struct timespec *expire_time);
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:
#endif
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 */
};
#ifdef __cplusplus
extern "C" {
#endif
void llc_queue_init(struct gprs_llc_queue *q);
void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts);
void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o);
inline uint16_t gprs_llc::chunk_size() const
static inline uint16_t llc_chunk_size(const struct gprs_llc *llc)
{
return m_length - m_index;
return llc->m_length - llc->m_index;
}
inline uint16_t gprs_llc::remaining_space() const
static inline uint16_t llc_remaining_space(const struct gprs_llc *llc)
{
return LLC_MAX_LEN - m_length;
return LLC_MAX_LEN - llc->m_length;
}
inline uint16_t gprs_llc::frame_length() const
static inline uint16_t llc_frame_length(const struct gprs_llc *llc)
{
return m_length;
return llc->m_length;
}
inline void gprs_llc::consume(size_t len)
static inline void llc_consume(struct gprs_llc *llc, size_t len)
{
m_index += len;
llc->m_index += len;
}
inline void gprs_llc::consume(uint8_t *data, size_t len)
static inline void llc_consume_data(struct gprs_llc *llc, uint8_t *data, size_t len)
{
/* copy and increment index */
memcpy(data, frame + m_index, len);
consume(len);
memcpy(data, llc->frame + llc->m_index, len);
llc_consume(llc, len);
}
inline bool gprs_llc::fits_in_current_frame(uint8_t chunk_size) const
static inline bool llc_fits_in_current_frame(const struct gprs_llc *llc, uint8_t chunk_size)
{
return m_length + chunk_size <= LLC_MAX_LEN;
return llc->m_length + chunk_size <= LLC_MAX_LEN;
}
inline size_t gprs_llc_queue::size() const
static inline size_t llc_queue_size(const struct gprs_llc_queue *q)
{
return m_queue_size;
return q->m_queue_size;
}
inline size_t gprs_llc_queue::octets() const
static inline size_t llc_queue_octets(const struct gprs_llc_queue *q)
{
return m_queue_octets;
return q->m_queue_octets;
}
#ifdef __cplusplus
}
#endif

904
src/nacc_fsm.c Normal file
View File

@@ -0,0 +1,904 @@
/* nacc_fsm.c
*
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@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 <unistd.h>
#include <talloc.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/ctrl/control_cmd.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_bssgp_rim.h>
#include <nacc_fsm.h>
#include <gprs_rlcmac.h>
#include <gprs_debug.h>
#include <gprs_ms.h>
#include <encoding.h>
#include <bts.h>
#include <neigh_cache.h>
#define X(s) (1 << (s))
/* Infer CTRL id (seqnum) for a given tgt arfcn+bsic (bsic range: 0-63) */
#define arfcn_bsic_2_ctrl_id(arfcn, bsic) ((arfcn) * 100 + (bsic))
static const struct osmo_tdef_state_timeout nacc_fsm_timeouts[32] = {
[NACC_ST_INITIAL] = {},
[NACC_ST_WAIT_RESOLVE_RAC_CI] = { .T = PCU_TDEF_NEIGH_RESOLVE_TO },
[NACC_ST_WAIT_REQUEST_SI] = { .T = PCU_TDEF_SI_RESOLVE_TO },
[NACC_ST_TX_NEIGHBOUR_DATA] = {},
[NACC_ST_TX_CELL_CHG_CONTINUE] = {},
[NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK] = {}, /* Timeout through event controlled by tbf::poll_timeout() */
[NACC_ST_DONE] = {},
};
/* Transition to a state, using the T timer defined in nacc_fsm_timeouts.
* The actual timeout value is in turn obtained from conn->T_defs.
* Assumes local variable fi exists. */
#define nacc_fsm_state_chg(fi, NEXT_STATE) \
osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
nacc_fsm_timeouts, \
((struct nacc_fsm_ctx*)(fi->priv))->ms->bts->pcu->T_defs, \
-1)
const struct value_string nacc_fsm_event_names[] = {
{ NACC_EV_RX_CELL_CHG_NOTIFICATION, "RX_CELL_CHG_NOTIFICATION" },
{ NACC_EV_RX_RAC_CI, "RX_RAC_CI" },
{ NACC_EV_RX_SI, "RX_SI" },
{ NACC_EV_CREATE_RLCMAC_MSG, "CREATE_RLCMAC_MSG" },
{ NACC_EV_RX_CELL_CHG_CONTINUE_ACK, "RX_CELL_CHG_CONTINUE_ACK"},
{ NACC_EV_TIMEOUT_CELL_CHG_CONTINUE, "TIMEOUT_CELL_CHG_CONTINUE" },
{ 0, NULL }
};
/* TS 44 060 11.2.9e Packet Neighbour Cell Data */
static struct msgb *create_packet_neighbour_cell_data(struct nacc_fsm_ctx *ctx,
const struct gprs_rlcmac_tbf *tbf,
bool *all_si_info_sent)
{
struct msgb *msg;
int rc;
RlcMacDownlink_t *mac_control_block;
struct GprsMs *ms = tbf_ms(tbf);
OSMO_ASSERT(tbf_is_tfi_assigned(tbf));
uint8_t tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF;
uint8_t tfi = tbf_tfi(tbf);
uint8_t container_id = 0;
PNCDContainer_t container;
size_t max_len, len_to_write;
uint8_t *cont_buf;
uint8_t si_type = ctx->si_info.type_psi ? 0x01 : 0x0;
memset(&container, 0, sizeof(container));
if (ctx->container_idx == 0) {
container.UnionType = 1; /* with ID */
container.u.PNCD_Container_With_ID.ARFCN = ctx->neigh_key.tgt_arfcn;
container.u.PNCD_Container_With_ID.BSIC = ctx->neigh_key.tgt_bsic;
cont_buf = &container.u.PNCD_Container_With_ID.CONTAINER[0];
max_len = sizeof(container.u.PNCD_Container_With_ID.CONTAINER) - 1;
} else {
container.UnionType = 0; /* without ID */
cont_buf = &container.u.PNCD_Container_Without_ID.CONTAINER[0];
max_len = sizeof(container.u.PNCD_Container_Without_ID.CONTAINER) - 1;
}
len_to_write = ctx->si_info.si_len - ctx->si_info_bytes_sent;
if (len_to_write == 0) {
/* We sent all info on last message filing it exactly, we now send a zeroed one to finish */
*all_si_info_sent = true;
*cont_buf = (si_type << 5) | 0x00;
} else if (len_to_write >= max_len) {
/* We fill the rlcmac block, we'll need more messages */
*all_si_info_sent = false;
*cont_buf = (si_type << 5) | 0x1F;
memcpy(cont_buf + 1, &ctx->si_info.si_buf[ctx->si_info_bytes_sent], max_len);
ctx->si_info_bytes_sent += max_len;
} else {
/* Last block, we don't fill it exactly */
*all_si_info_sent = true;
*cont_buf = (si_type << 5) | (len_to_write & 0x1F);
memcpy(cont_buf + 1, &ctx->si_info.si_buf[ctx->si_info_bytes_sent], len_to_write);
ctx->si_info_bytes_sent += len_to_write;
}
msg = msgb_alloc(GSM_MACBLOCK_LEN, "neighbour_cell_data");
if (!msg)
return NULL;
/* Initialize a bit vector that uses allocated msgb as the data buffer. */
struct bitvec bv = {
.data = msgb_put(msg, GSM_MACBLOCK_LEN),
.data_len = GSM_MACBLOCK_LEN,
};
bitvec_unhex(&bv, DUMMY_VEC);
mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->ms, RlcMacDownlink_t);
write_packet_neighbour_cell_data(mac_control_block,
tfi_is_dl, tfi, container_id,
ctx->container_idx, &container);
LOGP(DNACC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Neighbour Cell Data +++++++++++++++++++++++++\n");
rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block);
if (rc < 0) {
LOGP(DTBF, LOGL_ERROR, "Encoding of Packet Neighbour Cell Data failed (%d)\n", rc);
goto free_ret;
}
LOGP(DNACC, LOGL_DEBUG, "------------------------- TX : Packet Neighbour Cell Data -------------------------\n");
rate_ctr_inc(rate_ctr_group_get_ctr(bts_rate_counters(ms->bts), CTR_PKT_NEIGH_CELL_DATA));
talloc_free(mac_control_block);
ctx->container_idx++;
return msg;
free_ret:
talloc_free(mac_control_block);
msgb_free(msg);
return NULL;
}
/* TS 44 060 11.2.2a Packet Cell Change Continue */
static struct msgb *create_packet_cell_chg_continue(const struct nacc_fsm_ctx *ctx,
const struct nacc_ev_create_rlcmac_msg_ctx *data,
uint32_t *new_poll_fn)
{
struct msgb *msg;
int rc;
RlcMacDownlink_t *mac_control_block;
struct gprs_rlcmac_tbf *tbf = data->tbf;
struct GprsMs *ms = tbf_ms(tbf);
unsigned int rrbp;
rc = tbf_check_polling(tbf, data->fn, data->ts, new_poll_fn, &rrbp);
if (rc < 0) {
LOGP(DTBF, LOGL_ERROR, "Failed registering poll for Pkt Cell Chg Continue (%d)\n", rc);
return NULL;
}
msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_cell_chg_continue");
if (!msg)
return NULL;
/* Initialize a bit vector that uses allocated msgb as the data buffer. */
struct bitvec bv = {
.data = msgb_put(msg, GSM_MACBLOCK_LEN),
.data_len = GSM_MACBLOCK_LEN,
};
bitvec_unhex(&bv, DUMMY_VEC);
mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->ms, RlcMacDownlink_t);
OSMO_ASSERT(tbf_is_tfi_assigned(tbf));
uint8_t tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF;
uint8_t tfi = tbf_tfi(tbf);
uint8_t container_id = 0;
write_packet_cell_change_continue(mac_control_block, 1, rrbp, tfi_is_dl, tfi, true,
ctx->neigh_key.tgt_arfcn, ctx->neigh_key.tgt_bsic, container_id);
LOGP(DNACC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Cell Change Continue +++++++++++++++++++++++++\n");
rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block);
if (rc < 0) {
LOGP(DTBF, LOGL_ERROR, "Encoding of Packet Cell Change Continue failed (%d)\n", rc);
goto free_ret;
}
LOGP(DNACC, LOGL_DEBUG, "------------------------- TX : Packet Cell Change Continue -------------------------\n");
rate_ctr_inc(rate_ctr_group_get_ctr(bts_rate_counters(ms->bts), CTR_PKT_CELL_CHG_CONTINUE));
talloc_free(mac_control_block);
tbf_set_polling(tbf, *new_poll_fn, data->ts, PDCH_ULC_POLL_CELL_CHG_CONTINUE);
LOGPTBFDL(tbf, LOGL_DEBUG, "Scheduled 'Packet Cell Change Continue' polling on PACCH (FN=%d, TS=%d)\n",
*new_poll_fn, data->ts);
return msg;
free_ret:
talloc_free(mac_control_block);
msgb_free(msg);
return NULL;
}
static int fill_rim_ran_info_req(const struct nacc_fsm_ctx *ctx, struct bssgp_ran_information_pdu *pdu)
{
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
*pdu = (struct bssgp_ran_information_pdu){
.routing_info_dest = {
.discr = BSSGP_RIM_ROUTING_INFO_GERAN,
.geran = {
.raid = {
.mcc = ctx->cgi_ps.rai.lac.plmn.mcc,
.mnc = ctx->cgi_ps.rai.lac.plmn.mnc,
.mnc_3_digits = ctx->cgi_ps.rai.lac.plmn.mnc_3_digits,
.lac = ctx->cgi_ps.rai.lac.lac,
.rac = ctx->cgi_ps.rai.rac,
},
.cid = ctx->cgi_ps.cell_identity,
},
},
.routing_info_src = {
.discr = BSSGP_RIM_ROUTING_INFO_GERAN,
.geran = {
.raid = {
.mcc = bts->cgi_ps.rai.lac.plmn.mcc,
.mnc = bts->cgi_ps.rai.lac.plmn.mnc,
.mnc_3_digits = bts->cgi_ps.rai.lac.plmn.mnc_3_digits,
.lac = bts->cgi_ps.rai.lac.lac,
.rac = bts->cgi_ps.rai.rac,
},
.cid = bts->cgi_ps.cell_identity,
},
},
.rim_cont_iei = BSSGP_IE_RI_REQ_RIM_CONTAINER,
.decoded_present = true,
.decoded = {
.req_rim_cont = {
.app_id = BSSGP_RAN_INF_APP_ID_NACC,
.seq_num = 1,
.pdu_ind = {
.ack_requested = 0,
.pdu_type_ext = RIM_PDU_TYPE_SING_REP,
},
.prot_ver = 1,
.son_trans_app_id = NULL,
.son_trans_app_id_len = 0,
.u = {
.app_cont_nacc = {
.reprt_cell = ctx->cgi_ps,
},
},
},
},
};
return 0;
}
static int fill_neigh_key_from_bts_pkt_cell_chg_not(struct neigh_cache_entry_key *neigh_key,
const struct gprs_rlcmac_bts *bts,
const Packet_Cell_Change_Notification_t *notif)
{
switch (notif->Target_Cell.UnionType) {
case 0: /* GSM */
neigh_key->local_lac = bts->cgi_ps.rai.lac.lac;
neigh_key->local_ci = bts->cgi_ps.cell_identity;
neigh_key->tgt_arfcn = notif->Target_Cell.u.Target_Cell_GSM_Notif.ARFCN;
neigh_key->tgt_bsic = notif->Target_Cell.u.Target_Cell_GSM_Notif.BSIC;
return 0;
default:
return -ENOTSUP;
}
}
#define SI_HDR_LEN 2
static void bts_fill_si_cache_value(const struct gprs_rlcmac_bts *bts, struct si_cache_value *val)
{
val->type_psi = false;
val->si_len = 0;
if (bts->si1_is_set) {
osmo_static_assert(sizeof(bts->si1) - SI_HDR_LEN == BSSGP_RIM_SI_LEN, _si1_header_size);
memcpy(&val->si_buf[val->si_len], bts->si1 + SI_HDR_LEN, BSSGP_RIM_SI_LEN);
val->si_len += BSSGP_RIM_SI_LEN;
}
if (bts->si3_is_set) {
osmo_static_assert(sizeof(bts->si3) - SI_HDR_LEN == BSSGP_RIM_SI_LEN, _si3_header_size);
memcpy(&val->si_buf[val->si_len], bts->si3 + SI_HDR_LEN, BSSGP_RIM_SI_LEN);
val->si_len += BSSGP_RIM_SI_LEN;
}
if (bts->si13_is_set) {
osmo_static_assert(sizeof(bts->si13) - SI_HDR_LEN == BSSGP_RIM_SI_LEN, _si13_header_size);
memcpy(&val->si_buf[val->si_len], bts->si13 + SI_HDR_LEN, BSSGP_RIM_SI_LEN);
val->si_len += BSSGP_RIM_SI_LEN;
}
}
/* Called on event NACC_EV_RX_CELL_CHG_NOTIFICATION on states after
* WAIT_RESOLVE_RAC_CI. Ignore duplicate messages, transition back if target
* cell changed.
*/
static void handle_retrans_pkt_cell_chg_notif(struct nacc_fsm_ctx *ctx, const Packet_Cell_Change_Notification_t *notif)
{
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
struct neigh_cache_entry_key neigh_key;
if (fill_neigh_key_from_bts_pkt_cell_chg_not(&neigh_key, bts, notif) < 0) {
LOGPFSML(ctx->fi, LOGL_NOTICE, "TargetCell type=0x%x not supported\n",
notif->Target_Cell.UnionType);
if (ctx->fi->state != NACC_ST_TX_CELL_CHG_CONTINUE)
nacc_fsm_state_chg(ctx->fi, NACC_ST_TX_CELL_CHG_CONTINUE);
return;
}
/* If tgt cell changed, restart resolving it */
if (!neigh_cache_entry_key_eq(&ctx->neigh_key, &neigh_key)) {
ctx->neigh_key = neigh_key;
nacc_fsm_state_chg(ctx->fi, NACC_ST_WAIT_RESOLVE_RAC_CI);
}
/* else: ignore it, it's a dup, carry on what we were doing */
}
////////////////
// FSM states //
////////////////
static void st_initial(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
Packet_Cell_Change_Notification_t *notif;
switch (event) {
case NACC_EV_RX_CELL_CHG_NOTIFICATION:
notif = (Packet_Cell_Change_Notification_t *)data;
if (fill_neigh_key_from_bts_pkt_cell_chg_not(&ctx->neigh_key, bts, notif) < 0) {
LOGPFSML(fi, LOGL_NOTICE, "TargetCell type=0x%x not supported\n",
notif->Target_Cell.UnionType);
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
} else {
nacc_fsm_state_chg(fi, NACC_ST_WAIT_RESOLVE_RAC_CI);
}
break;
default:
OSMO_ASSERT(0);
}
}
static int send_neigh_addr_req_ctrl_iface(struct nacc_fsm_ctx *ctx)
{
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
struct gprs_pcu *pcu = bts->pcu;
struct ctrl_cmd *cmd = NULL;
int rc;
/* We may have changed to this state previously (eg: we are handling
* another Pkt cell Change Notify with different target). Avoid
* re-creating the socket in that case. */
if (ctx->neigh_ctrl_conn->write_queue.bfd.fd == -1) {
rc = osmo_sock_init2_ofd(&ctx->neigh_ctrl_conn->write_queue.bfd,
AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP,
NULL, 0, pcu->vty.neigh_ctrl_addr, pcu->vty.neigh_ctrl_port,
OSMO_SOCK_F_CONNECT);
if (rc < 0) {
LOGPFSML(ctx->fi, LOGL_ERROR,
"Failed to establish CTRL (neighbor resolution) connection to BSC r=%s:%u\n\n",
pcu->vty.neigh_ctrl_addr, pcu->vty.neigh_ctrl_port);
goto err_term;
}
}
cmd = ctrl_cmd_create(ctx, CTRL_TYPE_GET);
if (!cmd) {
LOGPFSML(ctx->fi, LOGL_ERROR, "CTRL msg creation failed\n");
goto err_term;
}
cmd->id = talloc_asprintf(cmd, "%u", arfcn_bsic_2_ctrl_id(ctx->neigh_key.tgt_arfcn,
ctx->neigh_key.tgt_bsic));
cmd->variable = talloc_asprintf(cmd, "neighbor_resolve_cgi_ps_from_lac_ci.%d.%d.%d.%d",
ctx->neigh_key.local_lac, ctx->neigh_key.local_ci,
ctx->neigh_key.tgt_arfcn, ctx->neigh_key.tgt_bsic);
rc = ctrl_cmd_send(&ctx->neigh_ctrl_conn->write_queue, cmd);
if (rc) {
LOGPFSML(ctx->fi, LOGL_ERROR, "CTRL msg sent failed: %d\n", rc);
goto err_term;
}
talloc_free(cmd);
return 0;
err_term:
talloc_free(cmd);
return -1;
}
static int send_neigh_addr_req(struct nacc_fsm_ctx *ctx)
{
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
/* If PCU was configured to use the old CTRL interface, use it: */
if (ctx->neigh_ctrl_conn)
return send_neigh_addr_req_ctrl_iface(ctx);
/* Otherwise, by default the new PCUIF over IPA Abis multiplex proto should be used: */
return pcu_tx_neigh_addr_res_req(bts, &ctx->neigh_key);
}
static void st_wait_resolve_rac_ci_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
struct gprs_pcu *pcu = bts->pcu;
const struct osmo_cell_global_id_ps *cgi_ps;
/* First try to find the value in the cache */
cgi_ps = neigh_cache_lookup_value(pcu->neigh_cache, &ctx->neigh_key);
if (cgi_ps) {
ctx->cgi_ps = *cgi_ps;
nacc_fsm_state_chg(fi, NACC_ST_WAIT_REQUEST_SI);
return;
}
/* CGI-PS not in cache, resolve it using BSC Neighbor Resolution CTRL interface */
LOGPFSML(fi, LOGL_DEBUG, "No CGI-PS found in cache, resolving " NEIGH_CACHE_ENTRY_KEY_FMT "...\n",
NEIGH_CACHE_ENTRY_KEY_ARGS(&ctx->neigh_key));
if (send_neigh_addr_req(ctx) < 0)
nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
}
static void st_wait_resolve_rac_ci(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
const Packet_Cell_Change_Notification_t *notif;
switch (event) {
case NACC_EV_RX_CELL_CHG_NOTIFICATION:
notif = (const Packet_Cell_Change_Notification_t *)data;
handle_retrans_pkt_cell_chg_notif(ctx, notif);
break;
case NACC_EV_RX_RAC_CI:
/* data is NULL upon failure */
if (data) {
ctx->cgi_ps = *(struct osmo_cell_global_id_ps *)data;
nacc_fsm_state_chg(fi, NACC_ST_WAIT_REQUEST_SI);
} else {
nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
}
break;
default:
OSMO_ASSERT(0);
}
}
/* At this point, we expect correct tgt cell info to be already in ctx->cgi_ps */
static void st_wait_request_si_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
struct gprs_pcu *pcu = bts->pcu;
struct bssgp_ran_information_pdu pdu;
const struct si_cache_value *si;
struct gprs_rlcmac_bts *bts_i;
int rc;
/* First check if the CGI-PS addresses a cell managed by this PCU. If
* that's the case, we already have the info and there's no need to go
* the RIM way since we'd end up to this same PCU on the other end anyway.
*/
llist_for_each_entry(bts_i, &the_pcu->bts_list, list) {
if (bts_i == bts) /* Makes no sense targeting the same cell */
continue;
if (osmo_cgi_ps_cmp(&ctx->cgi_ps, &bts_i->cgi_ps) != 0)
continue;
LOGPFSML(fi, LOGL_DEBUG, "neighbor CGI-PS %s addresses local BTS %d\n",
osmo_cgi_ps_name(&ctx->cgi_ps), bts_i->nr);
bts_fill_si_cache_value(bts, &ctx->si_info);
/* Tell the PCU scheduler we are ready to go, from here one we
* are polled/driven by the scheduler */
nacc_fsm_state_chg(fi, NACC_ST_TX_NEIGHBOUR_DATA);
return;
}
/* First check if we have SI info for the target cell in cache */
si = si_cache_lookup_value(pcu->si_cache, &ctx->cgi_ps);
if (si) {
/* Copy info since cache can be deleted at any point */
memcpy(&ctx->si_info, si, sizeof(ctx->si_info));
/* Tell the PCU scheduler we are ready to go, from here one we
* are polled/driven by the scheduler */
nacc_fsm_state_chg(fi, NACC_ST_TX_NEIGHBOUR_DATA);
return;
}
/* SI info not in cache, resolve it using RIM procedure against SGSN */
if (fill_rim_ran_info_req(ctx, &pdu) < 0) {
nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
return;
}
LOGPFSML(fi, LOGL_INFO, "Tx RIM RAN-INFO to request SI of %s\n",
osmo_cgi_ps_name(&ctx->cgi_ps));
rc = bssgp_tx_rim(&pdu, gprs_ns2_nse_nsei(ctx->ms->bts->nse));
if (rc < 0) {
LOGPFSML(fi, LOGL_ERROR, "Failed transmitting RIM RAN-INFO %s PDU: %d\n",
osmo_cgi_ps_name(&ctx->cgi_ps), rc);
nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
return;
}
}
static void st_wait_request_si(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
const Packet_Cell_Change_Notification_t *notif;
struct si_cache_entry *entry;
switch (event) {
case NACC_EV_RX_CELL_CHG_NOTIFICATION:
notif = (const Packet_Cell_Change_Notification_t *)data;
handle_retrans_pkt_cell_chg_notif(ctx, notif);
break;
case NACC_EV_RX_SI:
entry = (struct si_cache_entry *)data;
/* Copy info since cache can be deleted at any point */
memcpy(&ctx->si_info, &entry->value, sizeof(ctx->si_info));
/* Tell the PCU scheduler we are ready to go, from here one we
* are polled/driven by the scheduler */
nacc_fsm_state_chg(fi, NACC_ST_TX_NEIGHBOUR_DATA);
break;
default:
OSMO_ASSERT(0);
}
}
/* At this point, we already received all required SI information to send stored
* in struct nacc_fsm_ctx. We now wait for scheduler to ask us to construct
* RLCMAC DL CTRL messages to move FSM states forward
*/
static void st_tx_neighbour_data_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
ctx->si_info_bytes_sent = 0;
ctx->container_idx = 0;
}
static void st_tx_neighbour_data(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
const Packet_Cell_Change_Notification_t *notif;
struct nacc_ev_create_rlcmac_msg_ctx *data_ctx;
bool all_si_info_sent;
switch (event) {
case NACC_EV_RX_CELL_CHG_NOTIFICATION:
notif = (const Packet_Cell_Change_Notification_t *)data;
handle_retrans_pkt_cell_chg_notif(ctx, notif);
break;
case NACC_EV_CREATE_RLCMAC_MSG:
data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx *)data;
data_ctx->msg = create_packet_neighbour_cell_data(ctx, data_ctx->tbf, &all_si_info_sent);
if (!data_ctx->msg) {
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
if (all_si_info_sent) /* DONE */
nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
break;
default:
OSMO_ASSERT(0);
}
}
/* st_cell_chg_continue_on_enter:
* At this point, we already sent all Pkt Cell Neighbour Change rlcmac
* blocks, and we only need to wait to be scheduled again to send PKT
* CELL CHANGE NOTIFICATION and then we are done
*/
static void st_cell_chg_continue(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
const Packet_Cell_Change_Notification_t *notif;
struct nacc_ev_create_rlcmac_msg_ctx *data_ctx;
switch (event) {
case NACC_EV_RX_CELL_CHG_NOTIFICATION:
notif = (const Packet_Cell_Change_Notification_t *)data;
handle_retrans_pkt_cell_chg_notif(ctx, notif);
break;
case NACC_EV_CREATE_RLCMAC_MSG:
data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx *)data;
data_ctx->msg = create_packet_cell_chg_continue(ctx, data_ctx, &ctx->continue_poll_fn);
if (data_ctx->msg) {
ctx->continue_poll_ts = data_ctx->ts;
nacc_fsm_state_chg(fi, NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK);
}
break;
default:
OSMO_ASSERT(0);
}
}
static void st_wait_cell_chg_continue_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
const Packet_Cell_Change_Notification_t *notif;
switch (event) {
case NACC_EV_RX_CELL_CHG_NOTIFICATION:
notif = (const Packet_Cell_Change_Notification_t *)data;
handle_retrans_pkt_cell_chg_notif(ctx, notif);
break;
case NACC_EV_TIMEOUT_CELL_CHG_CONTINUE:
nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
break;
case NACC_EV_RX_CELL_CHG_CONTINUE_ACK:
nacc_fsm_state_chg(fi, NACC_ST_DONE);
break;
default:
OSMO_ASSERT(0);
}
}
static void st_done_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
}
static void nacc_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
/* after cleanup() finishes, FSM termination calls osmo_fsm_inst_free,
so we need to avoid double-freeing it during ctx talloc free
destructor */
talloc_reparent(ctx, ctx->ms, ctx->fi);
ctx->fi = NULL;
/* remove references from owning MS and free entire ctx */
ctx->ms->nacc = NULL;
talloc_free(ctx);
}
static int nacc_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
switch (fi->T) {
case PCU_TDEF_NEIGH_RESOLVE_TO:
case PCU_TDEF_SI_RESOLVE_TO:
nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
break;
}
return 0;
}
static struct osmo_fsm_state nacc_fsm_states[] = {
[NACC_ST_INITIAL] = {
.in_event_mask =
X(NACC_EV_RX_CELL_CHG_NOTIFICATION),
.out_state_mask =
X(NACC_ST_WAIT_RESOLVE_RAC_CI),
.name = "INITIAL",
.action = st_initial,
},
[NACC_ST_WAIT_RESOLVE_RAC_CI] = {
.in_event_mask =
X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
X(NACC_EV_RX_RAC_CI),
.out_state_mask =
X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
X(NACC_ST_WAIT_REQUEST_SI) |
X(NACC_ST_TX_CELL_CHG_CONTINUE),
.name = "WAIT_RESOLVE_RAC_CI",
.onenter = st_wait_resolve_rac_ci_on_enter,
.action = st_wait_resolve_rac_ci,
},
[NACC_ST_WAIT_REQUEST_SI] = {
.in_event_mask =
X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
X(NACC_EV_RX_SI),
.out_state_mask =
X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
X(NACC_ST_TX_NEIGHBOUR_DATA) |
X(NACC_ST_TX_CELL_CHG_CONTINUE),
.name = "WAIT_REQUEST_SI",
.onenter = st_wait_request_si_on_enter,
.action = st_wait_request_si,
},
[NACC_ST_TX_NEIGHBOUR_DATA] = {
.in_event_mask =
X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
X(NACC_EV_CREATE_RLCMAC_MSG),
.out_state_mask =
X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
X(NACC_ST_TX_CELL_CHG_CONTINUE),
.name = "TX_NEIGHBOUR_DATA",
.onenter = st_tx_neighbour_data_on_enter,
.action = st_tx_neighbour_data,
},
[NACC_ST_TX_CELL_CHG_CONTINUE] = {
.in_event_mask =
X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
X(NACC_EV_CREATE_RLCMAC_MSG),
.out_state_mask =
X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
X(NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK),
.name = "TX_CELL_CHG_CONTINUE",
.action = st_cell_chg_continue,
},
[NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK] = {
.in_event_mask =
X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
X(NACC_EV_RX_CELL_CHG_CONTINUE_ACK) |
X(NACC_EV_TIMEOUT_CELL_CHG_CONTINUE),
.out_state_mask =
X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
X(NACC_ST_TX_CELL_CHG_CONTINUE) |
X(NACC_ST_DONE),
.name = "WAIT_CELL_CHG_CONTINUE_ACK",
.action = st_wait_cell_chg_continue_ack,
},
[NACC_ST_DONE] = {
.in_event_mask = 0,
.out_state_mask = 0,
.name = "DONE",
.onenter = st_done_on_enter,
},
};
static struct osmo_fsm nacc_fsm = {
.name = "NACC",
.states = nacc_fsm_states,
.num_states = ARRAY_SIZE(nacc_fsm_states),
.timer_cb = nacc_fsm_timer_cb,
.cleanup = nacc_fsm_cleanup,
.log_subsys = DNACC,
.event_names = nacc_fsm_event_names,
};
static __attribute__((constructor)) void nacc_fsm_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&nacc_fsm) == 0);
}
void nacc_fsm_ctrl_reply_cb(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd, void *data)
{
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)data;
char *tmp = NULL, *tok, *saveptr;
unsigned int exp_id;
struct osmo_cell_global_id_ps cgi_ps;
LOGPFSML(ctx->fi, LOGL_NOTICE, "Received CTRL message: type=%d %s %s: %s\n",
cmd->type, cmd->variable, cmd->id, osmo_escape_str(cmd->reply, -1));
if (cmd->type != CTRL_TYPE_GET_REPLY || !cmd->reply) {
osmo_fsm_inst_dispatch(ctx->fi, NACC_EV_RX_RAC_CI, NULL);
return;
}
/* Validate it's the seqnum from our last GET cmd, and not from older
* one we may have requested in case MS decided to resend Pkt Cell
* Change Notify with a different tgt cell:
*/
exp_id = arfcn_bsic_2_ctrl_id(ctx->neigh_key.tgt_arfcn, ctx->neigh_key.tgt_bsic);
if ((unsigned int)atoi(cmd->id) != exp_id) {
LOGPFSML(ctx->fi, LOGL_INFO,
"Received CTRL message with id=%s doesn't match our expected last id=%d, ignoring\n",
cmd->id, exp_id);
return;
}
/* TODO: Potentially validate cmd->variable contains same params as we
sent, and that cmd->id matches the original set. We may want to keep
the original cmd around by setting cmd->defer=1 when sending it. */
tmp = talloc_strdup(cmd, cmd->reply);
if (!tmp)
goto free_ret;
if (!(tok = strtok_r(tmp, "-", &saveptr)))
goto free_ret;
cgi_ps.rai.lac.plmn.mcc = atoi(tok);
if (!(tok = strtok_r(NULL, "-", &saveptr)))
goto free_ret;
cgi_ps.rai.lac.plmn.mnc = atoi(tok);
if (!(tok = strtok_r(NULL, "-", &saveptr)))
goto free_ret;
cgi_ps.rai.lac.lac = atoi(tok);
if (!(tok = strtok_r(NULL, "-", &saveptr)))
goto free_ret;
cgi_ps.rai.rac = atoi(tok);
if (!(tok = strtok_r(NULL, "\0", &saveptr)))
goto free_ret;
cgi_ps.cell_identity = atoi(tok);
/* Cache the cgi_ps so we can avoid requesting again same resolution for a while */
neigh_cache_add(ctx->ms->bts->pcu->neigh_cache, &ctx->neigh_key, &cgi_ps);
osmo_fsm_inst_dispatch(ctx->fi, NACC_EV_RX_RAC_CI, &cgi_ps);
return;
free_ret:
talloc_free(tmp);
osmo_fsm_inst_dispatch(ctx->fi, NACC_EV_RX_RAC_CI, NULL);
return;
}
static int nacc_fsm_ctx_talloc_destructor(struct nacc_fsm_ctx *ctx)
{
if (ctx->fi) {
osmo_fsm_inst_free(ctx->fi);
ctx->fi = NULL;
}
if (ctx->neigh_ctrl_conn) {
if (ctx->neigh_ctrl_conn->write_queue.bfd.fd != -1) {
osmo_wqueue_clear(&ctx->neigh_ctrl_conn->write_queue);
osmo_fd_unregister(&ctx->neigh_ctrl_conn->write_queue.bfd);
close(ctx->neigh_ctrl_conn->write_queue.bfd.fd);
ctx->neigh_ctrl_conn->write_queue.bfd.fd = -1;
}
}
return 0;
}
struct nacc_fsm_ctx *nacc_fsm_alloc(struct GprsMs* ms)
{
struct nacc_fsm_ctx *ctx = talloc_zero(ms, struct nacc_fsm_ctx);
char buf[64];
talloc_set_destructor(ctx, nacc_fsm_ctx_talloc_destructor);
ctx->ms = ms;
snprintf(buf, sizeof(buf), "TLLI-0x%08x", ms_tlli(ms));
ctx->fi = osmo_fsm_inst_alloc(&nacc_fsm, ctx, ctx, LOGL_INFO, buf);
if (!ctx->fi)
goto free_ret;
/* If CTRL ip present, use the old CTRL interface for neighbor resolution */
if (ms->bts->pcu->vty.neigh_ctrl_addr) {
ctx->neigh_ctrl = ctrl_handle_alloc(ctx, ctx, NULL);
ctx->neigh_ctrl->reply_cb = nacc_fsm_ctrl_reply_cb;
ctx->neigh_ctrl_conn = osmo_ctrl_conn_alloc(ctx, ctx->neigh_ctrl);
if (!ctx->neigh_ctrl_conn)
goto free_ret;
/* Older versions of osmo_ctrl_conn_alloc didn't properly initialize fd to -1,
* so make sure to do it here otherwise fd may be valid fd 0 and cause trouble */
ctx->neigh_ctrl_conn->write_queue.bfd.fd = -1;
llist_add(&ctx->neigh_ctrl_conn->list_entry, &ctx->neigh_ctrl->ccon_list);
}
return ctx;
free_ret:
talloc_free(ctx);
return NULL;
}
bool nacc_fsm_is_waiting_addr_resolution(const struct nacc_fsm_ctx *ctx,
const struct neigh_cache_entry_key *neigh_key)
{
if (ctx->fi->state != NACC_ST_WAIT_RESOLVE_RAC_CI)
return false;
return neigh_cache_entry_key_eq(&ctx->neigh_key, neigh_key);
}
bool nacc_fsm_is_waiting_si_resolution(const struct nacc_fsm_ctx *ctx,
const struct osmo_cell_global_id_ps *cgi_ps)
{
if (ctx->fi->state != NACC_ST_WAIT_REQUEST_SI)
return false;
return !osmo_cgi_ps_cmp(&ctx->cgi_ps, cgi_ps);
}
bool nacc_fsm_exp_ctrl_ack(const struct nacc_fsm_ctx *ctx, uint32_t fn, uint8_t ts)
{
return ctx->fi->state == NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK &&
ctx->continue_poll_fn == fn &&
ctx->continue_poll_ts == ts;
}

79
src/nacc_fsm.h Normal file
View File

@@ -0,0 +1,79 @@
/* nacc_fsm.h
*
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@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 <osmocom/core/fsm.h>
#include <osmocom/gsm/gsm23003.h>
#include <neigh_cache.h>
struct GprsMs;
struct gprs_rlcmac_tbf;
enum nacc_fsm_event {
NACC_EV_RX_CELL_CHG_NOTIFICATION, /* data: Packet_Cell_Change_Notification_t* */
NACC_EV_RX_RAC_CI, /* RAC_CI became available in neigh_cache. NULL on failure, pointer to ctx->cgi_ps on success */
NACC_EV_RX_SI, /* data: struct si_cache_entry* */
NACC_EV_CREATE_RLCMAC_MSG, /* data: struct nacc_ev_create_rlcmac_msg_ctx* */
NACC_EV_RX_CELL_CHG_CONTINUE_ACK,
NACC_EV_TIMEOUT_CELL_CHG_CONTINUE, /* Poll Timeout */
};
enum nacc_fsm_states {
NACC_ST_INITIAL,
NACC_ST_WAIT_RESOLVE_RAC_CI,
NACC_ST_WAIT_REQUEST_SI,
NACC_ST_TX_NEIGHBOUR_DATA,
NACC_ST_TX_CELL_CHG_CONTINUE,
NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK,
NACC_ST_DONE,
};
struct nacc_fsm_ctx {
struct osmo_fsm_inst *fi;
struct GprsMs* ms; /* back pointer */
struct ctrl_handle *neigh_ctrl;
struct ctrl_connection *neigh_ctrl_conn;
struct neigh_cache_entry_key neigh_key; /* target cell info from MS */
struct osmo_cell_global_id_ps cgi_ps; /* target cell info resolved from req_{arfcn+bsic} */
struct si_cache_value si_info; /* SI info resolved from SGSN, to be sent to MS */
size_t si_info_bytes_sent; /* How many bytes out of si_info->si_len were already sent to MS */
size_t container_idx; /* Next container_idx to assign when sending Packet Neighbor Data message */
uint32_t continue_poll_fn; /* Scheduled poll FN to CTRL ACK the Pkt Cell Chg Continue */
uint8_t continue_poll_ts; /* Scheduled poll TS to CTRL ACK the Pkt Cell Chg Continue */
};
/* passed as data in NACC_EV_CREATE_RLCMAC_MSG */
struct nacc_ev_create_rlcmac_msg_ctx {
struct gprs_rlcmac_tbf *tbf; /* target tbf to create messages for */
uint32_t fn; /* FN where the created DL ctrl block is to be sent */
uint8_t ts; /* TS where the created DL ctrl block is to be sent */
struct msgb *msg; /* to be filled by FSM during event processing */
};
struct nacc_fsm_ctx *nacc_fsm_alloc(struct GprsMs* ms);
bool nacc_fsm_is_waiting_addr_resolution(const struct nacc_fsm_ctx *ctx,
const struct neigh_cache_entry_key *neigh_key);
bool nacc_fsm_is_waiting_si_resolution(const struct nacc_fsm_ctx *ctx,
const struct osmo_cell_global_id_ps *cgi_ps);
bool nacc_fsm_exp_ctrl_ack(const struct nacc_fsm_ctx *ctx, uint32_t fn, uint8_t ts);

289
src/neigh_cache.c Normal file
View File

@@ -0,0 +1,289 @@
/* si_cache.c
*
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@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 <string.h>
#include <talloc.h>
#include <inttypes.h>
#include <osmocom/core/utils.h>
#include <neigh_cache.h>
#include <gprs_debug.h>
static void neigh_cache_schedule_cleanup(struct neigh_cache *cache);
static void neigh_cache_cleanup_cb(void *data)
{
struct timespec now, threshold;
struct neigh_cache *cache = (struct neigh_cache *)data;
struct neigh_cache_entry *it, *tmp;
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
/* Instead of adding keep_time_intval to each, substract it from now once */
timespecsub(&now, &cache->keep_time_intval, &threshold);
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
if (timespeccmp(&threshold, &it->update_ts, <))
break;
LOGP(DNACC, LOGL_DEBUG,
"neigh_cache: Removing entry " NEIGH_CACHE_ENTRY_KEY_FMT " => %s\n",
NEIGH_CACHE_ENTRY_KEY_ARGS(&it->key), osmo_cgi_ps_name(&it->value));
llist_del(&it->list);
talloc_free(it);
}
neigh_cache_schedule_cleanup(cache);
}
static void neigh_cache_schedule_cleanup(struct neigh_cache *cache)
{
struct neigh_cache_entry *it;
struct timespec now, threshold, result;
/* First item is the one with oldest update_ts */
it = llist_first_entry_or_null(&cache->list, struct neigh_cache_entry, list);
if (!it)
return;
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);
if (timespeccmp(&now, &threshold, >=)) {
/* Too late, let's flush asynchonously so newly added isn't
* immediatelly freed before return. */
result = (struct timespec){ .tv_sec = 0, .tv_nsec = 0 };
} else {
timespecsub(&threshold, &now, &result);
}
osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);
}
struct neigh_cache *neigh_cache_alloc(void *ctx, unsigned int keep_time_sec)
{
struct neigh_cache *cache = talloc_zero(ctx, struct neigh_cache);
OSMO_ASSERT(cache);
INIT_LLIST_HEAD(&cache->list);
osmo_timer_setup(&cache->cleanup_timer, neigh_cache_cleanup_cb, cache);
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
return cache;
}
void neigh_cache_set_keep_time_interval(struct neigh_cache *cache, unsigned int keep_time_sec)
{
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
neigh_cache_schedule_cleanup(cache);
}
struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,
const struct neigh_cache_entry_key *key,
const struct osmo_cell_global_id_ps *value)
{
struct neigh_cache_entry *it;
/* First check if it already exists. If so, simply update timer+value */
it = neigh_cache_lookup_entry(cache, key);
if (!it) {
LOGP(DNACC, LOGL_DEBUG,
"neigh_cache: Inserting new entry " NEIGH_CACHE_ENTRY_KEY_FMT " => %s\n",
NEIGH_CACHE_ENTRY_KEY_ARGS(key), osmo_cgi_ps_name(value));
it = talloc_zero(cache, struct neigh_cache_entry);
OSMO_ASSERT(it);
memcpy(&it->key, key, sizeof(it->key));
} else {
LOGP(DNACC, LOGL_DEBUG,
"neigh_cache: Updating entry " NEIGH_CACHE_ENTRY_KEY_FMT " => (%s -> %s)\n",
NEIGH_CACHE_ENTRY_KEY_ARGS(key), osmo_cgi_ps_name(&it->value), osmo_cgi_ps_name2(value));
/* remove item, we'll add it to the end to have them sorted by last update */
llist_del(&it->list);
}
memcpy(&it->value, value, sizeof(it->value));
OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);
llist_add_tail(&it->list, &cache->list);
neigh_cache_schedule_cleanup(cache);
return it;
}
struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,
const struct neigh_cache_entry_key *key)
{
struct neigh_cache_entry *tmp;
llist_for_each_entry(tmp, &cache->list, list) {
if (neigh_cache_entry_key_eq(&tmp->key, key))
return tmp;
}
return NULL;
}
const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,
const struct neigh_cache_entry_key *key)
{
struct neigh_cache_entry *it = neigh_cache_lookup_entry(cache, key);
if (it)
return &it->value;
return NULL;
}
void neigh_cache_free(struct neigh_cache *cache)
{
struct neigh_cache_entry *it, *tmp;
if (!cache)
return;
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
llist_del(&it->list);
talloc_free(it);
}
osmo_timer_del(&cache->cleanup_timer);
talloc_free(cache);
}
////////////////////
// SI CACHE
///////////////////
static void si_cache_schedule_cleanup(struct si_cache *cache);
static void si_cache_cleanup_cb(void *data)
{
struct timespec now, threshold;
struct si_cache *cache = (struct si_cache *)data;
struct si_cache_entry *it, *tmp;
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
/* Instead of adding keep_time_intval to each, substract it from now once */
timespecsub(&now, &cache->keep_time_intval, &threshold);
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
if (timespeccmp(&threshold, &it->update_ts, <))
break;
LOGP(DNACC, LOGL_DEBUG, "si_cache: Removing entry %s\n",
osmo_cgi_ps_name(&it->key));
llist_del(&it->list);
talloc_free(it);
}
si_cache_schedule_cleanup(cache);
}
static void si_cache_schedule_cleanup(struct si_cache *cache)
{
struct si_cache_entry *it;
struct timespec now, threshold, result;
/* First item is the one with oldest update_ts */
it = llist_first_entry_or_null(&cache->list, struct si_cache_entry, list);
if (!it)
return;
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);
if (timespeccmp(&now, &threshold, >=)) {
/* Too late, let's flush asynchonously so newly added isn't
* immediatelly freed before return. */
result = (struct timespec){ .tv_sec = 0, .tv_nsec = 0 };
} else {
timespecsub(&threshold, &now, &result);
}
osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);
}
struct si_cache *si_cache_alloc(void *ctx, unsigned int keep_time_sec)
{
struct si_cache *cache = talloc_zero(ctx, struct si_cache);
OSMO_ASSERT(cache);
INIT_LLIST_HEAD(&cache->list);
osmo_timer_setup(&cache->cleanup_timer, si_cache_cleanup_cb, cache);
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
return cache;
}
void si_cache_set_keep_time_interval(struct si_cache *cache, unsigned int keep_time_sec)
{
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
si_cache_schedule_cleanup(cache);
}
struct si_cache_entry *si_cache_add(struct si_cache *cache,
const struct osmo_cell_global_id_ps *key,
const struct si_cache_value *value)
{
struct si_cache_entry *it;
/* First check if it already exists. If so, simply update timer+value */
it = si_cache_lookup_entry(cache, key);
if (!it) {
LOGP(DNACC, LOGL_DEBUG, "si_cache: Inserting new entry %s\n",
osmo_cgi_ps_name(key));
it = talloc_zero(cache, struct si_cache_entry);
OSMO_ASSERT(it);
memcpy(&it->key, key, sizeof(it->key));
} else {
LOGP(DNACC, LOGL_DEBUG, "si_cache: Updating entry %s\n",
osmo_cgi_ps_name(&it->key));
/* remove item, we'll add it to the end to have them sorted by last update */
llist_del(&it->list);
}
memcpy(&it->value, value, sizeof(it->value));
OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);
llist_add_tail(&it->list, &cache->list);
si_cache_schedule_cleanup(cache);
return it;
}
struct si_cache_entry *si_cache_lookup_entry(struct si_cache *cache,
const struct osmo_cell_global_id_ps *key)
{
struct si_cache_entry *tmp;
llist_for_each_entry(tmp, &cache->list, list) {
if (osmo_cgi_ps_cmp(&tmp->key, key) == 0)
return tmp;
}
return NULL;
}
const struct si_cache_value *si_cache_lookup_value(struct si_cache *cache,
const struct osmo_cell_global_id_ps *key)
{
struct si_cache_entry *it = si_cache_lookup_entry(cache, key);
if (it)
return &it->value;
return NULL;
}
void si_cache_free(struct si_cache *cache)
{
struct si_cache_entry *it, *tmp;
if (!cache)
return;
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
llist_del(&it->list);
talloc_free(it);
}
osmo_timer_del(&cache->cleanup_timer);
talloc_free(cache);
}

112
src/neigh_cache.h Normal file
View File

@@ -0,0 +1,112 @@
/* neigh_cache.h
*
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@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 <inttypes.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gprs/gprs_bssgp_rim.h>
////////////////////
// NEIGH CACHE
///////////////////
/* ARFC+BSIC -> CGI PS cache */
struct neigh_cache {
struct llist_head list; /* list of neigh_cache_entry items */
struct osmo_timer_list cleanup_timer; /* Timer removing too-old entries */
struct timespec keep_time_intval;
};
struct neigh_cache_entry_key {
uint16_t local_lac;
uint16_t local_ci;
uint16_t tgt_arfcn;
uint8_t tgt_bsic;
};
#define NEIGH_CACHE_ENTRY_KEY_FMT "%" PRIu16 "-%" PRIu16 "-%" PRIu16 "-%" PRIu8
#define NEIGH_CACHE_ENTRY_KEY_ARGS(key) (key)->local_lac, (key)->local_ci, (key)->tgt_arfcn, (key)->tgt_bsic
static inline bool neigh_cache_entry_key_eq(const struct neigh_cache_entry_key *a,
const struct neigh_cache_entry_key *b)
{
return a->local_lac == b->local_lac &&
a->local_ci == b->local_ci &&
a->tgt_arfcn == b->tgt_arfcn &&
a->tgt_bsic == b->tgt_bsic;
}
struct neigh_cache_entry {
struct llist_head list; /* to be included in neigh_cache->list */
struct timespec update_ts;
struct neigh_cache_entry_key key;
struct osmo_cell_global_id_ps value;
};
struct neigh_cache *neigh_cache_alloc(void *ctx, unsigned int keep_time_sec);
void neigh_cache_set_keep_time_interval(struct neigh_cache *cache, unsigned int keep_time_sec);
struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,
const struct neigh_cache_entry_key *key,
const struct osmo_cell_global_id_ps *value);
struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,
const struct neigh_cache_entry_key *key);
const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,
const struct neigh_cache_entry_key *key);
void neigh_cache_free(struct neigh_cache *cache);
////////////////////
// SI CACHE
///////////////////
/* CGI-PS-> SI cache */
struct si_cache {
struct llist_head list; /* list of si_cache_entry items */
struct osmo_timer_list cleanup_timer; /* Timer removing too-old entries */
struct timespec keep_time_intval;
};
struct si_cache_value {
uint8_t si_buf[BSSGP_RIM_PSI_LEN * 127]; /* 3GPP TS 48.018 11.3.63.2.1 */
size_t si_len;
bool type_psi;
};
struct si_cache_entry {
struct llist_head list; /* to be included in si_cache->list */
struct timespec update_ts;
struct osmo_cell_global_id_ps key;
struct si_cache_value value;
};
struct si_cache *si_cache_alloc(void *ctx, unsigned int keep_time_sec);
void si_cache_set_keep_time_interval(struct si_cache *cache, unsigned int keep_time_sec);
struct si_cache_entry *si_cache_add(struct si_cache *cache,
const struct osmo_cell_global_id_ps *key,
const struct si_cache_value *value);
struct si_cache_entry *si_cache_lookup_entry(struct si_cache *cache,
const struct osmo_cell_global_id_ps *key);
const struct si_cache_value *si_cache_lookup_value(struct si_cache *cache,
const struct osmo_cell_global_id_ps *key);
void si_cache_free(struct si_cache *cache);

View File

@@ -153,11 +153,7 @@ int l1if_transport_open(int q, struct lc15l1_hdl *hdl)
buf, strerror(errno));
return rc;
}
read_ofd->fd = rc;
read_ofd->priv_nr = q;
read_ofd->data = hdl;
read_ofd->cb = l1if_fd_cb;
read_ofd->when = BSC_FD_READ;
osmo_fd_setup(read_ofd, rc, OSMO_FD_READ, l1if_fd_cb, hdl, q);
rc = osmo_fd_register(read_ofd);
if (rc < 0) {
close(read_ofd->fd);
@@ -176,10 +172,7 @@ int l1if_transport_open(int q, struct lc15l1_hdl *hdl)
}
osmo_wqueue_init(wq, 10);
wq->write_cb = l1fd_write_cb;
write_ofd->fd = rc;
write_ofd->priv_nr = q;
write_ofd->data = hdl;
write_ofd->when = BSC_FD_WRITE;
osmo_fd_setup(write_ofd, rc, OSMO_FD_WRITE, osmo_wqueue_bfd_cb, hdl, q);
rc = osmo_fd_register(write_ofd);
if (rc < 0) {
close(write_ofd->fd);

View File

@@ -148,6 +148,7 @@ static int handle_ph_readytosend_ind(struct lc15l1_hdl *fl1h,
GsmL1_PhReadyToSendInd_t *rts_ind)
{
struct gsm_time g_time;
struct gprs_rlcmac_bts *bts;
int rc = 0;
gsm_fn2gsmtime(&g_time, rts_ind->u32Fn);
@@ -156,13 +157,19 @@ static int handle_ph_readytosend_ind(struct lc15l1_hdl *fl1h,
g_time.t1, g_time.t2, g_time.t3,
get_value_string(lc15bts_l1sapi_names, rts_ind->sapi));
bts = llist_first_entry_or_null(&the_pcu->bts_list, struct gprs_rlcmac_bts, list);
switch (rts_ind->sapi) {
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
rc = pcu_rx_rts_req_pdtch(fl1h->trx_no, rts_ind->u8Tn,
rc = pcu_rx_rts_req_pdtch(bts, fl1h->trx_no, rts_ind->u8Tn,
rts_ind->u32Fn, rts_ind->u8BlockNbr);
break;
case GsmL1_Sapi_Ptcch:
// FIXME
rc = pcu_rx_rts_req_ptcch(bts, fl1h->trx_no, rts_ind->u8Tn,
rts_ind->u32Fn, rts_ind->u8BlockNbr);
break;
default:
break;
}
@@ -186,47 +193,47 @@ static int handle_ph_data_ind(struct lc15l1_hdl *fl1h,
GsmL1_PhDataInd_t *data_ind, struct msgb *l1p_msg)
{
int rc = 0;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_pdch *pdch;
struct pcu_l1_meas meas = {0};
uint8_t *data;
uint8_t data_len;
DEBUGP(DL1IF, "Rx PH-DATA.ind %s (hL2 %08x): %s\n",
DEBUGP(DL1IF, "(trx=%" PRIu8 ",ts=%u) FN=%u Rx PH-DATA.ind %s (hL2 %08x): %s\n",
fl1h->trx_no, data_ind->u8Tn, data_ind->u32Fn,
get_value_string(lc15bts_l1sapi_names, data_ind->sapi),
data_ind->hLayer2,
osmo_hexdump(data_ind->msgUnitParam.u8Buffer,
data_ind->msgUnitParam.u8Size));
/*
* TODO: Add proper bad frame handling here. This could be used
* to switch the used CS. Avoid a crash with the PCU right now
* feed "0 - 1" amount of data.
*/
if (data_ind->msgUnitParam.u8Size == 0)
return -1;
bts = llist_first_entry_or_null(&the_pcu->bts_list, struct gprs_rlcmac_bts, list);
get_meas(&meas, &data_ind->measParam);
bts_update_tbf_ta("PH-DATA", data_ind->u32Fn, fl1h->trx_no,
bts_update_tbf_ta(bts, "PH-DATA", data_ind->u32Fn, fl1h->trx_no,
data_ind->u8Tn, sign_qta2ta(meas.bto), false);
switch (data_ind->sapi) {
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
/* drop incomplete UL block */
if (data_ind->msgUnitParam.u8Buffer[0]
!= GsmL1_PdtchPlType_Full)
break;
/* PDTCH / PACCH frame handling */
rc = pcu_rx_data_ind_pdtch(fl1h->trx_no, data_ind->u8Tn,
data_ind->msgUnitParam.u8Buffer + 1,
data_ind->msgUnitParam.u8Size - 1,
data_ind->u32Fn,
&meas);
break;
case GsmL1_Sapi_Ptcch:
// FIXME
rc = -1;
if (data_ind->msgUnitParam.u8Size != 0 &&
data_ind->msgUnitParam.u8Buffer[0] == GsmL1_PdtchPlType_Full) {
data = data_ind->msgUnitParam.u8Buffer + 1;
data_len = data_ind->msgUnitParam.u8Size - 1;
if (data_len == 0)
data = NULL;
} else {
data = NULL;
data_len = 0;
}
pdch = &bts->trx[fl1h->trx_no].pdch[data_ind->u8Tn];
pcu_rx_data_ind_pdtch(bts, pdch, data, data_len, data_ind->u32Fn, &meas);
break;
default:
LOGP(DL1IF, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n",
get_value_string(lc15bts_l1sapi_names, data_ind->sapi));
LOGP(DL1IF, LOGL_NOTICE,
"(trx=%" PRIu8 ",ts=%u) FN=%u Rx PH-DATA.ind for unknown L1 SAPI %s\n",
fl1h->trx_no, data_ind->u8Tn, data_ind->u32Fn,
get_value_string(lc15bts_l1sapi_names, data_ind->sapi));
break;
}
@@ -244,12 +251,29 @@ static int handle_ph_data_ind(struct lc15l1_hdl *fl1h,
static int handle_ph_ra_ind(struct lc15l1_hdl *fl1h, GsmL1_PhRaInd_t *ra_ind)
{
struct gprs_rlcmac_bts *bts;
if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
return 0;
DEBUGP(DL1IF, "Rx PH-RA.ind");
bts_update_tbf_ta("PH-RA", ra_ind->u32Fn, fl1h->trx_no, ra_ind->u8Tn,
qta2ta(ra_ind->measParam.i16BurstTiming), true);
bts = llist_first_entry_or_null(&the_pcu->bts_list, struct gprs_rlcmac_bts, list);
switch (ra_ind->sapi) {
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Prach:
bts_update_tbf_ta(bts, "PH-RA", ra_ind->u32Fn, fl1h->trx_no, ra_ind->u8Tn,
qta2ta(ra_ind->measParam.i16BurstTiming), true);
break;
case GsmL1_Sapi_Ptcch:
pcu_rx_rach_ind_ptcch(bts, fl1h->trx_no, ra_ind->u8Tn, ra_ind->u32Fn,
ra_ind->measParam.i16BurstTiming);
break;
default:
LOGP(DL1IF, LOGL_NOTICE, "Rx PH-RA.ind for unknown L1 SAPI %s\n",
get_value_string(lc15bts_l1sapi_names, ra_ind->sapi));
return -ENOTSUP;
}
return 0;
}
@@ -374,4 +398,3 @@ int l1if_close_pdch(void *obj)
talloc_free(fl1h);
return 0;
}

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More