Compare commits

...

38 Commits

Author SHA1 Message Date
Oliver Smith
7db79cc39a Fix build with debian 13
oml.c:461:20: error: returning 'HANDLE' {aka 'void *'} from a function with return type 'uint32_t' {aka 'unsigned int'} makes integer from pointer without a cast [-Wint-conversion]

Change-Id: I0831e448692713c488c0590a86bdc99b37160f47
2025-10-02 09:50:04 +02:00
Mychaela N. Falconia
8d11671539 HRv1 codec: add support for TW-TS-002
OsmoBTS supports TW-TS-001 enhanced RTP format for FR and EFR codecs
since 2024, providing functional equivalent of GSM 08.60 TRAU-UL
output over IP physical transport.  Now do the same with TW-TS-002,
IP equivalent of GSM 08.61 for HRv1 codec.

Only TCH UL path is affected; no changes are needed to TCH DL path
because existing RTP Rx handling for RFC 5993 also covers TW-TS-002.

Related: OS#6036
Change-Id: Icf11e43d4ce9df990d0e0a856d62d9ea11cb837c
2025-09-05 22:16:07 +00:00
Mychaela N. Falconia
cb5b10e7b7 FR/HR/EFR: implement SID filter in TCH UL path
As a result of how FR/HR/EFR DTX interacts with block diagonal
interleaving, at the beginning and end of each DTX pause a
correctly functioning TCH receiver will always pick up an
artifact consisting of 4 received bursts (2 for TCH/HS)
and same number of omitted bursts.  Standard channel decoding
of this Rx artifact will produce a "half-block" in which half
of the bits prior to convolutional decoding will come from
a SID repetition whose Tx was notionally suppressed, while
the other half will be garbage.  As a result of convolutional
decoding, the result will often appear as valid SID per
classification rules - but passing it as such to the Rx DTX
handler is wrong.  Classic E1 BTS and GSM MS implementations
include a kind of SID filter at this point, setting BFI on
these received half-blocks, so that the Rx DTX handler will
see an invalid SID condition.  Invalid SID means that comfort
noise generation is to be continued, but no updated CN
parameters are available - which is the truth in half-block
Rx situations.  Implement the same filter.

Additional background info can be found here:

https://osmocom.org/projects/retro-gsm/wiki/DTXu_half-blocks

Change-Id: I46c62312316b04567bcadf6050597673f071247d
2025-09-05 19:38:55 +00:00
Mychaela N. Falconia
d9b456ce6d FR/HR/EFR: centralize TCH UL SID classification
In any environment where GSM MS may exercise DTXu on TCH/FS, TCH/HS or
TCH/EFS, the BTS receiving this TCH UL has to classify each received
traffic frame as valid SID, invalid SID or non-SID speech.  For E1 BTS
this SID classification requirement is explicit as there are dedicated
bits in TRAU-UL frames carrying the SID code.  For an IP BTS the need
for this classification is less obvious as most RTP payload formats
omit SID indicator bits - however:

* For HR codec, RTP output in RFC 5993 and TW-TS-002 formats does
  include explicit SID classification;

* Also for HR output in both TS 101 318 and RFC 5993 formats
  (but not TW-TS-002), SID classification must be considered in order
  to turn valid SID with some bit errors into perfect SID codeword;

* OsmoBTS already had logic for all 3 of FR/HR/EFR whereby if a frame
  is received that is an accepted SID frame in GSM 06.31/06.41/06.81
  definition, a flag is set so that the next good speech frame will
  be emitted in RTP with marker bit set.  This logic implies SID
  classification in TCH UL path.

Prior to this patch, OsmoBTS performed limited, non-consolidated
SID classification:

* For FR and EFR, the only SID classification in TCH UL path was done
  for RTP marker purposes by way of osmo_{fr,efr}_is_any_sid() Boolean
  result fed to lchan_set_marker();

* For the same RTP marker logic with HR codec, only perfect, error-free
  SID frames were detected;

* The same limitation applied to SID classification for RFC 5993 output.

Centralize this SID classification by moving it from BTS model to common
l1sap code and unifying it across all 3 codecs.  Immediate functional
effects from this change are:

* On TCH/HS we now detect imperfect (partially corrupted) SID frames
  and classify them as valid or invalid SID as intended by ETSI,
  like we already did for TCH/FS and TCH/EFS;

* When emitting TS 101 318 or RFC 5993, we apply the inherent limitations
  of those RTP formats to valid and invalid SID;

* With all 3 codecs, the check for a good speech frame as exit criterion
  from DTXu state now happens after the link quality check in l1sap,
  rather than before.

AMR speech mode is not affected at all by these changes: AMR DTX model
is completely different from that of FR/HR/EFR.

Related: OS#6036
Change-Id: Id6c8c146962de2f173760889eb232693bb4229d3
2025-09-05 19:30:07 +00:00
Mychaela N. Falconia
a9f3c01ece TCH UL path: add out-of-band BFI flag
In original OsmoBTS architecture prior to Themyscira patches,
BFI (Bad Frame Indication) condition was signaled internally by
zero-length payload passed from BTS model to l1sap, and externally
by absence of RTP output (intentional gap) or a zero-length RTP
payload emitted in 'rtp continuous-streaming' mode.  However, this
paradigm is contrary to classic GSM BSS architecture in which BFI
is an out-of-band metadata flag that travels alongside with frame
payload bits, whether the latter are valid or invalid.

Since 2024 OsmoBTS supports the option of TW-TS-001 output for
FR and EFR codecs, which allows the possibility of BFI-with-data
in RTP.  OsmoBTS can already emit such BFI-with-data packets when
the BTS model delivered a deemed-good traffic frame, but that frame
was subsequently deemed bad by the link quality check in l1sap -
but there is still no explicit out-of-band BFI flag inside OsmoBTS
TCH UL path.

By introducing an out-of-band BFI flag, we make it possible for BTS
model PHYs to deliver marked-bad traffic frames: when CRC fails
in GSM 05.03 channel decoding step, a PHY that permits modification
(libosmocoding or future FOSS DSP PHY) can be enhanced to preserve
channel-decoded bits while conveying BFI.

The link quality check in l1sap is reworked to use the new OOB BFI
flag.  Follow-on patches will introduce further logic that can also
assert BFI at high level, above BTS model PHYs.

While reworking the link quality check in l1sap, restrict it to
speech modes only: per GSM specs, BFI does not exist in CSD.

Change-Id: I8097946429e83eae90f89e49d17ffb8eb0636fcb
2025-09-05 19:03:40 +00:00
Vadim Yanitskiy
799ad7240b osmo-bts-trx: trx_fn_timer_cb(): fix misleading shutdown reason
If osmo-bts-trx exit()s due to the PC clock issues, e.g. if the
process stalls, it produces rather confusing logging messages:

  DL1C ERROR PC clock skew: elapsed_us=387574, error_us=382959
  DOML NOTICE ... Shutting down BTS, exit 1, reason: No clock from osmo-trx

The second message suggests that the transceiver (osmo-trx) is the
culprit, but the first one reflects the actual reason (PC clock skew).

Let's pass proper shutdown reason to avoid confusion.

Change-Id: Ibbbbc4e919e6eb812882fc60de4be13fa77934b7
2025-04-25 21:53:36 +07:00
Vadim Yanitskiy
d4bb8d61e7 power_control: always feed input values to do_avg_algo()
The purpose of the power control interval (P_CON_INTERVAL) is to
temporarily suspend the decision-making process of the MS/BS power
control algorithm, allowing time to observe the effects of a
previous adjustment.

However, input value (RxLev/RxQual) averaging must continue
uninterrupted, regardless of the power control interval.
Otherwise we're simply loosing measurement samples.

Change-Id: I2ccad1cb0ebbfcce64a93bc81b66db37b1399769
2025-04-17 05:54:29 +00:00
Vadim Yanitskiy
aa743cd97e rsl: rsl_rx_chan_activ(): fix code formatting
Change-Id: Ib22f9b90a5194370d2b5bb768d050cc0b167320e
2025-03-19 02:15:20 +07:00
Oliver Smith
65c497a69a Bump version: 1.8.0.29-b032-dirty → 1.9.0
Change-Id: Ibef4a94a6c7bb3a2b9acb3ebb1aaf50ff7a060ba
2025-02-12 12:56:09 +01:00
Philipp Maier
b032208a4a pcu_sock: do not receive a TXT ind. with PCU_VERSION for a specific BTS
We receive a TXT indication that contains the PCU_VERSION from the PCU when the
connection to the PCU is established. This message contains a BTS number, which
is always 0. This is a hard coded value that does not refer to a specific BTS
object. Also it is not logical to inform a specific BTS object about the PCU
version. This information should be directed to the connecting process as a
whole. However, we use this TXT indication to trigger certain initialization
processes on the BTS object we manage inside the BTS process (currently this
is only 1 bts, but this may change in the future). So instead of using the
BTS in the TXT indication, we should iterate of over all BTS objects and
trigger the initializations for each of the BTS objects.

This change does not have any dependencies, nor does it change the current
behavior of osmo-bts. It just cleans up the logic, so that it works by
intension and not just by chance.

Related: OS#6507
Change-Id: I1bb8d0ec5e8d4b9f822f94249a75d8dc477144a3
2024-12-05 12:25:42 +00:00
Pau Espin Pedrol
c963f0b6eb Drop use of libosmo-abis osmocom/abis/ipaccess.h
That header is only really used to provide an old hack for
ipaccess-proxy tool in openbsc.h/osmo-bsc.h, and will be deprecated
soon.

Take the chance to organize a bit better the includes based on dependency
chain.

Change-Id: Ie4540eb6f535375a1e4ddf3849602aecb6260914
2024-11-29 18:55:24 +01:00
Pau Espin Pedrol
a7b3a780ae abis: Fix reusing bts->*_link while it is being destroyed
Call to e1inp_sign_link_destroy() may trigger a sign_down() callback,
which if happening synchronously, could end up reentring the same code
path we are in before bts->*_link was set to NULL.
Avoid it by marking the pointer as NULL immediatelly before calling
e1inp_sign_link_destroy().

Change-Id: Ibc06cdc2d2cd2028b7676fa0c3211ae251cca587
2024-11-22 18:51:20 +01:00
Pau Espin Pedrol
62b37b32f9 abis: Log line and ts nr of signal
Change-Id: I322633a90566dbd4fae10ab6b1fbbedf55907e8b
2024-11-22 18:47:50 +01:00
Pau Espin Pedrol
c211b34955 Fix missing quote char in log line
Change-Id: Ie272a268be8986210f7f6de0d28626789f28f4bb
2024-11-22 17:18:00 +01:00
Pau Espin Pedrol
88f03f8d1d bts-omldummy: print category names instead of hex values
Change-Id: I6082409f25e33cdf4480455bae16b5ac6c5bc1a4
2024-11-22 16:55:35 +01:00
Pau Espin Pedrol
c8946a077e bts-omldummy: Support configuring logging through cmdline
Add cmdline args to control logging, as already present in other more
usual bts models.
This is extremely important here since there's no VTY at all to
configure them.

Change-Id: I0b33919d71bacefe60be192810518fd927625127
2024-11-21 19:08:20 +01:00
Pau Espin Pedrol
8797837deb jenkins.sh: libosmo-netif no longer depends on libosmo-abis
Change-Id: Iaf391691093d84824d99059d1fad98293872db5d
Depends: libosmo-abis.git Change-Id I079dc3999de508301dd37ed03e399356a58d3cab
Depends: libosmo-netif.git Change-Id I13d6e88158f6d9ce017986283183ee9c2cc68cae
2024-11-21 14:46:57 +01:00
Vadim Yanitskiy
5bd8543a5f csd_v110: handle TCH/F14.4
Thanks to the work done by Mychaela (see the related Change-IDs), we
can finally implement handling of TCH/F14.4 (both T and NT modes) for
osmo-bts-trx and osmo-bts-virtual.

This channel mode is special in a way that it employs a different
rate adaptation function RAA' (defined in 3GPP TS 48.020 chapter 11),
which converts between between 290-bit blocks used on Um and Abis-E1
interfaces (E-TRAU frames on the latter) and A-TRAU frames spoken
over the A interface.

The result of function RAA' in the Uplink direction is 320 bits,
which is equal to 4 x 80-bit V.110 frames, but actually carrying
8 x 36-bit packed frames.  These 320 bits are then fed to the RA2
function, like we do for other CSD channel modes.

Change-Id: I3c3bef0bd2f72b8381597b5699e2060165b702a0
Depends: libosmo-abis.git I11fc1529f5be88fa778c7e05cb11eef58a389d40
Depends: libosmo-abis.git I1347a25ce97d5022502ee9112caded66315b09a4
Related: OS#6167
2024-11-19 05:31:29 +07:00
Vadim Yanitskiy
7b712121b8 csd_v110: clarify lchan description for TCH/F14.4
According to 3GPP TS 44.021, section 10.3.2, a radio-interface data
block for a TCH/F14.4 channel consists of eight 36-bit data frames
and two M-bits (290 bits total).  Let's fix our definition.

Change-Id: I4f11eda98420587efa339484b62f46bf19f78809
Related: OS#6167
2024-11-19 05:05:18 +07:00
Vadim Yanitskiy
f33e3c8378 csd_v110: clarify field names in csd_v110_lchan_desc[]
A radio-interface block in GSM carries several modified 36-bit or
60-bit V.110 frames.  Let's rename the fields to avoid confusion.

Change-Id: If97787a64e30ff9b39c26749ba32aed09d3d7983
2024-11-19 05:05:01 +07:00
Vadim Yanitskiy
ff4b83dc23 csd_v110: add CSD_V110_NUM_BITS macro
Change-Id: Iabd2ee5ea9d580e0a6f5c93d681f6be67d1949c5
2024-11-19 04:43:12 +07:00
Vadim Yanitskiy
64d9f142ab csd_v110: use osmo_csd_ra2_* API from libosmotrau
Change-Id: I0be44b15a812397377fe7772b988724dd205737c
Depends: libosmo-abis.git I7b98b958651b3fc1a814d119d1b8644c91f98676
Related: OS#6167
2024-11-14 01:21:16 +07:00
Vadim Yanitskiy
4af496d44c l1sap: l1sap_tch_rts_ind(): fix NULL ptr dereference
The 'resp_msg' will be NULL if msgb_dequeue_count() returns NULL,
i.e. in the case of Downlink queue underrun.  We need to handle
this gracefully and check 'resp_msg' before dereferencing it.

Change-Id: I4e1ea1d1ded2ffb3a07cc06f8b9b5dd922d32ec6
Fixes: 0a34af153 ("CSD NT modes: transmit properly aligned RLP frames on DL")
2024-11-13 19:44:34 +07:00
Vadim Yanitskiy
6f9ccafde4 osmo-bts-trx: fix scheduling of DL FACCH/H for TCH/H4.8 and TCH/H2.4
The mapping sched_tchh_dl_csd_map[] is valid for DL TCH/H4.8 and
TCH/H2.4, but not for DL FACCH/H.  We already use a separate
lookup table sched_tchh_dl_facch_map[] when sending RTS.ind for
DL FACCH/H, so no additional checks are added in this commit.

Change-Id: Idb753fa5c87dc79e9ad19e550680de6f462eed69
Fixes: 95407f3f6 ("osmo-bts-trx: implement CSD scheduling support")
Related: OS#1572, OS#6618
2024-11-09 04:24:33 +07:00
Vadim Yanitskiy
a928fce659 l1sap: make send_ul_rtp_packet_hrdata() NULL-safe
lchan->abis_ip.rtp_socket is NULL before the BSC indicates the RTP
parameters using the CRCX/MDCX procedures.  send_ul_rtp_packet()
does check lchan->abis_ip.rtp_socket against NULL before calling
osmo_rtp_send_frame_ext(), so should send_ul_rtp_packet_hrdata().

Take a chance to make send_ul_rtp_packet_hrdata() more consistent
with the send_ul_rtp_packet() by reordering code a bit.

Change-Id: I05d88a739dc54ca68e50d20b2d0d0b097ae8ca4a
Fixes: 59686cd27 ("CSD: implement half-rate modes correctly")
Related: OS#6618
2024-11-08 18:40:35 +07:00
Mychaela N. Falconia
0a34af1530 CSD NT modes: transmit properly aligned RLP frames on DL
There are two levels of alignment inside clearmode RTP packets
carrying CSData per TS 48.103 section 5.6:

1) Alignment of 2 or 4 V.110 (T) or pseudo-V.110 (NT) frames within
   one RTP packet of 160 octets of an imaginary ISDN B channel;

2) For NT modes only, alignment of 4 pseudo-V.110 frames to form
   a single 240-bit RLP frame.

Per previous patch, alignment 1 is to be treated as mandatory for
RTP transport inside an Osmocom network.  Alignment 2 _could_ be
made mandatory for TCH/F9.6 NT, but the same is not possible for
TCH/[FH]4.8 NT: in the best case of half-alignment, alternating
RTP packets will carry alternating halves of RLP frames.

Implemented solution: allow arbitrary state of alignment 2
(aligned or misaligned) in the incoming RTP stream for all CSD NT
modes, and perform the necessary alignment internally.

This approach is consistent with the world of E1 BTS: a TRAU in
data mode is responsible for alignment 1 (with 20 ms TRAU frames
taking the place of our clearmode RTP packets), but only the BTS can
perform alignment 2, as the TRAU is agnostic to T vs NT distinction.

Related: OS#6579
Change-Id: Idaebfce6da13b23ba265a197502712d83991873e
2024-10-28 17:10:18 +00:00
Mychaela N. Falconia
02951e4af3 cosmetic: move gsmtap_csd_rlp_process() to csd_rlp.c
The next patch in the series will add code for alignment of downlink
RLP frames in CSD NT modes; that code will go into new file csd_rlp.c.
RLP GSMTAP code logically belongs in the same place - hence move it
there in preparation.

Change-Id: I824ce6614c8591cccc5f6bcdde767fe49d551378
2024-10-28 17:10:15 +00:00
Mychaela N. Falconia
7accf7579d cosmetic: eliminate else-after-return in gsmtap_csd_rlp_process()
The code in this function used else-after-return constructs, which
are now rejected by the linter for newly committed code.  This
function needs to be moved to a new source file, which will cause
it to be treated as new code by the linter - hence fix this code
style issue first.

Change-Id: Ide00e819222bb0173eca42ee3714db7f7e1a6d1e
2024-10-28 17:10:13 +00:00
Mychaela N. Falconia
5bed286c6c csd_v110_rtp_decode: preserve E2 & E3 bits for RLP alignment
Modify CSD RTP input path code so incoming bits E2 & E3 from V.110
frames reach the TCH-RTS.ind handler in l1sap.  These bits will be
used in the next patch to ensure proper alignment of DL RLP frames
in non-transparent CSD modes.

Related: OS#6579
Change-Id: I43b97caa6030b9401779998ca5dddc4cfe636e2f
2024-10-28 17:10:11 +00:00
Mychaela N. Falconia
414e1ca8ed CSD RTP: verify alignment of V.110 frames
Since the beginning of CSD implementation, OsmoBTS has operated
with an implicit (unstated) policy that V.110 frames must be
perfectly aligned within received clearmode RTP packets - a requirement
which is NOT set anywhere in TS 48.103 or any of the other specs
it references.  This design policy is sensible from the standpoint
of implementation complexity (both OsmoBTS and osmo_trau2rtp emit
such perfectly aligned packets; if someone is building a gateway
between an Osmocom GSM network and ISDN-style external networks,
that gateway can act as the aligner), but it should be explicit
rather than implicit.

Check V.110 frame alignment in received clearmode RTP packets,
and reject packets that fail this alignment check.

Change-Id: Icd704dc7fa02e60074efc8a29ad7e42ebdf63783
2024-10-28 17:10:09 +00:00
Mychaela N. Falconia
c40e75277f csd_v110: set E2 bit correctly for TCH/[FH]4.8 NT
In all classic NT modes below 14.5 kbit/s, V.110 frame bits E2 and E3
are repurposed to indicate the alignment whereby a single 240-bit RLP
frame is composed of 4 pseudo-V.110 frames in switching transport.
(TS 48.020 section 15.1.)  In the case of TCH/F9.6, setting both E2
and E3 is easy because all 4 pseudo-V.110 frames go out in the same
clearmode RTP packet as a result of a single channel decoding
operation.  However, in the case of TCH/F4.8 there are two separate
channel decoding operations producing two separate RTP packets
20 ms apart.  Furthermore, GSM 05.03 section 3.4.1 specifies which
TDMA frame positions hold which half of the RLP frame - therefore,
the correct emission of bit E2 needs to be based on the TDMA frame
number at which the block was received.

A similar situation occurs with TCH/H4.8 NT: we receive a single
block from the 05.03 decoder every 40 ms, containing a full RLP frame,
but we emit it as two separate RTP packets, each carrying 20 ms worth
of bits.  These RTP packets also require correct setting of E2 bit.

Related: OS#6579
Change-Id: I485af5e01ea87c1721a298a486cd344a17884200
2024-10-28 17:10:07 +00:00
Mychaela N. Falconia
59686cd276 CSD: implement half-rate modes correctly
TCH/H4.8 and TCH/H2.4 modes are different from all other TCH in that
the channel coder runs (statistically) every 40 ms instead of the usual
20 ms.  However, the standard RTP interface of TS 48.103 still requires
20 ms packets for all CSD modes; furtherfore, this requirement is not
just a matter of 3GPP spec being obstinent, but is highly useful for
interoperability, both between FR and HR and between OsmoBTS and E1 BTS.

The handling of CSD HR was totally broken in this regard: wrong RA2
packing mode, RTP clock model was violated, and even an internal
mismatch with l1sap sending TCH.req prims at twice the rate
at which the PHY expects them.

With this patch CSD HR handling becomes correct for TCH/H2.4 and for
TCH/H4.8 transparent; fully correct handling for TCH/H4.8 NT will
appear in a follow-up patch.

The packet/frame rate mismatch (40 ms Rx/Tx times while each RTP packet
carries 20 ms worth of data) is reconciled by emitting two RTP packets
directly back-to-back for UL, and pulling two RTP packets at a time
from the Rx jitter buffer at the needed times for DL.

Note: due to bug OS#6604, TTCN3 tests for CSD half-rate transparent modes
will start failing once this patch is merged; to make them pass again,
TTCN3 test code will need to be reworked to fix that bug.

Related: OS#6577
Change-Id: Ib35e910df263743cd243f51fb0bd6551ddfcf4c5
2024-10-28 17:07:18 +00:00
Vadim Yanitskiy
82d500ccac vty: lchan_dump_full_vty(): print CSD mode
Change-Id: Ibcf489eff73d6d117e94dee06a5a82a3404368f2
Related: OS#1572, OS#6579
2024-10-13 18:58:59 +07:00
Vadim Yanitskiy
aeb78dcc77 rsl: rsl_handle_chan_mod_ie(): set lchan->csd_mode for NT CSD
In commit 66eae187, I skipped assigning LCHAN_CSD_M_NT (0) to
lchan->csd_mode for non-transparent channel modes.  I assumed that
the entire gsm_lchan struct was zero-initialized during activation
or release procedures, but this assumption was incorrect.

In fact, some lchan fields are initialized during activation, while
others are initialized during release.  Specifically, lchan->csd_mode
is zero-initialized when the process starts, and then updated only
for transparent mode requests.  For non-transparent mode requests,
this field is not updated, meaning it retains its previous value.

This bug caused incorrect E1/E2/E3 bit settings and missing GSMTAP
RLP output for channels that were previously used for transparent
CSD calls.  This patch is fixing it.

Change-Id: I793ab4dc25fa852eade6f7a3b67ae961ceb7a093
Fixes: 66eae187 "rsl: rsl_handle_chan_mod_ie(): set lchan->csd_mode"
Related: OS#1572, OS#6579
2024-10-13 17:18:11 +07:00
Vadim Yanitskiy
a880011a5f l1sap: move struct osmo_rlp_frame_decoded to the if-scope
Change-Id: Ide99b35192246b0f7b2a4f31281e2d84984a9795
2024-10-03 18:29:00 +00:00
Vadim Yanitskiy
89415e7433 l1sap: prevent buffer overflow in l1sap_rtp_rx_cb()
Change-Id: I214070ecf7458202922475505a8747950bedf930
Fixes: d1f8f3429 "l1sap: proper rate adaptation for CSD"
2024-10-03 18:28:40 +00:00
Vadim Yanitskiy
332976672e tests/csd: add NT variants for TCH/F4.8 and TCH/F9.6
The existing tests are all for T (transparent mode).
Add NT (non-transparent mode) variants.

Change-Id: Ie335bc0623dd7e887a0b3b1c40d61153b84924b2
Related: OS#1572
2024-09-27 02:53:38 +07:00
Mychaela N. Falconia
9512355ebc sysmo: generate empty TCH/H payload on FACCH/H Rx
When sysmoBTS PHY decodes TCH UL Rx as FACCH, it sends only one
PH-DATA.ind for the FACCH, but none for TCH.  This case was handled
correctly for TCH/F in 2023-03, but FACCH/H has not been handled
correctly until now.

Change-Id: Id6d966348fb2be084485279e75480f98f46e464b
2024-07-28 23:12:06 +00:00
41 changed files with 1429 additions and 425 deletions

View File

@@ -69,15 +69,15 @@ then
fi fi
dnl checks for libraries dnl checks for libraries
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.10.0) PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.10.0) PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.10.0) PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.10.0) PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec >= 1.10.0) PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.10.0) PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.6.0) PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 2.0.0)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.6.0) PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 2.0.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.5.0) PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.6.0)
AC_MSG_CHECKING([whether to enable support for sysmobts calibration tool]) AC_MSG_CHECKING([whether to enable support for sysmobts calibration tool])
AC_ARG_ENABLE(sysmobts-calib, AC_ARG_ENABLE(sysmobts-calib,

View File

@@ -8,9 +8,8 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib" export LD_LIBRARY_PATH="$inst/lib"
osmo-build-dep.sh libosmocore "" --disable-doxygen osmo-build-dep.sh libosmocore "" --disable-doxygen
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
osmo-build-dep.sh libosmo-netif "" --disable-doxygen osmo-build-dep.sh libosmo-netif "" --disable-doxygen
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
cd "$deps" cd "$deps"

View File

@@ -4,13 +4,12 @@
# shellcheck source=contrib/jenkins_common.sh # shellcheck source=contrib/jenkins_common.sh
. $(dirname "$0")/jenkins_common.sh . $(dirname "$0")/jenkins_common.sh
osmo-build-dep.sh libosmocore "" --disable-doxygen
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib" export LD_LIBRARY_PATH="$inst/lib"
osmo-build-dep.sh libosmo-abis "" --disable-dahdi osmo-build-dep.sh libosmocore "" --disable-doxygen
osmo-build-dep.sh libosmo-netif "" --disable-doxygen osmo-build-dep.sh libosmo-netif "" --disable-doxygen
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
cd "$deps" cd "$deps"
osmo-layer1-headers.sh lc15 "$FIRMWARE_VERSION" osmo-layer1-headers.sh lc15 "$FIRMWARE_VERSION"

View File

@@ -4,13 +4,12 @@
# shellcheck source=contrib/jenkins_common.sh # shellcheck source=contrib/jenkins_common.sh
. $(dirname "$0")/jenkins_common.sh . $(dirname "$0")/jenkins_common.sh
osmo-build-dep.sh libosmocore "" --disable-doxygen
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib" export LD_LIBRARY_PATH="$inst/lib"
osmo-build-dep.sh libosmo-abis "" --disable-dahdi osmo-build-dep.sh libosmocore "" --disable-doxygen
osmo-build-dep.sh libosmo-netif "" --disable-doxygen osmo-build-dep.sh libosmo-netif "" --disable-doxygen
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
cd "$deps" cd "$deps"
osmo-layer1-headers.sh oc2g "$FIRMWARE_VERSION" osmo-layer1-headers.sh oc2g "$FIRMWARE_VERSION"

View File

@@ -4,13 +4,12 @@
# shellcheck source=contrib/jenkins_common.sh # shellcheck source=contrib/jenkins_common.sh
. $(dirname "$0")/jenkins_common.sh . $(dirname "$0")/jenkins_common.sh
osmo-build-dep.sh libosmocore "" --disable-doxygen
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib" export LD_LIBRARY_PATH="$inst/lib"
osmo-build-dep.sh libosmo-abis "" --disable-dahdi osmo-build-dep.sh libosmocore "" --disable-doxygen
osmo-build-dep.sh libosmo-netif "" --disable-doxygen osmo-build-dep.sh libosmo-netif "" --disable-doxygen
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
cd "$deps" cd "$deps"
osmo-layer1-headers.sh oct "$FIRMWARE_VERSION" osmo-layer1-headers.sh oct "$FIRMWARE_VERSION"

View File

@@ -8,9 +8,8 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib" export LD_LIBRARY_PATH="$inst/lib"
osmo-build-dep.sh libosmocore "" --disable-doxygen osmo-build-dep.sh libosmocore "" --disable-doxygen
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
osmo-build-dep.sh libosmo-netif "" --disable-doxygen osmo-build-dep.sh libosmo-netif "" --disable-doxygen
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
cd "$deps" cd "$deps"

View File

@@ -4,13 +4,12 @@
# shellcheck source=contrib/jenkins_common.sh # shellcheck source=contrib/jenkins_common.sh
. $(dirname "$0")/jenkins_common.sh . $(dirname "$0")/jenkins_common.sh
osmo-build-dep.sh libosmocore "" --disable-doxygen
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib" export LD_LIBRARY_PATH="$inst/lib"
osmo-build-dep.sh libosmo-abis "" --disable-dahdi osmo-build-dep.sh libosmocore "" --disable-doxygen
osmo-build-dep.sh libosmo-netif "" --disable-doxygen osmo-build-dep.sh libosmo-netif "" --disable-doxygen
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
cd "$deps" cd "$deps"
osmo-layer1-headers.sh sysmo "$FIRMWARE_VERSION" osmo-layer1-headers.sh sysmo "$FIRMWARE_VERSION"

41
debian/changelog vendored
View File

@@ -1,3 +1,44 @@
osmo-bts (1.9.0) unstable; urgency=medium
[ Mychaela N. Falconia ]
* sysmo: generate empty TCH/H payload on FACCH/H Rx
* CSD: implement half-rate modes correctly
* csd_v110: set E2 bit correctly for TCH/[FH]4.8 NT
* CSD RTP: verify alignment of V.110 frames
* csd_v110_rtp_decode: preserve E2 & E3 bits for RLP alignment
* cosmetic: eliminate else-after-return in gsmtap_csd_rlp_process()
* cosmetic: move gsmtap_csd_rlp_process() to csd_rlp.c
* CSD NT modes: transmit properly aligned RLP frames on DL
[ Vadim Yanitskiy ]
* tests/csd: add NT variants for TCH/F4.8 and TCH/F9.6
* l1sap: prevent buffer overflow in l1sap_rtp_rx_cb()
* l1sap: move struct osmo_rlp_frame_decoded to the if-scope
* rsl: rsl_handle_chan_mod_ie(): set lchan->csd_mode for NT CSD
* vty: lchan_dump_full_vty(): print CSD mode
* l1sap: make send_ul_rtp_packet_hrdata() NULL-safe
* osmo-bts-trx: fix scheduling of DL FACCH/H for TCH/H4.8 and TCH/H2.4
* l1sap: l1sap_tch_rts_ind(): fix NULL ptr dereference
* csd_v110: use osmo_csd_ra2_* API from libosmotrau
* csd_v110: add CSD_V110_NUM_BITS macro
* csd_v110: clarify field names in csd_v110_lchan_desc[]
* csd_v110: clarify lchan description for TCH/F14.4
* csd_v110: handle TCH/F14.4
[ Pau Espin Pedrol ]
* jenkins.sh: libosmo-netif no longer depends on libosmo-abis
* bts-omldummy: Support configuring logging through cmdline
* bts-omldummy: print category names instead of hex values
* Fix missing quote char in log line
* abis: Log line and ts nr of signal
* abis: Fix reusing bts->*_link while it is being destroyed
* Drop use of libosmo-abis osmocom/abis/ipaccess.h
[ Philipp Maier ]
* pcu_sock: do not receive a TXT ind. with PCU_VERSION for a specific BTS
-- Oliver Smith <osmith@sysmocom.de> Wed, 12 Feb 2025 12:56:09 +0100
osmo-bts (1.8.0) unstable; urgency=medium osmo-bts (1.8.0) unstable; urgency=medium
[ Vadim Yanitskiy ] [ Vadim Yanitskiy ]

6
debian/control vendored
View File

@@ -7,9 +7,9 @@ Build-Depends: debhelper (>= 10),
dh-autoreconf, dh-autoreconf,
autotools-dev, autotools-dev,
pkg-config, pkg-config,
libosmocore-dev (>= 1.10.0), libosmocore-dev (>= 1.11.0),
libosmo-abis-dev (>= 1.6.0), libosmo-abis-dev (>= 2.0.0),
libosmo-netif-dev (>= 1.5.0), libosmo-netif-dev (>= 1.6.0),
libgps-dev, libgps-dev,
txt2man, txt2man,
osmo-gsm-manuals-dev (>= 1.6.0) osmo-gsm-manuals-dev (>= 1.6.0)

View File

@@ -24,6 +24,7 @@ noinst_HEADERS = \
tx_power.h \ tx_power.h \
control_if.h \ control_if.h \
cbch.h \ cbch.h \
csd_rlp.h \
csd_v110.h \ csd_v110.h \
l1sap.h \ l1sap.h \
lchan.h \ lchan.h \

View File

@@ -0,0 +1,36 @@
/*
* Declarations for functions in csd_rlp.c: alignment of downlink RLP frames
* and RLP GSMTAP mechanism for CSD NT modes.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <osmocom/core/bits.h>
#include <osmocom/gsm/l1sap.h>
#include <osmo-bts/lchan.h>
extern const uint8_t csd_tchf48_nt_e2_map[26];
/* Per TS 48.020 section 15.1, the cadence of E2+E3 bits in a properly
* aligned sequence of pseudo-V.110 frames forming a single RLP frame
* is 00-01-10-11. The following constant captures this bit sequence
* in hex, for comparison against align_bits output from
* csd_v110_rtp_decode() or against rlpdl_align_bits accumulator
* in CSD NT lchan state.
*/
#define NTCSD_ALIGNED_EBITS 0x1B
void ntcsd_dl_reset(struct gsm_lchan *lchan);
void ntcsd_dl_input_48(struct gsm_lchan *lchan, const ubit_t *data_bits,
uint8_t align_bits);
void ntcsd_dl_input_96(struct gsm_lchan *lchan, const ubit_t *data_bits,
uint8_t align_bits);
bool ntcsd_dl_output(struct gsm_lchan *lchan, ubit_t *rlp_frame_out);
void gsmtap_csd_rlp_process(struct gsm_lchan *lchan, bool is_uplink,
const struct ph_tch_param *tch_ind,
const ubit_t *data, unsigned int data_len);
void gsmtap_csd_rlp_dl(struct gsm_lchan *lchan, uint32_t fn,
const ubit_t *data, unsigned int data_len);

View File

@@ -5,19 +5,20 @@
struct gsm_lchan; struct gsm_lchan;
struct csd_v110_frame_desc {
uint16_t num_blocks;
uint16_t num_bits;
};
struct csd_v110_lchan_desc { struct csd_v110_lchan_desc {
struct csd_v110_frame_desc fr; uint16_t num_frames; /* number of V.110 frames in a radio block */
struct csd_v110_frame_desc hr; uint16_t num_frame_bits; /* number of bits in each V.110 frame */
uint16_t num_other_bits; /* number of other bits (e.g. M-bits for TCH/F14.4) */
uint8_t ra2_ir; /* intermediate rate (8 or 16 kbit/s) for RA2 step */
}; };
extern const struct csd_v110_lchan_desc csd_v110_lchan_desc[256]; extern const struct csd_v110_lchan_desc csd_v110_lchan_desc[256];
#define CSD_V110_NUM_BITS(desc) \
((desc)->num_frames * (desc)->num_frame_bits + (desc)->num_other_bits)
int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp, int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp,
const uint8_t *data, size_t data_len); const uint8_t *data, size_t data_len,
uint8_t nt48_half_num);
int csd_v110_rtp_decode(const struct gsm_lchan *lchan, uint8_t *data, int csd_v110_rtp_decode(const struct gsm_lchan *lchan, uint8_t *data,
const uint8_t *rtp, size_t rtp_len); uint8_t *align_bits, const uint8_t *rtp, size_t rtp_len);

View File

@@ -4,6 +4,7 @@
#include <stdint.h> #include <stdint.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/timer.h> #include <osmocom/core/timer.h>
#include <osmocom/core/linuxlist.h> #include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h> #include <osmocom/core/logging.h>
@@ -88,6 +89,12 @@ enum lchan_csd_mode {
_LCHAN_CSD_M_NUM, _LCHAN_CSD_M_NUM,
}; };
extern const struct value_string lchan_csd_mode_descs[];
static inline const char *lchan_csd_mode_desc(enum lchan_csd_mode mode)
{
return get_value_string(lchan_csd_mode_descs, mode);
}
/* State of the SAPIs in the lchan */ /* State of the SAPIs in the lchan */
enum lchan_sapi_state { enum lchan_sapi_state {
LCHAN_SAPI_S_NONE, LCHAN_SAPI_S_NONE,
@@ -285,13 +292,21 @@ struct gsm_lchan {
bool dl_sid_transmitted; bool dl_sid_transmitted;
/* The current frame in the DL is taken up by FACCH */ /* The current frame in the DL is taken up by FACCH */
bool dl_facch_stealing; bool dl_facch_stealing;
/* UL SID filter to catch DTXu half-blocks,
* see tch_ul_fr_hr_efr() function. */
bool ul_sid_filter;
} dtx_fr_hr_efr; } dtx_fr_hr_efr;
uint8_t last_cmr; uint8_t last_cmr;
uint32_t last_fn; uint32_t last_fn;
struct { struct {
/* buffers to re-combine RLP frame from multiple Um blocks */ /* RLP GSMTAP mechanism */
uint8_t rlp_buf_ul[576/8]; /* maximum size of RLP frame */ uint8_t rlp_buf_ul[576/8]; /* maximum size of RLP frame */
uint8_t rlp_buf_dl[576/8]; /* maximum size of RLP frame */ uint8_t rlp_buf_dl[576/8]; /* maximum size of RLP frame */
/* alignment of RLP frames in DL for NT modes */
ubit_t rlpdl_data_bits[60 * 7];
uint16_t rlpdl_align_bits;
uint8_t rlpdl_fill_level;
ubit_t tchf48_nt_2ndhalf[120];
} csd; } csd;
} tch; } tch;

View File

@@ -13,17 +13,49 @@
struct msgb; struct msgb;
/* Access 1st part of msgb control buffer */ /****************************************************************
* Accessor macros for control buffer words in RTP input path (DL)
*****************************************************************/
/* Storing RTP header fields in the path from RTP and Osmux
* Rx callback functions to TCH-RTS.ind handling.
* FIXME: do we really need this RTP header info downstream
* of the jitter buffer mechanism in the RTP endpoint library?
*/
#define rtpmsg_marker_bit(x) ((x)->cb[0]) #define rtpmsg_marker_bit(x) ((x)->cb[0])
#define rtpmsg_seq(x) ((x)->cb[1])
#define rtpmsg_ts(x) ((x)->cb[2])
/* Access 2nd part of msgb control buffer */ /* l1sap_rtp_rx_cb() does some preening or preparsing on some
#define rtpmsg_seq(x) ((x)->cb[1]) * RTP payloads, and in two cases (HR with RFC 5993 input and
* CSD NT modes) this preparsing step produces some metadata
/* Access 3rd part of msgb control buffer */ * that need to be passed to TCH-RTS.ind handling.
#define rtpmsg_ts(x) ((x)->cb[2]) */
/* Access 4th part of msgb control buffer */
#define rtpmsg_is_rfc5993_sid(x) ((x)->cb[3]) #define rtpmsg_is_rfc5993_sid(x) ((x)->cb[3])
#define rtpmsg_csd_align_bits(x) ((x)->cb[4])
/********************************************************
* Accessor macros for control buffer words in TCH UL path
*********************************************************/
/* We provide an ability for BTS models to indicate BFI along with payload
* bits just like in GSM 08.60 TRAU-UL frames, and the same BFI flag can
* then be set by model-independent functions for higher-level BFI
* conditions. This cb word shall act as a Boolean flag.
*/
#define tch_ul_msg_bfi(x) ((x)->cb[0])
/* For HRv1 codec, we have to pass SID classification from the function
* that makes the initial determination to TS 101 318, RFC 5993 and
* TW-TS-002 output functions. Per classic GSM specs, common across
* FR/HR/EFR, SID classification code is an integer equal to 0, 1 or 2;
* in Osmocom it is enum osmo_gsm631_sid_class.
*
* NOTE: while the actual SID ternary classification exists in exactly
* the same form across all 3 of FR/HR/EFR, we store it in a cb word
* only for HR codec where we need it for RTP output functions.
*/
#define tch_ul_msg_hr_sid(x) ((x)->cb[1])
/** /**
* Classification of OML message. ETSI for plain GSM 12.21 * Classification of OML message. ETSI for plain GSM 12.21

View File

@@ -51,6 +51,7 @@ libbts_a_SOURCES = \
bts_ctrl_commands.c \ bts_ctrl_commands.c \
bts_ctrl_lookup.c \ bts_ctrl_lookup.c \
bts_shutdown_fsm.c \ bts_shutdown_fsm.c \
csd_rlp.c \
csd_v110.c \ csd_v110.c \
l1sap.c \ l1sap.c \
cbch.c \ cbch.c \

View File

@@ -39,10 +39,10 @@
#include <osmocom/core/signal.h> #include <osmocom/core/signal.h>
#include <osmocom/core/macaddr.h> #include <osmocom/core/macaddr.h>
#include <osmocom/core/fsm.h> #include <osmocom/core/fsm.h>
#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmocom/gsm/ipa.h>
#include <osmocom/abis/abis.h> #include <osmocom/abis/abis.h>
#include <osmocom/abis/e1_input.h> #include <osmocom/abis/e1_input.h>
#include <osmocom/abis/ipaccess.h>
#include <osmocom/gsm/ipa.h>
#include <osmo-bts/abis.h> #include <osmo-bts/abis.h>
#include <osmo-bts/logging.h> #include <osmo-bts/logging.h>
@@ -87,10 +87,17 @@ struct abis_link_fsm_priv {
static void reset_oml_link(struct gsm_bts *bts) static void reset_oml_link(struct gsm_bts *bts)
{ {
struct e1inp_sign_link *link;
if (bts->oml_link) { if (bts->oml_link) {
struct timespec now; struct timespec now;
e1inp_sign_link_destroy(bts->oml_link); /* Mark bts->oml_link ptr null before calling sign_link_destroy,
* to avoid a callback triggering this same code path. */
link = bts->oml_link;
bts->oml_link = NULL;
e1inp_sign_link_destroy(link);
/* Log a special notice if the OML connection was dropped relatively quickly. */ /* Log a special notice if the OML connection was dropped relatively quickly. */
if (bts->oml_conn_established_timestamp.tv_sec != 0 && clock_gettime(CLOCK_MONOTONIC, &now) == 0 && if (bts->oml_conn_established_timestamp.tv_sec != 0 && clock_gettime(CLOCK_MONOTONIC, &now) == 0 &&
@@ -100,14 +107,16 @@ static void reset_oml_link(struct gsm_bts *bts)
"A common error is a mismatch between unit_id configuration parameters of BTS and BSC.\n", "A common error is a mismatch between unit_id configuration parameters of BTS and BSC.\n",
(uint64_t) (now.tv_sec - bts->oml_conn_established_timestamp.tv_sec)); (uint64_t) (now.tv_sec - bts->oml_conn_established_timestamp.tv_sec));
} }
bts->oml_link = NULL;
} }
memset(&bts->oml_conn_established_timestamp, 0, sizeof(bts->oml_conn_established_timestamp)); memset(&bts->oml_conn_established_timestamp, 0, sizeof(bts->oml_conn_established_timestamp));
/* Same for IPAC_PROTO_OSMO on the same ipa connection: */ /* Same for IPAC_PROTO_OSMO on the same ipa connection: */
if (bts->osmo_link) { if (bts->osmo_link) {
e1inp_sign_link_destroy(bts->osmo_link); /* Mark bts->osmo_link ptr null before calling sign_link_destroy,
* to avoid a callback triggering this same code path. */
link = bts->osmo_link;
bts->osmo_link = NULL; bts->osmo_link = NULL;
e1inp_sign_link_destroy(link);
} }
} }
@@ -226,8 +235,11 @@ static void abis_link_connected(struct osmo_fsm_inst *fi, uint32_t event, void *
/* Then iterate over the RSL signalling links */ /* Then iterate over the RSL signalling links */
llist_for_each_entry(trx, &bts->trx_list, list) { llist_for_each_entry(trx, &bts->trx_list, list) {
if (trx->bb_transc.rsl.link) { if (trx->bb_transc.rsl.link) {
e1inp_sign_link_destroy(trx->bb_transc.rsl.link); /* Mark link ptr null before calling sign_link_destroy,
* to avoid a callback triggering this same code path. */
struct e1inp_sign_link *link = trx->bb_transc.rsl.link;
trx->bb_transc.rsl.link = NULL; trx->bb_transc.rsl.link = NULL;
e1inp_sign_link_destroy(link);
if (trx == trx->bts->c0) if (trx == trx->bts->c0)
load_timer_stop(trx->bts); load_timer_stop(trx->bts);
} else { } else {
@@ -476,8 +488,11 @@ static int inp_s_cbfn(unsigned int subsys, unsigned int signal,
return 0; return 0;
struct input_signal_data *isd = signal_data; struct input_signal_data *isd = signal_data;
DEBUGP(DABIS, "Input Signal %s received for link_type=%s\n", DEBUGP(DABIS, "Input Signal %s received for ts-%u-%u link_type=%s\n",
get_value_string(e1inp_signal_names, signal), e1inp_signtype_name(isd->link_type)); get_value_string(e1inp_signal_names, signal),
isd->line ? isd->line->num : -1,
isd->ts_nr,
e1inp_signtype_name(isd->link_type));
return 0; return 0;
} }

View File

@@ -394,6 +394,7 @@ int bts_init(struct gsm_bts *bts)
osmo_bts_set_feature(bts->features, BTS_FEAT_IPV6_NSVC); osmo_bts_set_feature(bts->features, BTS_FEAT_IPV6_NSVC);
osmo_bts_set_feature(bts->features, BTS_FEAT_PAGING_COORDINATION); osmo_bts_set_feature(bts->features, BTS_FEAT_PAGING_COORDINATION);
osmo_bts_set_feature(bts->features, BTS_FEAT_TWTS001); osmo_bts_set_feature(bts->features, BTS_FEAT_TWTS001);
osmo_bts_set_feature(bts->features, BTS_FEAT_TWTS002);
/* Maximum TA supported by the PHY (can be overridden by PHY specific code) */ /* Maximum TA supported by the PHY (can be overridden by PHY specific code) */
bts->support.max_ta = MAX_TA_DEF; bts->support.max_ta = MAX_TA_DEF;
@@ -896,8 +897,8 @@ static bool bts_supports_cm_data(const struct gsm_bts *bts,
switch (bts->variant) { switch (bts->variant) {
case BTS_OSMO_TRX: case BTS_OSMO_TRX:
switch (cm->chan_rate) { switch (cm->chan_rate) {
/* TODO: RSL_CMOD_CSD_NT_14k5 */ case RSL_CMOD_CSD_NT_14k5:
/* TODO: RSL_CMOD_CSD_T_14k4 */ case RSL_CMOD_CSD_T_14k4:
case RSL_CMOD_CSD_NT_12k0: case RSL_CMOD_CSD_NT_12k0:
case RSL_CMOD_CSD_T_9k6: case RSL_CMOD_CSD_T_9k6:
if (cm->chan_rt != RSL_CMOD_CRT_TCH_Bm) if (cm->chan_rt != RSL_CMOD_CRT_TCH_Bm)

228
src/common/csd_rlp.c Normal file
View File

@@ -0,0 +1,228 @@
/* This module has been split from l1sap.c; original header comments preserved:
*
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/l1sap.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/rsl.h>
#include <osmocom/gsm/rlp.h>
#include <osmocom/gsm/rtp_extensions.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/utils.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/lchan.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/csd_rlp.h>
/* In the case of TCH/F4.8 NT, each 240-bit RLP frame is split between
* two channel-coding blocks of 120 bits each. We need to know which
* frame numbers correspond to which half: in the UL-to-RTP path we have
* to set bit E2 based on the TDMA frame number at which we received the
* block in question, and in the DL direction we have to transmit the
* right half at the right time.
*
* See GSM 05.03 section 3.4.1 and the mapping tables of GSM 05.02;
* having "e2_map" in the array name shall serve as a mnemonic as to
* the sense of this array: 0 means 1st half and 1 means 2nd half,
* exactly as the value of bit E2 per TS 48.020 section 15.1.
*/
const uint8_t csd_tchf48_nt_e2_map[26] = {
[4] = 1, /* B1 position */
[13] = 1, /* B3 position */
[21] = 1, /* B5 position */
};
/* This function resets (clears) the state of the DL alignment buffer.
* It needs to be called when we encounter a gap (packet loss, invalid
* packets, etc) in our RTP input stream. */
void ntcsd_dl_reset(struct gsm_lchan *lchan)
{
lchan->tch.csd.rlpdl_fill_level = 0;
}
/* This function is to be called with the decoded content of a single
* incoming RTP packet (data and alignment bits) for TCH/[FH]4.8 NT. */
void ntcsd_dl_input_48(struct gsm_lchan *lchan, const ubit_t *data_bits,
uint8_t align_bits)
{
memmove(lchan->tch.csd.rlpdl_data_bits,
lchan->tch.csd.rlpdl_data_bits + 60 * 2, 60 * 5);
memcpy(lchan->tch.csd.rlpdl_data_bits + 60 * 5, data_bits, 60 * 2);
lchan->tch.csd.rlpdl_align_bits <<= 4;
lchan->tch.csd.rlpdl_align_bits |= (align_bits & 0xF);
lchan->tch.csd.rlpdl_fill_level += 2;
if (lchan->tch.csd.rlpdl_fill_level > 7)
lchan->tch.csd.rlpdl_fill_level = 7;
}
/* This function is to be called with the decoded content of a single
* incoming RTP packet (data and alignment bits) for TCH/F9.6 NT. */
void ntcsd_dl_input_96(struct gsm_lchan *lchan, const ubit_t *data_bits,
uint8_t align_bits)
{
memmove(lchan->tch.csd.rlpdl_data_bits,
lchan->tch.csd.rlpdl_data_bits + 60 * 4, 60 * 3);
memcpy(lchan->tch.csd.rlpdl_data_bits + 60 * 3, data_bits, 60 * 4);
lchan->tch.csd.rlpdl_align_bits <<= 8;
lchan->tch.csd.rlpdl_align_bits |= (align_bits & 0xFF);
lchan->tch.csd.rlpdl_fill_level += 4;
if (lchan->tch.csd.rlpdl_fill_level > 7)
lchan->tch.csd.rlpdl_fill_level = 7;
}
/* This function is to be called to obtain a complete RLP frame for
* downlink transmission. It will provide either a properly aligned
* frame (return value true) or a filler (return value false). */
bool ntcsd_dl_output(struct gsm_lchan *lchan, ubit_t *rlp_frame_out)
{
if (lchan->tch.csd.rlpdl_fill_level < 4)
goto no_frame_out;
if (((lchan->tch.csd.rlpdl_align_bits >> 0) & 0xFF) == NTCSD_ALIGNED_EBITS) {
memcpy(rlp_frame_out, lchan->tch.csd.rlpdl_data_bits + 60 * 3,
60 * 4);
return true;
}
if (lchan->tch.csd.rlpdl_fill_level < 5)
goto no_frame_out;
if (((lchan->tch.csd.rlpdl_align_bits >> 2) & 0xFF) == NTCSD_ALIGNED_EBITS) {
memcpy(rlp_frame_out, lchan->tch.csd.rlpdl_data_bits + 60 * 2,
60 * 4);
return true;
}
if (lchan->tch.csd.rlpdl_fill_level < 6)
goto no_frame_out;
if (((lchan->tch.csd.rlpdl_align_bits >> 4) & 0xFF) == NTCSD_ALIGNED_EBITS) {
memcpy(rlp_frame_out, lchan->tch.csd.rlpdl_data_bits + 60 * 1,
60 * 4);
return true;
}
if (lchan->tch.csd.rlpdl_fill_level < 7)
goto no_frame_out;
if (((lchan->tch.csd.rlpdl_align_bits >> 6) & 0xFF) == NTCSD_ALIGNED_EBITS) {
memcpy(rlp_frame_out, lchan->tch.csd.rlpdl_data_bits, 60 * 4);
return true;
}
no_frame_out:
/* TS 44.021 section 12.1 says that a missing/unavailable 240-bit
* RLP frame is to be filled with 0 bits, unlike ones-fill
* used everywhere else in the world of V.110 and CSD. */
memset(rlp_frame_out, 0, 60 * 4);
return false;
}
/* process one MAC block of unpacked bits of a non-transparent CSD channel */
void gsmtap_csd_rlp_process(struct gsm_lchan *lchan, bool is_uplink,
const struct ph_tch_param *tch_ind,
const ubit_t *data, unsigned int data_len)
{
struct gsm_bts_trx *trx = lchan->ts->trx;
struct gsmtap_inst *inst = trx->bts->gsmtap.inst;
pbit_t *rlp_buf;
uint16_t arfcn;
int byte_len;
if (!inst || !trx->bts->gsmtap.rlp)
return;
if (lchan->csd_mode != LCHAN_CSD_M_NT)
return;
if (is_uplink)
rlp_buf = lchan->tch.csd.rlp_buf_ul;
else
rlp_buf = lchan->tch.csd.rlp_buf_dl;
/* TCH/F 9.6: 4x60bit block => 240bit RLP frame
* TCH/F 4.8: 2x 2x60bit blocks starting at B0/B2/B4 => 240bit RLP frame
* TCH/H 4.8: 4x60bit block => 240bit RLP frame
* TCH/F 2.4: 2x36bit blocks => transparent only
* TCH/H 2.4: 4x36bit blocks => transparent only
* TCH/F 14.4: 2x 290 bit block (starting with M1=0) => 576-bit RLP frame
*/
if (lchan->type == GSM_LCHAN_TCH_F &&
lchan->tch_mode == GSM48_CMODE_DATA_6k0 && is_uplink) {
/* In this mode we have 120-bit MAC blocks; two of them need
* to be concatenated to render a 240-bit RLP frame. The first
* block is present in B0/B2/B4, and we have to use FN to
* detect this position.
* This code path is only for UL: in the case of DL,
* alignment logic elsewhere in the code will present us
* with a fully assembled RLP frame. */
OSMO_ASSERT(data_len == 120);
if (csd_tchf48_nt_e2_map[tch_ind->fn % 26] == 0) {
osmo_ubit2pbit_ext(rlp_buf, 0, data, 0, data_len, 1);
return;
}
osmo_ubit2pbit_ext(rlp_buf, 120, data, 0, data_len, 1);
byte_len = 240/8;
} else if (lchan->type == GSM_LCHAN_TCH_F && lchan->tch_mode == GSM48_CMODE_DATA_14k5) {
/* in this mode we have 290bit MAC blocks containing M1, M2 and 288 data bits;
* two of them need to be concatenated to render a
* 576-bit RLP frame. The start of a RLP frame is
* denoted by a block with M1-bit set to 0. */
OSMO_ASSERT(data_len == 290);
ubit_t m1 = data[0];
if (m1 == 0) {
osmo_ubit2pbit_ext(rlp_buf, 0, data, 2, data_len, 1);
return;
}
osmo_ubit2pbit_ext(rlp_buf, 288, data, 2, data_len, 1);
byte_len = 576/8;
} else {
byte_len = osmo_ubit2pbit_ext(rlp_buf, 0, data, 0, data_len, 1);
}
if (trx->bts->gsmtap.rlp_skip_null) {
struct osmo_rlp_frame_decoded rlpf;
int rc = osmo_rlp_decode(&rlpf, 0, rlp_buf, byte_len);
if (rc == 0 && rlpf.ftype == OSMO_RLP_FT_U && rlpf.u_ftype == OSMO_RLP_U_FT_NULL)
return;
}
arfcn = trx->arfcn;
if (is_uplink)
arfcn |= GSMTAP_ARFCN_F_UPLINK;
gsmtap_send_ex(inst, GSMTAP_TYPE_GSM_RLP, arfcn, lchan->ts->nr,
lchan->type == GSM_LCHAN_TCH_H ? GSMTAP_CHANNEL_VOICE_H : GSMTAP_CHANNEL_VOICE_F,
lchan->nr, tch_ind->fn, tch_ind->rssi, 0, rlp_buf, byte_len);
}
/* wrapper for downlink path */
void gsmtap_csd_rlp_dl(struct gsm_lchan *lchan, uint32_t fn,
const ubit_t *data, unsigned int data_len)
{
/* 'fake' tch_ind containing all-zero so gsmtap code can be shared
* between UL and DL */
const struct ph_tch_param fake_tch_ind = { .fn = fn };
gsmtap_csd_rlp_process(lchan, false, &fake_tch_ind, data, data_len);
}

View File

@@ -20,6 +20,7 @@
*/ */
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <errno.h> #include <errno.h>
#include <osmocom/core/bits.h> #include <osmocom/core/bits.h>
@@ -28,33 +29,38 @@
#include <osmocom/gsm/gsm_utils.h> #include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/isdn/v110.h> #include <osmocom/isdn/v110.h>
#include <osmocom/trau/csd_ra2.h>
#include <osmocom/trau/csd_raa_prime.h>
#include <osmo-bts/csd_v110.h> #include <osmo-bts/csd_v110.h>
#include <osmo-bts/lchan.h> #include <osmo-bts/lchan.h>
/* key is enum gsm48_chan_mode, so assuming a value in range 0..255 */ /* key is enum gsm48_chan_mode, so assuming a value in range 0..255 */
const struct csd_v110_lchan_desc csd_v110_lchan_desc[256] = { const struct csd_v110_lchan_desc csd_v110_lchan_desc[256] = {
#if 0
[GSM48_CMODE_DATA_14k5] = { [GSM48_CMODE_DATA_14k5] = {
/* TCH/F14.4: 290 bits every 20 ms (14.5 kbit/s) */ /* TCH/F14.4: 8 * 36 + 2 bits every 20 ms (14.5 kbit/s) */
.fr = { .num_blocks = 1, .num_bits = 290 }, .num_frames = 8,
.num_frame_bits = 36, /* D-bits */
.num_other_bits = 2, /* M-bits */
.ra2_ir = 16,
}, },
#endif
[GSM48_CMODE_DATA_12k0] = { [GSM48_CMODE_DATA_12k0] = {
/* TCH/F9.6: 4 * 60 bits every 20 ms (12.0 kbit/s) */ /* TCH/F9.6: 4 * 60 bits every 20 ms (12.0 kbit/s) */
.fr = { .num_blocks = 4, .num_bits = 60 }, .num_frames = 4,
.num_frame_bits = 60,
.ra2_ir = 16,
}, },
[GSM48_CMODE_DATA_6k0] = { [GSM48_CMODE_DATA_6k0] = {
/* TCH/F4.8: 2 * 60 bits every 20 ms (6.0 kbit/s) */ /* TCH/[FH]4.8: 2 * 60 bits every 20 ms (6.0 kbit/s) */
.fr = { .num_blocks = 2, .num_bits = 60 }, .num_frames = 2,
/* TCH/H4.8: 4 * 60 bits every 40 ms (6.0 kbit/s) */ .num_frame_bits = 60,
.hr = { .num_blocks = 4, .num_bits = 60 }, .ra2_ir = 8,
}, },
[GSM48_CMODE_DATA_3k6] = { [GSM48_CMODE_DATA_3k6] = {
/* TCH/F2.4: 2 * 36 bits every 20 ms (3.6 kbit/s) */ /* TCH/[FH]2.4: 2 * 36 bits every 20 ms (3.6 kbit/s) */
.fr = { .num_blocks = 2, .num_bits = 36 }, .num_frames = 2,
/* TCH/H2.4: 4 * 36 bits every 40 ms (3.6 kbit/s) */ .num_frame_bits = 36,
.hr = { .num_blocks = 4, .num_bits = 36 }, .ra2_ir = 8,
}, },
}; };
@@ -74,44 +80,74 @@ static const uint8_t e1e2e3_map[_LCHAN_CSD_M_NUM][3] = {
}; };
int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp, int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp,
const uint8_t *data, size_t data_len) const uint8_t *data, size_t data_len,
uint8_t nt48_half_num)
{ {
const struct csd_v110_frame_desc *desc; const struct csd_v110_lchan_desc *desc;
ubit_t ra_bits[80 * 4]; ubit_t ra_bits[80 * 4];
OSMO_ASSERT(lchan->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc)); OSMO_ASSERT(lchan->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc));
if (lchan->type == GSM_LCHAN_TCH_F) desc = &csd_v110_lchan_desc[lchan->tch_mode];
desc = &csd_v110_lchan_desc[lchan->tch_mode].fr; if (OSMO_UNLIKELY(desc->num_frames == 0))
else
desc = &csd_v110_lchan_desc[lchan->tch_mode].hr;
if (OSMO_UNLIKELY(desc->num_blocks == 0))
return -ENOTSUP; return -ENOTSUP;
/* TCH/F14.4 is special: RAA' function is employed */
if (lchan->tch_mode == GSM48_CMODE_DATA_14k5) {
/* 3GPP TS 44.021, section 10.3 "TCH/F14.4 channel coding"
* 3GPP TS 48.020, chapter 11 "THE RAA' FUNCTION" */
const ubit_t *m_bits = &data[0]; /* M-bits */
const ubit_t *d_bits = &data[2]; /* D-bits */
ubit_t c4, c5;
/* 3GPP TS 48.020, Table 3
* | C4 | Date Rate |
* | =1 | 14,4 kbit/s |
* | =0 | 14.4 kbit/s idle (IWF to BSS only) | */
c4 = 1;
/* 3GPP TS 48.020, Table 4
* | C5 | BSS to IWF FT | IWF to BSS UFE |
* | =1 | idle | framing error |
* | =0 | data | no framing error | */
c5 = 0;
/* Unless there is a bug, it's highly unlikely */
OSMO_ASSERT(data_len == CSD_V110_NUM_BITS(desc));
osmo_csd144_to_atrau_bits(&ra_bits[0], m_bits, d_bits, c4, c5);
goto ra1_ra2;
}
/* handle empty/incomplete Uplink frames gracefully */ /* handle empty/incomplete Uplink frames gracefully */
if (OSMO_UNLIKELY(data_len < (desc->num_blocks * desc->num_bits))) { if (OSMO_UNLIKELY(data_len < CSD_V110_NUM_BITS(desc))) {
/* encode N idle frames as per 3GPP TS 44.021, section 8.1.6 */ /* encode N idle frames as per 3GPP TS 44.021, section 8.1.6 */
memset(&ra_bits[0], 0x01, sizeof(ra_bits)); memset(&ra_bits[0], 0x01, sizeof(ra_bits));
for (unsigned int i = 0; i < desc->num_blocks; i++) for (unsigned int i = 0; i < desc->num_frames; i++)
memset(&ra_bits[i * 80], 0x00, 8); /* alignment pattern */ memset(&ra_bits[i * 80], 0x00, 8); /* alignment pattern */
goto ra1_ra2; goto ra1_ra2;
} }
/* RA1'/RA1: convert from radio rate to an intermediate data rate */ /* RA1'/RA1: convert from radio rate to an intermediate data rate */
for (unsigned int i = 0; i < desc->num_blocks; i++) { for (unsigned int i = 0; i < desc->num_frames; i++) {
struct osmo_v110_decoded_frame df; struct osmo_v110_decoded_frame df;
/* convert a V.110 36-/60-bit frame to a V.110 80-bit frame */ /* convert a V.110 36-/60-bit frame to a V.110 80-bit frame */
if (desc->num_bits == 60) if (desc->num_frame_bits == 60)
osmo_csd_12k_6k_decode_frame(&df, &data[i * 60], 60); osmo_csd_12k_6k_decode_frame(&df, &data[i * 60], 60);
else /* desc->num_bits == 36 */ else /* desc->num_frame_bits == 36 */
osmo_csd_3k6_decode_frame(&df, &data[i * 36], 36); osmo_csd_3k6_decode_frame(&df, &data[i * 36], 36);
/* E1 .. E3 must set by out-of-band knowledge */ /* E1 .. E3 must set by out-of-band knowledge */
if (lchan->csd_mode == LCHAN_CSD_M_NT) { if (lchan->csd_mode == LCHAN_CSD_M_NT) {
/* non-transparent: as per 3GPP TS 48.020, Table 7 */ /* non-transparent: as per 3GPP TS 48.020, Table 7 */
df.e_bits[0] = 0; /* E1: as per 15.1.2, shall be set to 0 (for BSS-MSC) */ /* E1: as per 15.1.2, shall be set to 0 (for BSS-MSC) */
df.e_bits[1] = (i >> 1) & 0x01; /* E2: 0 for Q1/Q2, 1 for Q3/Q4 */ df.e_bits[0] = 0;
df.e_bits[2] = (i >> 0) & 0x01; /* E3: 0 for Q1/Q3, 1 for Q2/Q4 */ /* E2: 0 for Q1/Q2, 1 for Q3/Q4 */
if (desc->num_frames == 4)
df.e_bits[1] = (i >> 1) & 0x01;
else
df.e_bits[1] = nt48_half_num;
/* E3: 0 for Q1/Q3, 1 for Q2/Q4 */
df.e_bits[2] = (i >> 0) & 0x01;
} else { } else {
/* transparent: as per 3GPP TS 44.021, Figure 4 */ /* transparent: as per 3GPP TS 44.021, Figure 4 */
df.e_bits[0] = e1e2e3_map[lchan->csd_mode][0]; /* E1 */ df.e_bits[0] = e1e2e3_map[lchan->csd_mode][0]; /* E1 */
@@ -124,65 +160,83 @@ int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp,
ra1_ra2: ra1_ra2:
/* RA1/RA2: convert from an intermediate rate to 64 kbit/s */ /* RA1/RA2: convert from an intermediate rate to 64 kbit/s */
if (desc->num_blocks == 4) { if (desc->ra2_ir == 16)
/* 4 * 80 bits (16 kbit/s) => 2 bits per octet */ osmo_csd_ra2_16k_pack(&rtp[0], &ra_bits[0], RFC4040_RTP_PLEN);
for (unsigned int i = 0, j = 0; i < RFC4040_RTP_PLEN; i++) { else /* desc->ra2_ir == 8 */
rtp[i] = (0xff >> 2); osmo_csd_ra2_8k_pack(&rtp[0], &ra_bits[0], RFC4040_RTP_PLEN);
rtp[i] |= (ra_bits[j++] << 7);
rtp[i] |= (ra_bits[j++] << 6);
}
} else {
/* 2 * 80 bits (8 kbit/s) => 1 bit per octet */
for (unsigned int i = 0; i < RFC4040_RTP_PLEN; i++) {
rtp[i] = (0xff >> 1);
rtp[i] |= (ra_bits[i] << 7);
}
}
return RFC4040_RTP_PLEN; return RFC4040_RTP_PLEN;
} }
int csd_v110_rtp_decode(const struct gsm_lchan *lchan, uint8_t *data, static bool check_v110_align(const ubit_t *ra_bits)
const uint8_t *rtp, size_t rtp_len)
{ {
const struct csd_v110_frame_desc *desc; int i;
ubit_t bit0 = 0, bit1 = 1;
/* The weird code structure is for performance optimization,
* to avoid conditionals inside loops. */
for (i = 0; i < 8; i++)
bit0 |= ra_bits[i];
for (i = 1; i < 10; i++)
bit1 &= ra_bits[i * 8];
return (bit0 == 0) && (bit1 == 1);
}
int csd_v110_rtp_decode(const struct gsm_lchan *lchan, uint8_t *data,
uint8_t *align_bits, const uint8_t *rtp, size_t rtp_len)
{
const struct csd_v110_lchan_desc *desc;
ubit_t ra_bits[80 * 4]; ubit_t ra_bits[80 * 4];
uint8_t align_accum = 0;
OSMO_ASSERT(lchan->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc)); OSMO_ASSERT(lchan->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc));
if (lchan->type == GSM_LCHAN_TCH_F) desc = &csd_v110_lchan_desc[lchan->tch_mode];
desc = &csd_v110_lchan_desc[lchan->tch_mode].fr; if (OSMO_UNLIKELY(desc->num_frames == 0))
else
desc = &csd_v110_lchan_desc[lchan->tch_mode].hr;
if (OSMO_UNLIKELY(desc->num_blocks == 0))
return -ENOTSUP; return -ENOTSUP;
if (OSMO_UNLIKELY(rtp_len != RFC4040_RTP_PLEN)) if (OSMO_UNLIKELY(rtp_len != RFC4040_RTP_PLEN))
return -EINVAL; return -EINVAL;
/* RA1/RA2: convert from 64 kbit/s to an intermediate rate */ /* RA1/RA2: convert from 64 kbit/s to an intermediate rate */
if (desc->num_blocks == 4) { if (desc->ra2_ir == 16)
/* 4 * 80 bits (16 kbit/s) => 2 bits per octet */ osmo_csd_ra2_16k_unpack(&ra_bits[0], &rtp[0], RFC4040_RTP_PLEN);
for (unsigned int i = 0, j = 0; i < RFC4040_RTP_PLEN; i++) { else /* desc->ra2_ir == 8 */
ra_bits[j++] = (rtp[i] >> 7); osmo_csd_ra2_8k_unpack(&ra_bits[0], &rtp[0], RFC4040_RTP_PLEN);
ra_bits[j++] = (rtp[i] >> 6) & 0x01;
} /* TCH/F14.4 is special: RAA' function is employed */
} else { if (lchan->tch_mode == GSM48_CMODE_DATA_14k5) {
/* 2 * 80 bits (8 kbit/s) => 1 bit per octet */ /* 3GPP TS 44.021, section 10.3 "TCH/F14.4 channel coding"
for (unsigned int i = 0; i < RFC4040_RTP_PLEN; i++) * 3GPP TS 48.020, chapter 11 "THE RAA' FUNCTION" */
ra_bits[i] = (rtp[i] >> 7); ubit_t *m_bits = &data[0]; /* M-bits */
ubit_t *d_bits = &data[2]; /* D-bits */
int rc;
rc = osmo_csd144_from_atrau_bits(m_bits, d_bits, NULL, NULL, &ra_bits[0]);
return rc == 0 ? CSD_V110_NUM_BITS(desc) : rc;
} }
/* RA1'/RA1: convert from an intermediate rate to radio rate */ /* RA1'/RA1: convert from an intermediate rate to radio rate */
for (unsigned int i = 0; i < desc->num_blocks; i++) { for (unsigned int i = 0; i < desc->num_frames; i++) {
struct osmo_v110_decoded_frame df; struct osmo_v110_decoded_frame df;
/* We require our RTP input to consist of aligned V.110
* frames. If we get misaligned input, let's catch it
* explicitly, rather than send garbage downstream. */
if (!check_v110_align(&ra_bits[i * 80]))
return -EINVAL;
/* convert a V.110 80-bit frame to a V.110 36-/60-bit frame */ /* convert a V.110 80-bit frame to a V.110 36-/60-bit frame */
osmo_v110_decode_frame(&df, &ra_bits[i * 80], 80); osmo_v110_decode_frame(&df, &ra_bits[i * 80], 80);
if (desc->num_bits == 60) if (desc->num_frame_bits == 60)
osmo_csd_12k_6k_encode_frame(&data[i * 60], 60, &df); osmo_csd_12k_6k_encode_frame(&data[i * 60], 60, &df);
else /* desc->num_bits == 36 */ else /* desc->num_frame_bits == 36 */
osmo_csd_3k6_encode_frame(&data[i * 36], 36, &df); osmo_csd_3k6_encode_frame(&data[i * 36], 36, &df);
/* save bits E2 & E3 that may be needed for RLP alignment */
align_accum <<= 2;
align_accum |= df.e_bits[1] << 1;
align_accum |= df.e_bits[2] << 0;
} }
return desc->num_blocks * desc->num_bits; if (align_bits)
*align_bits = align_accum;
return CSD_V110_NUM_BITS(desc);
} }

View File

@@ -60,6 +60,7 @@
#include <osmo-bts/pcuif_proto.h> #include <osmo-bts/pcuif_proto.h>
#include <osmo-bts/cbch.h> #include <osmo-bts/cbch.h>
#include <osmo-bts/asci.h> #include <osmo-bts/asci.h>
#include <osmo-bts/csd_rlp.h>
#include <osmo-bts/csd_v110.h> #include <osmo-bts/csd_v110.h>
/* determine the CCCH block number based on the frame number */ /* determine the CCCH block number based on the frame number */
@@ -1490,6 +1491,216 @@ static void fr_hr_efr_dtxd_output(struct gsm_lchan *lchan, uint32_t fn,
lchan->tch.dtx_fr_hr_efr.dl_sid_transmitted = true; lchan->tch.dtx_fr_hr_efr.dl_sid_transmitted = true;
} }
/* TDMA frame number of burst 'a' % 26 is the table index.
* This mapping is valid for both TCH/H(0) and TCH/H(1). */
const uint8_t sched_tchh_dl_csd_map[26] = {
[0] = 1, /* TCH/H(0): B0(0 ... 19) */
[1] = 1, /* TCH/H(1): B0(1 ... 20) */
[8] = 1, /* TCH/H(0): B1(8 ... 2) */
[9] = 1, /* TCH/H(1): B1(9 ... 3) */
[17] = 1, /* TCH/H(0): B2(17 ... 10) */
[18] = 1, /* TCH/H(1): B2(18 ... 11) */
};
/* In the case of half-rate CSD, we need to send TCH.req every 40 ms instead of
* every 20 ms. However, both TS 48.103 and common sense desire for
* interoperability (between FR and HR, between OsmoBTS and E1 BTS)
* require 20 ms RTP packetization interval. Hence a special adapter
* function is needed. */
static int tch_rts_ind_csd_hr(struct gsm_bts_trx *trx, struct gsm_lchan *lchan,
struct ph_tch_param *rts_ind)
{
uint8_t chan_nr = rts_ind->chan_nr;
uint32_t fn = rts_ind->fn;
const struct csd_v110_lchan_desc *desc;
unsigned bits_per_20ms;
struct msgb *input_msg[2], *phy_msg;
struct osmo_phsap_prim *resp_l1sap, empty_l1sap;
uint8_t *phy_data;
struct gsm_time g_time;
int i;
/* The generic scheduler still sends us TCH-RTS.ind every 20 ms,
* hence we have to filter out half of them here. */
if (!sched_tchh_dl_csd_map[fn % 26])
return 0;
gsm_fn2gsmtime(&g_time, fn);
desc = &csd_v110_lchan_desc[lchan->tch_mode];
bits_per_20ms = CSD_V110_NUM_BITS(desc);
OSMO_ASSERT(bits_per_20ms != 0);
for (i = 0; i < ARRAY_SIZE(input_msg); i++) {
if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
lchan->abis_ip.rtp_socket->rx_user_ts += GSM_RTP_DURATION;
}
input_msg[i] = msgb_dequeue_count(&lchan->dl_tch_queue,
&lchan->dl_tch_queue_len);
}
if (lchan->csd_mode == LCHAN_CSD_M_NT) {
for (i = 0; i < ARRAY_SIZE(input_msg); i++) {
if (input_msg[i]) {
ntcsd_dl_input_48(lchan, input_msg[i]->data,
rtpmsg_csd_align_bits(input_msg[i]));
} else {
ntcsd_dl_reset(lchan);
}
}
}
phy_msg = l1sap_msgb_alloc(bits_per_20ms * 2);
if (phy_msg) {
resp_l1sap = msgb_l1sap_prim(phy_msg);
phy_msg->l2h = phy_msg->tail;
if (lchan->csd_mode == LCHAN_CSD_M_NT) {
bool good_rlp;
phy_data = msgb_put(phy_msg, 240); /* RLP frame */
good_rlp = ntcsd_dl_output(lchan, phy_data);
if (good_rlp)
gsmtap_csd_rlp_dl(lchan, fn, phy_data, 240);
} else {
for (i = 0; i < ARRAY_SIZE(input_msg); i++) {
phy_data = msgb_put(phy_msg, bits_per_20ms);
if (input_msg[i]) {
memcpy(phy_data, input_msg[i]->data,
bits_per_20ms);
} else {
/* IDLE frame, filled with 1 bits */
memset(phy_data, 0x01, bits_per_20ms);
}
}
}
} else {
resp_l1sap = &empty_l1sap;
}
for (i = 0; i < ARRAY_SIZE(input_msg); i++) {
if (input_msg[i])
msgb_free(input_msg[i]);
}
memset(resp_l1sap, 0, sizeof(*resp_l1sap));
osmo_prim_init(&resp_l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_REQUEST,
phy_msg);
resp_l1sap->u.tch.chan_nr = chan_nr;
resp_l1sap->u.tch.fn = fn;
resp_l1sap->u.tch.marker = 0; /* M bit is undefined for clearmode */
LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "Tx TCH.req\n");
l1sap_down(trx, resp_l1sap);
return 0;
}
/* The case of TCH/F4.8 NT also requires special processing that is
* somewhat similar to half-rate CSD. We have to produce an RLP frame
* for DL every 40 ms, thus it makes the most sense for us to poll
* the Rx jitter buffer every 40 ms just like with CSD-HR. However,
* we need to send TCH.req to the PHY every 20 ms, sending either
* the first half or the second half of the RLP frame we put together
* every 40 ms. */
static int tch_rts_ind_tchf48_nt(struct gsm_bts_trx *trx,
struct gsm_lchan *lchan,
struct ph_tch_param *rts_ind)
{
uint8_t chan_nr = rts_ind->chan_nr;
uint32_t fn = rts_ind->fn;
struct msgb *input_msg, *phy_msg;
struct osmo_phsap_prim *resp_l1sap, empty_l1sap;
ubit_t rlp_frame[240];
bool good_rlp;
struct gsm_time g_time;
int i;
gsm_fn2gsmtime(&g_time, fn);
/* Input processing happens every 40 ms */
if (csd_tchf48_nt_e2_map[fn % 26] == 0) {
for (i = 0; i < 2; i++) {
if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
lchan->abis_ip.rtp_socket->rx_user_ts += GSM_RTP_DURATION;
}
input_msg = msgb_dequeue_count(&lchan->dl_tch_queue,
&lchan->dl_tch_queue_len);
if (input_msg) {
ntcsd_dl_input_48(lchan, input_msg->data,
rtpmsg_csd_align_bits(input_msg));
msgb_free(input_msg);
} else {
ntcsd_dl_reset(lchan);
}
}
good_rlp = ntcsd_dl_output(lchan, rlp_frame);
if (good_rlp)
gsmtap_csd_rlp_dl(lchan, fn, rlp_frame, 240);
memcpy(lchan->tch.csd.tchf48_nt_2ndhalf, rlp_frame+120, 120);
}
/* back to every 20 ms code path */
phy_msg = l1sap_msgb_alloc(120); /* half of RLP frame */
if (phy_msg) {
resp_l1sap = msgb_l1sap_prim(phy_msg);
phy_msg->l2h = msgb_put(phy_msg, 120);
if (csd_tchf48_nt_e2_map[fn % 26] == 0)
memcpy(phy_msg->l2h, rlp_frame, 120);
else
memcpy(phy_msg->l2h, lchan->tch.csd.tchf48_nt_2ndhalf, 120);
} else {
resp_l1sap = &empty_l1sap;
}
memset(resp_l1sap, 0, sizeof(*resp_l1sap));
osmo_prim_init(&resp_l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_REQUEST,
phy_msg);
resp_l1sap->u.tch.chan_nr = chan_nr;
resp_l1sap->u.tch.fn = fn;
resp_l1sap->u.tch.marker = 0; /* M bit is undefined for clearmode */
LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "Tx TCH.req\n");
l1sap_down(trx, resp_l1sap);
return 0;
}
/* For TCH/F9.6 NT we need much less special processing than for TCH/F4.8 NT
* or for CSD-HR, but we still need to handle the possibility of misaligned
* RTP input, i.e., pseudo-V.110 frames aligned in the packet, but not
* forming proper RLP frame alignment via E2 & E3 bits. */
static void tchf96_nt_dl_alignment(struct gsm_lchan *lchan, struct msgb *msg,
uint32_t fn)
{
bool good_rlp;
if (!msg) {
ntcsd_dl_reset(lchan);
/* FIXME: do we really need to generate a PHY packet filled
* with 0 bits to satisfy TS 44.021 section 12.1, or can we
* get by with letting the PHY fill in ones like it does
* for all other CSD modes? */
return;
}
/* Fast path: handle the good case of already proper alignment */
if ((rtpmsg_csd_align_bits(msg) & 0xFF) == NTCSD_ALIGNED_EBITS) {
/* clear the buffer in case we have to do misaligned packets
* later, but otherwise let it go! */
ntcsd_dl_reset(lchan);
gsmtap_csd_rlp_dl(lchan, fn, msgb_l2(msg), msgb_l2len(msg));
return;
}
/* Slow path: realign like in other NT modes */
OSMO_ASSERT(msgb_l2len(msg) == 240);
ntcsd_dl_input_96(lchan, msgb_l2(msg), rtpmsg_csd_align_bits(msg));
good_rlp = ntcsd_dl_output(lchan, msgb_l2(msg));
if (good_rlp)
gsmtap_csd_rlp_dl(lchan, fn, msgb_l2(msg), msgb_l2len(msg));
}
/* TCH-RTS-IND prim received from bts model */ /* TCH-RTS-IND prim received from bts model */
static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx, static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct ph_tch_param *rts_ind) struct osmo_phsap_prim *l1sap, struct ph_tch_param *rts_ind)
@@ -1515,6 +1726,15 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "Rx TCH-RTS.ind\n"); LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "Rx TCH-RTS.ind\n");
} }
/* CSD-HR requires special processing */
if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA &&
lchan->type == GSM_LCHAN_TCH_H)
return tch_rts_ind_csd_hr(trx, lchan, rts_ind);
/* so does TCH/F4.8 NT mode */
if (lchan->tch_mode == GSM48_CMODE_DATA_6k0 &&
lchan->csd_mode == LCHAN_CSD_M_NT)
return tch_rts_ind_tchf48_nt(trx, lchan, rts_ind);
if (!lchan->loopback && lchan->abis_ip.rtp_socket) { if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket); osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
/* FIXME: we _assume_ that we never miss TDMA /* FIXME: we _assume_ that we never miss TDMA
@@ -1561,6 +1781,27 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
&resp_l1sap, &empty_l1sap); &resp_l1sap, &empty_l1sap);
} }
/* minimal extra handling for the remaining CSD NT modes */
if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA &&
lchan->csd_mode == LCHAN_CSD_M_NT) {
switch (lchan->tch_mode) {
case GSM48_CMODE_DATA_12k0:
tchf96_nt_dl_alignment(lchan, resp_msg, fn);
break;
case GSM48_CMODE_DATA_14k5:
if (resp_msg != NULL) {
gsmtap_csd_rlp_dl(lchan, fn,
msgb_l2(resp_msg),
msgb_l2len(resp_msg));
}
break;
default:
LOGPLCGT(lchan, &g_time, DL1P, LOGL_ERROR,
"Invalid TCH mode in TCH-RTS.ind under CSD NT\n");
break;
}
}
memset(resp_l1sap, 0, sizeof(*resp_l1sap)); memset(resp_l1sap, 0, sizeof(*resp_l1sap));
osmo_prim_init(&resp_l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_REQUEST, osmo_prim_init(&resp_l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_REQUEST,
resp_msg); resp_msg);
@@ -1850,119 +2091,16 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
return 1; return 1;
} }
/* process one MAC block of unpacked bits of a non-transparent CSD channel */
static void gsmtap_csd_rlp_process(struct gsm_lchan *lchan, bool is_uplink,
const struct ph_tch_param *tch_ind,
const uint8_t *data, unsigned int data_len)
{
struct gsm_bts_trx *trx = lchan->ts->trx;
struct gsmtap_inst *inst = trx->bts->gsmtap.inst;
struct osmo_rlp_frame_decoded rlpf;
pbit_t *rlp_buf;
uint16_t arfcn;
int byte_len;
if (!inst || !trx->bts->gsmtap.rlp)
return;
if (lchan->csd_mode != LCHAN_CSD_M_NT)
return;
if (is_uplink)
rlp_buf = lchan->tch.csd.rlp_buf_ul;
else
rlp_buf = lchan->tch.csd.rlp_buf_dl;
/* TCH/F 9.6: 4x60bit block => 240bit RLP frame
* TCH/F 4.8: 2x 2x60bit blocks starting at B0/B2/B4 => 240bit RLP frame
* TCH/H 4.8: 4x60bit block => 240bit RLP frame
* TCH/F 2.4: 2x36bit blocks => transparent only
* TCH/H 2.4: 4x36bit blocks => transparent only
* TCH/F 14.4: 2x 290 bit block (starting with M1=0) => 576-bit RLP frame
*/
if (lchan->type == GSM_LCHAN_TCH_F && lchan->tch_mode == GSM48_CMODE_DATA_6k0) {
/* in this mode we have 120bit MAC blocks; two of them need to be concatenated
* to render a 240-bit RLP frame. The fist block is present in B0/B2/B4.
* The E7 bit is used to indicate the Frame MF0a */
OSMO_ASSERT(data_len == 120);
ubit_t e7 = data[4*7+3];
if (e7 == 0) {
osmo_ubit2pbit_ext(rlp_buf, 0, data, 0, data_len, 1);
return;
} else {
osmo_ubit2pbit_ext(rlp_buf, 120, data, 0, data_len, 1);
byte_len = 240/8;
}
} else if (lchan->type == GSM_LCHAN_TCH_F && lchan->tch_mode == GSM48_CMODE_DATA_14k5) {
/* in this mode we have 290bit MAC blocks containing M1, M2 and 288 data bits;
* two of them need to be concatenated to render a
* 576-bit RLP frame. The start of a RLP frame is
* denoted by a block with M1-bit set to 0. */
OSMO_ASSERT(data_len == 290);
ubit_t m1 = data[0];
if (m1 == 0) {
osmo_ubit2pbit_ext(rlp_buf, 0, data, 2, data_len, 1);
return;
} else {
osmo_ubit2pbit_ext(rlp_buf, 288, data, 2, data_len, 1);
byte_len = 576/8;
}
} else {
byte_len = osmo_ubit2pbit_ext(rlp_buf, 0, data, 0, data_len, 1);
}
if (trx->bts->gsmtap.rlp_skip_null) {
int rc = osmo_rlp_decode(&rlpf, 0, rlp_buf, byte_len);
if (rc == 0 && rlpf.ftype == OSMO_RLP_FT_U && rlpf.u_ftype == OSMO_RLP_U_FT_NULL)
return;
}
arfcn = trx->arfcn;
if (is_uplink)
arfcn |= GSMTAP_ARFCN_F_UPLINK;
gsmtap_send_ex(inst, GSMTAP_TYPE_GSM_RLP, arfcn, lchan->ts->nr,
lchan->type == GSM_LCHAN_TCH_H ? GSMTAP_CHANNEL_VOICE_H : GSMTAP_CHANNEL_VOICE_F,
lchan->nr, tch_ind->fn, tch_ind->rssi, 0, rlp_buf, byte_len);
}
static void send_ul_rtp_packet_data(struct gsm_lchan *lchan, const struct ph_tch_param *tch_ind,
const uint8_t *data, uint16_t data_len)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
uint8_t rtp_pl[RFC4040_RTP_PLEN];
int rc;
gsmtap_csd_rlp_process(lchan, true, tch_ind, data, data_len);
rc = csd_v110_rtp_encode(lchan, &rtp_pl[0], data, data_len);
if (rc < 0)
return;
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_TOTAL);
if (lchan->rtp_tx_marker)
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_MARKER);
osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket,
&rtp_pl[0], sizeof(rtp_pl),
fn_ms_adj(tch_ind->fn, lchan),
lchan->rtp_tx_marker);
/* Only clear the marker bit once we have sent a RTP packet with it */
lchan->rtp_tx_marker = false;
}
/* a helper function for the logic in l1sap_tch_ind() */ /* a helper function for the logic in l1sap_tch_ind() */
static void send_ul_rtp_packet_speech(struct gsm_lchan *lchan, uint32_t fn, static void send_ul_rtp_packet(struct gsm_lchan *lchan, uint32_t fn,
const uint8_t *rtp_pl, uint16_t rtp_pl_len) const uint8_t *rtp_pl, uint16_t rtp_pl_len)
{ {
struct gsm_bts *bts = lchan->ts->trx->bts; struct gsm_bts *bts = lchan->ts->trx->bts;
if (lchan->abis_ip.osmux.use) { if (lchan->abis_ip.osmux.use) {
lchan_osmux_send_frame(lchan, rtp_pl, rtp_pl_len, lchan_osmux_send_frame(lchan, rtp_pl, rtp_pl_len,
fn_ms_adj(fn, lchan), lchan->rtp_tx_marker); fn_ms_adj(fn, lchan), lchan->rtp_tx_marker);
} else if (lchan->abis_ip.rtp_socket) { } else if (lchan->abis_ip.rtp_socket != NULL) {
osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket, osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket,
rtp_pl, rtp_pl_len, fn_ms_adj(fn, lchan), lchan->rtp_tx_marker); rtp_pl, rtp_pl_len, fn_ms_adj(fn, lchan), lchan->rtp_tx_marker);
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_TOTAL); rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_TOTAL);
@@ -1973,36 +2111,106 @@ static void send_ul_rtp_packet_speech(struct gsm_lchan *lchan, uint32_t fn,
lchan->rtp_tx_marker = false; lchan->rtp_tx_marker = false;
} }
/* a helper function for emitting HR1 UL in RFC 5993 format */ /* A modified version of send_ul_rtp_packet() that skips fn_ms_adj() check:
static void send_rtp_rfc5993(struct gsm_lchan *lchan, uint32_t fn, * this check will produce a lot of noise when we send two RTP packets
struct msgb *msg) * back-to-back every 40 ms on the same frame number, */
static void send_ul_rtp_packet_hrdata(struct gsm_lchan *lchan,
const uint8_t *rtp_pl, uint16_t rtp_pl_len)
{ {
uint8_t toc; struct gsm_bts *bts = lchan->ts->trx->bts;
OSMO_ASSERT(msg->len == GSM_HR_BYTES); if (lchan->abis_ip.rtp_socket != NULL) {
/* FIXME: implement proper SID classification per GSM 06.41 section osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket,
* 6.1.1; see OS#6036. Until then, detect error-free SID frames rtp_pl, rtp_pl_len,
* using our existing osmo_hr_check_sid() function. */ GSM_RTP_DURATION,
if (osmo_hr_check_sid(msg->data, msg->len)) lchan->rtp_tx_marker);
toc = 0x20; rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_TOTAL);
if (lchan->rtp_tx_marker)
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_MARKER);
}
/* Only clear the marker bit once we have sent a RTP packet with it */
lchan->rtp_tx_marker = false;
}
static void handle_tch_ind_csd_fr(struct gsm_lchan *lchan, const struct ph_tch_param *tch_ind,
const uint8_t *data, uint16_t data_len)
{
uint8_t rtp_pl[RFC4040_RTP_PLEN];
uint8_t tchf48_half = csd_tchf48_nt_e2_map[tch_ind->fn % 26];
int rc;
gsmtap_csd_rlp_process(lchan, true, tch_ind, data, data_len);
/* the last argument matters only for TCH/F4.8 NT mode,
* ignored in all other cases. */
rc = csd_v110_rtp_encode(lchan, rtp_pl, data, data_len, tchf48_half);
if (rc < 0)
return;
send_ul_rtp_packet(lchan, tch_ind->fn, rtp_pl, sizeof(rtp_pl));
}
static void handle_csd_hr_bfi(struct gsm_lchan *lchan)
{
uint8_t rtp_pl[RFC4040_RTP_PLEN];
int rc, i;
rc = csd_v110_rtp_encode(lchan, rtp_pl, NULL, 0, 0);
if (rc < 0)
return;
for (i = 0; i < 2; i++)
send_ul_rtp_packet_hrdata(lchan, rtp_pl, sizeof(rtp_pl));
}
static void handle_tch_ind_csd_hr(struct gsm_lchan *lchan, const struct ph_tch_param *tch_ind,
const uint8_t *data, uint16_t data_len)
{
const struct csd_v110_lchan_desc *desc;
unsigned bits_per_20ms;
uint8_t rtp_pl[RFC4040_RTP_PLEN];
int rc, i;
desc = &csd_v110_lchan_desc[lchan->tch_mode];
bits_per_20ms = CSD_V110_NUM_BITS(desc);
OSMO_ASSERT(bits_per_20ms != 0);
if (data_len != bits_per_20ms * 2) {
handle_csd_hr_bfi(lchan);
return;
}
gsmtap_csd_rlp_process(lchan, true, tch_ind, data, data_len);
for (i = 0; i < 2; i++) {
rc = csd_v110_rtp_encode(lchan, rtp_pl,
data + i * bits_per_20ms,
bits_per_20ms, i);
if (rc < 0)
return;
send_ul_rtp_packet_hrdata(lchan, rtp_pl, sizeof(rtp_pl));
}
}
static void handle_tch_ind_csd(struct gsm_lchan *lchan, const struct ph_tch_param *tch_ind,
const uint8_t *data, uint16_t data_len)
{
if (lchan->type == GSM_LCHAN_TCH_F)
handle_tch_ind_csd_fr(lchan, tch_ind, data, data_len);
else else
toc = 0x00; handle_tch_ind_csd_hr(lchan, tch_ind, data, data_len);
msgb_push_u8(msg, toc);
send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len);
} }
/* a helper function for emitting FR/EFR UL in TW-TS-001 format */ /* a helper function for emitting FR/EFR UL in TW-TS-001 format */
static void send_rtp_twts001(struct gsm_lchan *lchan, uint32_t fn, static void send_rtp_twts001(struct gsm_lchan *lchan, uint32_t fn,
struct msgb *msg, bool good_frame) struct msgb *msg)
{ {
uint8_t teh; uint8_t teh;
bool send_frame; bool send_frame;
if (msg->len == GSM_FR_BYTES || msg->len == GSM_EFR_BYTES) { if (msg->len == GSM_FR_BYTES || msg->len == GSM_EFR_BYTES) {
if (good_frame) teh = 0xE0;
teh = 0xE0; if (tch_ul_msg_bfi(msg))
else teh |= 0x02;
teh = 0xE2;
send_frame = true; send_frame = true;
} else { } else {
teh = 0xE6; teh = 0xE6;
@@ -2015,18 +2223,69 @@ static void send_rtp_twts001(struct gsm_lchan *lchan, uint32_t fn,
teh |= 0x01; teh |= 0x01;
if (send_frame) { if (send_frame) {
msgb_push_u8(msg, teh); msgb_push_u8(msg, teh);
send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len); send_ul_rtp_packet(lchan, fn, msg->data, msg->len);
} else { } else {
send_ul_rtp_packet_speech(lchan, fn, &teh, 1); send_ul_rtp_packet(lchan, fn, &teh, 1);
}
}
/* See Section 5.2 of RFC5993 and TW-TS-002 */
enum super5993_ft {
FT_GOOD_SPEECH = 0,
FT_INVALID_SID = 1,
FT_GOOD_SID = 2,
FT_BFI_WITH_DATA = 6,
FT_NO_DATA = 7,
};
/* a helper function for emitting GSM-HR UL in TW-TS-002 format */
static void send_rtp_twts002(struct gsm_lchan *lchan, uint32_t fn,
struct msgb *msg)
{
enum super5993_ft ft;
uint8_t toc;
if (msg->len == GSM_HR_BYTES) {
switch (tch_ul_msg_hr_sid(msg)) {
case OSMO_GSM631_SID_CLASS_SPEECH:
ft = tch_ul_msg_bfi(msg) ? FT_BFI_WITH_DATA
: FT_GOOD_SPEECH;
break;
case OSMO_GSM631_SID_CLASS_INVALID:
ft = FT_INVALID_SID;
break;
case OSMO_GSM631_SID_CLASS_VALID:
ft = tch_ul_msg_bfi(msg) ? FT_INVALID_SID : FT_GOOD_SID;
break;
default:
OSMO_ASSERT(0);
}
} else {
ft = FT_NO_DATA;
}
/* ToC octet of TW-TS-002 is an extension of RFC 5993 */
toc = ft << 4;
if (ft == FT_INVALID_SID)
toc |= 0x04; /* TW-TS-002 version 1.2.0 */
/* always set DTXd and TAF bits */
if (lchan->ts->trx->bts->dtxd)
toc |= 0x08;
if (fr_hr_efr_sid_position(lchan, fn))
toc |= 0x01;
if (ft != FT_NO_DATA) {
msgb_push_u8(msg, toc);
send_ul_rtp_packet(lchan, fn, msg->data, msg->len);
} else {
send_ul_rtp_packet(lchan, fn, &toc, 1);
} }
} }
/* A helper function for l1sap_tch_ind(): handling BFI /* A helper function for l1sap_tch_ind(): handling BFI
* *
* Please note that the msgb passed to this function is used only when * Please note that the msgb passed to this function is used only when
* the CN asked the BSS to emit extended RTP formats (currently TW-TS-001, * the CN asked the BSS to emit extended RTP formats of TW-TS-001 or
* later TW-TS-002 as well) that can indicate BFI along with deemed-bad * TW-TS-002 that can indicate BFI along with deemed-bad frame data bits,
* frame data bits, just like GSM 08.60 and 08.61 TRAU-UL frames. * just like GSM 08.60 and 08.61 TRAU-UL frames.
*/ */
static void tch_ul_bfi_handler(struct gsm_lchan *lchan, static void tch_ul_bfi_handler(struct gsm_lchan *lchan,
const struct gsm_time *g_time, struct msgb *msg) const struct gsm_time *g_time, struct msgb *msg)
@@ -2045,7 +2304,15 @@ static void tch_ul_bfi_handler(struct gsm_lchan *lchan,
lchan->type == GSM_LCHAN_TCH_F && lchan->type == GSM_LCHAN_TCH_F &&
(lchan->tch_mode == GSM48_CMODE_SPEECH_V1 || (lchan->tch_mode == GSM48_CMODE_SPEECH_V1 ||
lchan->tch_mode == GSM48_CMODE_SPEECH_EFR)) { lchan->tch_mode == GSM48_CMODE_SPEECH_EFR)) {
send_rtp_twts001(lchan, fn, msg, false); send_rtp_twts001(lchan, fn, msg);
return;
}
/* Ditto for TCH/HS and TW-TS-002. */
if ((lchan->abis_ip.rtp_extensions & OSMO_RTP_EXT_TWTS002) &&
lchan->type == GSM_LCHAN_TCH_H &&
lchan->tch_mode == GSM48_CMODE_SPEECH_V1) {
send_rtp_twts002(lchan, fn, msg);
return; return;
} }
@@ -2056,7 +2323,7 @@ static void tch_ul_bfi_handler(struct gsm_lchan *lchan,
/* did it actually give us some output? */ /* did it actually give us some output? */
if (rc > 0) { if (rc > 0) {
/* yes, send it out in RTP */ /* yes, send it out in RTP */
send_ul_rtp_packet_speech(lchan, fn, ecu_out, rc); send_ul_rtp_packet(lchan, fn, ecu_out, rc);
return; return;
} }
} }
@@ -2064,7 +2331,7 @@ static void tch_ul_bfi_handler(struct gsm_lchan *lchan,
/* Are we in rtp continuous-streaming special mode? If so, send out /* Are we in rtp continuous-streaming special mode? If so, send out
* a BFI packet as zero-length RTP payload. */ * a BFI packet as zero-length RTP payload. */
if (lchan->ts->trx->bts->rtp_nogaps_mode) { if (lchan->ts->trx->bts->rtp_nogaps_mode) {
send_ul_rtp_packet_speech(lchan, fn, NULL, 0); send_ul_rtp_packet(lchan, fn, NULL, 0);
return; return;
} }
@@ -2079,6 +2346,164 @@ static void tch_ul_bfi_handler(struct gsm_lchan *lchan,
lchan->rtp_tx_marker = true; lchan->rtp_tx_marker = true;
} }
/* Helper function for l1sap_tch_ind(): RTP output for GSM-HR in either
* of the two standard, non-ThemWi-extended payload formats, with restrictions
* inherent to these non-TRAU-UL-like formats. */
static void send_gsmhr_std_rtp(struct gsm_lchan *lchan,
const struct gsm_time *g_time, struct msgb *msg,
bool emit_rfc5993)
{
uint32_t fn = g_time->fn;
OSMO_ASSERT(msg->len == GSM_HR_BYTES);
switch (tch_ul_msg_hr_sid(msg)) {
case OSMO_GSM631_SID_CLASS_SPEECH:
break;
case OSMO_GSM631_SID_CLASS_INVALID:
/* neither of these RTP formats allows invalid SID */
tch_ul_bfi_handler(lchan, g_time, msg);
return;
case OSMO_GSM631_SID_CLASS_VALID:
/* both formats require perfect, error-free SID output */
osmo_hr_sid_reset(msg->data);
break;
default:
OSMO_ASSERT(0);
}
/* Are we emitting "bare" TS 101 318 or "decorated" RFC 5993? */
if (emit_rfc5993) {
uint8_t toc = tch_ul_msg_hr_sid(msg) << 4;
msgb_push_u8(msg, toc);
}
send_ul_rtp_packet(lchan, fn, msg->data, msg->len);
}
/* Helper function for l1sap_tch_ind(): SID classification and related logic
* for FR, HR and EFR speech codecs. */
static void tch_ul_fr_hr_efr(struct gsm_lchan *lchan, uint32_t fn, struct msgb *msg)
{
enum osmo_gsm631_sid_class sidc;
/* No matter what else is happening, even if we are about to bail out
* early because we received FACCH and thus BFI-no-data, if we are
* at a mandatory-Tx position for SID, we need to clear UL SID filter
* state so that the next valid SID frame will be allowed through,
* whether it occurs right now or in a later frame position because
* of FACCH. See the note in GSM 06.31 section 5.1.2: if the
* SACCH-aligned SID update position is stolen by FACCH, the next
* frame position shall carry the SID update as soon as FACCH stealing
* is over. */
if (fr_hr_efr_sid_position(lchan, fn))
lchan->tch.dtx_fr_hr_efr.ul_sid_filter = false;
/* If we got no payload (BFI without data), there is nothing more
* for us to do here. */
if (msg->len == 0)
return;
/* GSM 06.31, 06.41 and 06.81 are DTX specs for FR, HR and EFR,
* respectively. Section 6.1.1 in each of these specs defines a
* ternary SID classification whereby each channel-decoded traffic
* frame is valid SID, invalid SID or non-SID speech. Perform
* this classification. */
switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_V1:
if (lchan->type == GSM_LCHAN_TCH_F) {
sidc = osmo_fr_sid_classify(msg->data);
} else {
/* None of our current BTS models has UFI or BCI
* error flags for TCH/HS UL Rx, hence we have to
* perform SID classification without BCI. */
sidc = osmo_hr_sid_classify(msg->data, false, NULL);
/* Pass it to RTP output functions */
tch_ul_msg_hr_sid(msg) = sidc;
}
break;
case GSM48_CMODE_SPEECH_EFR:
sidc = osmo_efr_sid_classify(msg->data);
break;
default:
/* This static function should never be called except for
* V1 and EFR speech modes. */
OSMO_ASSERT(0);
}
/* We use this SID classification for three purposes:
*
* 1) For those users who desire to have RTP marker bit set in the
* output packet corresponding to the first speech frame after
* a DTX pause, the logic of lchan_set_marker() is driven by
* the determination of whether or not each received traffic frame
* is an accepted SID frame in the definition of GSM 06.31
* and its HR & EFR counterparts.
*
* 2) As a result of how FR/HR/EFR DTX interacts with block diagonal
* interleaving, at the beginning and end of each DTX pause a
* correctly functioning TCH receiver will always pick up an
* artifact consisting of 4 received bursts (2 for TCH/HS)
* and same number of omitted bursts. Standard channel decoding
* of this Rx artifact will produce a "half-block" in which half
* of the bits prior to convolutional decoding will come from
* a SID repetition whose Tx was notionally suppressed, while
* the other half will be garbage. As a result of convolutional
* decoding, the result will often appear as valid SID per
* classification rules - but passing it as such to the Rx DTX
* handler is wrong. Classic E1 BTS and GSM MS implementations
* include a kind of SID filter at this point, setting BFI on
* these received half-blocks, so that the Rx DTX handler will
* see an invalid SID condition. Invalid SID means that comfort
* noise generation is to be continued, but no updated CN
* parameters are available - which is the truth in half-block
* Rx situations. We implement the same filter. More info here:
*
* https://osmocom.org/projects/retro-gsm/wiki/DTXu_half-blocks
*
* 3) For HR codec only, RTP output format functions need to know
* both BFI flag and SID classification.
*/
switch (sidc) {
case OSMO_GSM631_SID_CLASS_SPEECH:
/* Only a good speech frame, not an unusable frame,
* using GSM 06.31 definitions, marks exit from a DTX pause. */
if (!tch_ul_msg_bfi(msg)) {
lchan_set_marker(false, lchan);
lchan->tch.dtx_fr_hr_efr.ul_sid_filter = false;
}
break;
case OSMO_GSM631_SID_CLASS_VALID:
/* Here comes the just-described SID filter. The logic is
* thus: a valid SID is allowed through to the Rx DTX handler
* if it follows speech (end of talkspurt), if it appears
* in the expected mandatory-Tx position for SID updates,
* or if it happens after that mandatory-Tx position due to
* FACCH stealing. Otherwise, valid SID is converted to
* invalid by setting BFI.
*/
if (lchan->tch.dtx_fr_hr_efr.ul_sid_filter)
tch_ul_msg_bfi(msg) = true;
/* fall through */
case OSMO_GSM631_SID_CLASS_INVALID:
/* Whether we got valid or invalid SID, we know that the MS
* has entered or remains in a DTXu pause. (Invalid SID
* means that MS state is known to be DTXu, but no CN
* parameters are available.) Hence we indicate DTXu pause
* state for both RTP marker bit and SID filter mechanisms.
*/
lchan_set_marker(true, lchan);
lchan->tch.dtx_fr_hr_efr.ul_sid_filter = true;
break;
default:
/* There are only 3 possible SID classifications per
* section 6.1.1 of each of the three DTX specs,
* and correspondingly only 3 possible output values
* from osmo_*_sid_classify() functions. */
OSMO_ASSERT(0);
}
}
/* TCH received from bts model */ /* TCH received from bts model */
static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap, static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
struct ph_tch_param *tch_ind) struct ph_tch_param *tch_ind)
@@ -2118,10 +2543,35 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
msgb_pull_to_l2(msg); msgb_pull_to_l2(msg);
/* Low level layers always call us when TCH content is expected, even if /* Low level layers always call us when TCH content is expected, even if
* the content is not available due to decoding issues. Content not * the content is not available due to decoding issues. Content not
* available is expected as empty payload. We also check if quality is * available is indicated as empty payload, also termed BFI w/o data.
* good enough. */ * Alternatively, a BTS model can supply channel-decoded payload bits,
if (msg->len && tch_ind->lqual_cb >= bts->min_qual_norm) { * but also set BFI flag, just like in TRAU-UL frames on E1 Abis.
*
* If the channel mode is speech (not CSD), we also check if quality is
* good enough - if it isn't, we set BFI. This quality check is
* essential with FRv1 codec and DTXu, otherwise DTXu pauses will be
* filled with very unpleasant sounds as channel-decoded radio noise
* gets declared as good traffic frames with 1/8 probability given
* only a 3-bit CRC. However, this check is restricted to speech
* because per the specs, BFI does not exist in CSD: every channel-
* decoded frame is passed along, error handling either falls on RLP
* or is the responsibility of user applications in Transparent mode.
*/
if ((lchan->rsl_cmode == RSL_CMOD_SPD_SPEECH) &&
(tch_ind->lqual_cb < bts->min_qual_norm))
tch_ul_msg_bfi(msg) = true;
/* For FR, HR or EFR speech we also have to perform SID classification
* and apply logic that results from such. This logic can also set
* BFI that wasn't set before! */
if (lchan->tch_mode == GSM48_CMODE_SPEECH_V1 ||
lchan->tch_mode == GSM48_CMODE_SPEECH_EFR)
tch_ul_fr_hr_efr(lchan, fn, msg);
/* Good RTP output happens when we got some payload AND it is not
* marked as BFI. */
if (msg->len && !tch_ul_msg_bfi(msg)) {
/* feed the good frame to the ECU, if we are applying one */ /* feed the good frame to the ECU, if we are applying one */
if (lchan->ecu_state) if (lchan->ecu_state)
osmo_ecu_frame_in(lchan->ecu_state, false, msg->data, msg->len); osmo_ecu_frame_in(lchan->ecu_state, false, msg->data, msg->len);
@@ -2134,24 +2584,26 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
lchan->tch_mode == GSM48_CMODE_SPEECH_EFR)) { lchan->tch_mode == GSM48_CMODE_SPEECH_EFR)) {
/* FR and EFR codecs */ /* FR and EFR codecs */
if (lchan->abis_ip.rtp_extensions & OSMO_RTP_EXT_TWTS001) if (lchan->abis_ip.rtp_extensions & OSMO_RTP_EXT_TWTS001)
send_rtp_twts001(lchan, fn, msg, true); send_rtp_twts001(lchan, fn, msg);
else else
send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len); send_ul_rtp_packet(lchan, fn, msg->data, msg->len);
} else if (lchan->type == GSM_LCHAN_TCH_H && } else if (lchan->type == GSM_LCHAN_TCH_H &&
lchan->tch_mode == GSM48_CMODE_SPEECH_V1) { lchan->tch_mode == GSM48_CMODE_SPEECH_V1) {
/* HR codec: TS 101 318 or RFC 5993, /* HR codec: TW-TS-002 in ThemWi environment,
* will also support TW-TS-002 in the future. */ * or TS 101 318 or RFC 5993 in traditional
if (bts->emit_hr_rfc5993) * 3GPP or Osmocom environments. */
send_rtp_rfc5993(lchan, fn, msg); if (lchan->abis_ip.rtp_extensions & OSMO_RTP_EXT_TWTS002)
send_rtp_twts002(lchan, fn, msg);
else else
send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len); send_gsmhr_std_rtp(lchan, &g_time, msg,
bts->emit_hr_rfc5993);
} else { } else {
/* generic case, no RTP alterations */ /* generic case, no RTP alterations */
send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len); send_ul_rtp_packet(lchan, fn, msg->data, msg->len);
} }
break; break;
case RSL_CMOD_SPD_DATA: case RSL_CMOD_SPD_DATA:
send_ul_rtp_packet_data(lchan, tch_ind, msg->data, msg->len); handle_tch_ind_csd(lchan, tch_ind, msg->data, msg->len);
break; break;
case RSL_CMOD_SPD_SIGN: case RSL_CMOD_SPD_SIGN:
return 0; /* drop stale TCH.ind */ return 0; /* drop stale TCH.ind */
@@ -2166,7 +2618,11 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
return 1; return 1;
} }
} else { } else {
tch_ul_bfi_handler(lchan, &g_time, msg); if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA &&
lchan->type == GSM_LCHAN_TCH_H)
handle_csd_hr_bfi(lchan);
else
tch_ul_bfi_handler(lchan, &g_time, msg);
} }
lchan->tch.last_fn = fn; lchan->tch.last_fn = fn;
@@ -2433,6 +2889,7 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
struct gsm_bts *bts = lchan->ts->trx->bts; struct gsm_bts *bts = lchan->ts->trx->bts;
struct msgb *msg; struct msgb *msg;
bool rfc5993_sid = false; bool rfc5993_sid = false;
uint8_t csd_align_bits = 0;
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_TOTAL); rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_TOTAL);
if (marker) if (marker)
@@ -2460,18 +2917,16 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
OSMO_ASSERT(0); OSMO_ASSERT(0);
} }
msg = l1sap_msgb_alloc(512); #define L1SAP_MSGB_L2LEN_TCH 512
msg = l1sap_msgb_alloc(L1SAP_MSGB_L2LEN_TCH);
if (!msg) if (!msg)
return; return;
if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA) { if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA) {
int rc = csd_v110_rtp_decode(lchan, msg->tail, int rc = csd_v110_rtp_decode(lchan, msg->tail, &csd_align_bits,
rtp_pl, rtp_pl_len); rtp_pl, rtp_pl_len);
if (rc > 0) { if (rc > 0) {
/* 'fake' tch_ind containing all-zero so gsmtap code can be shared
* between UL and DL */
static const struct ph_tch_param fake_tch_ind = {};
gsmtap_csd_rlp_process(lchan, false, &fake_tch_ind, msg->tail, rc);
msgb_put(msg, rc); msgb_put(msg, rc);
} else { } else {
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_DROP_V110_DEC); rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_DROP_V110_DEC);
@@ -2479,6 +2934,12 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
return; return;
} }
} else { } else {
if (OSMO_UNLIKELY(rtp_pl_len > L1SAP_MSGB_L2LEN_TCH)) {
LOGPLCHAN(lchan, DL1P, LOGL_ERROR,
"%s(): incoming RTP truncated: %u -> %u\n",
__func__, rtp_pl_len, L1SAP_MSGB_L2LEN_TCH);
rtp_pl_len = L1SAP_MSGB_L2LEN_TCH; /* truncate */
}
memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len); memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len);
} }
@@ -2492,6 +2953,8 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
rtpmsg_ts(msg) = timestamp; rtpmsg_ts(msg) = timestamp;
/* Store RFC 5993 SID flag likewise */ /* Store RFC 5993 SID flag likewise */
rtpmsg_is_rfc5993_sid(msg) = rfc5993_sid; rtpmsg_is_rfc5993_sid(msg) = rfc5993_sid;
/* ditto with CSD alignment bits */
rtpmsg_csd_align_bits(msg) = csd_align_bits;
/* make sure the queue doesn't get too long */ /* make sure the queue doesn't get too long */
lchan_dl_tch_queue_enqueue(lchan, msg, 1); lchan_dl_tch_queue_enqueue(lchan, msg, 1);

View File

@@ -56,6 +56,20 @@ const struct value_string lchan_ciph_state_names[] = {
{ 0, NULL } { 0, NULL }
}; };
const struct value_string lchan_csd_mode_descs[] = {
{ LCHAN_CSD_M_NT, "non-transparent" },
{ LCHAN_CSD_M_T_1200_75, "transparent @ 1200/75 bps" },
{ LCHAN_CSD_M_T_600, "transparent @ 600 bps" },
{ LCHAN_CSD_M_T_1200, "transparent @ 1200 bps" },
{ LCHAN_CSD_M_T_2400, "transparent @ 2400 bps" },
{ LCHAN_CSD_M_T_4800, "transparent @ 4800 bps" },
{ LCHAN_CSD_M_T_9600, "transparent @ 9600 bps" },
{ LCHAN_CSD_M_T_14400, "transparent @ 14400 bps" },
{ LCHAN_CSD_M_T_29000, "transparent @ 29000 bps" },
{ LCHAN_CSD_M_T_32000, "transparent @ 32000 bps" },
{ 0, NULL }
};
/* prepare the per-SAPI T200 arrays for a given lchan */ /* prepare the per-SAPI T200 arrays for a given lchan */
static int t200_by_lchan(uint32_t *t200_fn_dcch, uint32_t *t200_fn_acch, struct gsm_lchan *lchan) static int t200_by_lchan(uint32_t *t200_fn_dcch, uint32_t *t200_fn_acch, struct gsm_lchan *lchan)
{ {

View File

@@ -361,7 +361,7 @@ int bts_main(int argc, char **argv)
/* TODO: move this to gsm_bts_alloc() */ /* TODO: move this to gsm_bts_alloc() */
if (g_bts->gsmtap.remote_host != NULL) { if (g_bts->gsmtap.remote_host != NULL) {
LOGP(DLGLOBAL, LOGL_NOTICE, LOGP(DLGLOBAL, LOGL_NOTICE,
"Setting up GSMTAP Um forwarding '%s->'%s:%u'\n", "Setting up GSMTAP Um forwarding '%s'->'%s:%u'\n",
g_bts->gsmtap.local_host, g_bts->gsmtap.remote_host, GSMTAP_UDP_PORT); g_bts->gsmtap.local_host, g_bts->gsmtap.remote_host, GSMTAP_UDP_PORT);
g_bts->gsmtap.inst = gsmtap_source_init2(g_bts->gsmtap.local_host, 0, g_bts->gsmtap.inst = gsmtap_source_init2(g_bts->gsmtap.local_host, 0,
g_bts->gsmtap.remote_host, GSMTAP_UDP_PORT, 1); g_bts->gsmtap.remote_host, GSMTAP_UDP_PORT, 1);

View File

@@ -36,10 +36,10 @@
#include <osmocom/core/msgb.h> #include <osmocom/core/msgb.h>
#include <osmocom/core/utils.h> #include <osmocom/core/utils.h>
#include <osmocom/gsm/protocol/gsm_12_21.h> #include <osmocom/gsm/protocol/gsm_12_21.h>
#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmocom/gsm/abis_nm.h> #include <osmocom/gsm/abis_nm.h>
#include <osmocom/gsm/tlv.h> #include <osmocom/gsm/tlv.h>
#include <osmocom/abis/e1_input.h> #include <osmocom/abis/e1_input.h>
#include <osmocom/abis/ipaccess.h>
#include <osmo-bts/logging.h> #include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h> #include <osmo-bts/gsm_data.h>

View File

@@ -858,25 +858,31 @@ static int pcu_tx_si_all(struct gsm_bts *bts)
static int pcu_rx_txt_ind(struct gsm_bts *bts, static int pcu_rx_txt_ind(struct gsm_bts *bts,
struct gsm_pcu_if_txt_ind *txt) struct gsm_pcu_if_txt_ind *txt)
{ {
int rc; int rc = 0;
switch (txt->type) { switch (txt->type) {
case PCU_VERSION: case PCU_VERSION:
LOGP(DPCU, LOGL_INFO, "OsmoPCU version %s connected\n", LOGP(DPCU, LOGL_INFO, "OsmoPCU version %s connected\n",
txt->text); txt->text);
oml_tx_failure_event_rep(&bts->gprs.cell.mo, NM_SEVER_CEASED, OSMO_EVT_PCU_VERS, txt->text);
osmo_strlcpy(bts->pcu_version, txt->text, MAX_VERSION_LENGTH);
/* patch SI to advertise GPRS, *if* the SI sent by BSC said so */ /* we use the reception of the PCU_VERSION as a trigger to make the PCU available for
regenerate_si3_restoctets(bts); * all BTSs handled by this process (currently this is exactly one BTS, see FIXME notes) */
regenerate_si4_restoctets(bts); llist_for_each_entry(bts, &g_bts_sm->bts_list, list) {
oml_tx_failure_event_rep(&bts->gprs.cell.mo, NM_SEVER_CEASED, OSMO_EVT_PCU_VERS, txt->text);
osmo_strlcpy(bts->pcu_version, txt->text, MAX_VERSION_LENGTH);
rc = pcu_tx_si_all(bts); /* patch SI to advertise GPRS, *if* the SI sent by BSC said so */
regenerate_si3_restoctets(bts);
regenerate_si4_restoctets(bts);
if (pcu_tx_si_all(bts) < 0)
rc = -EINVAL;
}
if (rc < 0) if (rc < 0)
return -EINVAL; return rc;
break; break;
case PCU_OML_ALERT: case PCU_OML_ALERT:
OSMO_ASSERT(bts);
oml_tx_failure_event_rep(&bts->gprs.cell.mo, NM_SEVER_INDETERMINATE, OSMO_EVT_EXT_ALARM, oml_tx_failure_event_rep(&bts->gprs.cell.mo, NM_SEVER_INDETERMINATE, OSMO_EVT_EXT_ALARM,
txt->text); txt->text);
break; break;
@@ -937,36 +943,52 @@ static int pcu_rx_act_req(struct gsm_bts *bts,
return -EINVAL; \ return -EINVAL; \
} \ } \
} while (0) } while (0)
#define ENSURE_BTS_OBJECT(bts) \
do { \
if ((bts = gsm_bts_num(g_bts_sm, pcu_prim->bts_nr)) == NULL) { \
LOGP(DPCU, LOGL_ERROR, "Received PCU Prim for non-existent BTS %u\n", pcu_prim->bts_nr); \
return -EINVAL; \
} \
} while (0)
static int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim, size_t prim_len) static int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim, size_t prim_len)
{ {
int rc = 0; int rc = 0;
struct gsm_bts *bts; struct gsm_bts *bts;
size_t exp_len; size_t exp_len;
if ((bts = gsm_bts_num(g_bts_sm, pcu_prim->bts_nr)) == NULL) {
LOGP(DPCU, LOGL_ERROR, "Received PCU Prim for non-existent BTS %u\n", pcu_prim->bts_nr);
return -EINVAL;
}
switch (msg_type) { switch (msg_type) {
case PCU_IF_MSG_DATA_REQ: case PCU_IF_MSG_DATA_REQ:
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.data_req); CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.data_req);
ENSURE_BTS_OBJECT(bts);
rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req); rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req);
break; break;
case PCU_IF_MSG_PAG_REQ: case PCU_IF_MSG_PAG_REQ:
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.pag_req); CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.pag_req);
ENSURE_BTS_OBJECT(bts);
rc = pcu_rx_pag_req(bts, msg_type, &pcu_prim->u.pag_req); rc = pcu_rx_pag_req(bts, msg_type, &pcu_prim->u.pag_req);
break; break;
case PCU_IF_MSG_ACT_REQ: case PCU_IF_MSG_ACT_REQ:
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.act_req); CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.act_req);
ENSURE_BTS_OBJECT(bts);
rc = pcu_rx_act_req(bts, &pcu_prim->u.act_req); rc = pcu_rx_act_req(bts, &pcu_prim->u.act_req);
break; break;
case PCU_IF_MSG_TXT_IND: case PCU_IF_MSG_TXT_IND:
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.txt_ind); CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.txt_ind);
rc = pcu_rx_txt_ind(bts, &pcu_prim->u.txt_ind); if (pcu_prim->u.txt_ind.type == PCU_VERSION) {
/* A TXT indication that carries the PCU_VERSION is always addressed to the
* receiving process as a whole, which means we will not resolve a specific
* BTS object in this case. */
rc = pcu_rx_txt_ind(NULL, &pcu_prim->u.txt_ind);
} else {
ENSURE_BTS_OBJECT(bts);
rc = pcu_rx_txt_ind(bts, &pcu_prim->u.txt_ind);
}
break; break;
case PCU_IF_MSG_CONTAINER: case PCU_IF_MSG_CONTAINER:
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.container); CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.container);
ENSURE_BTS_OBJECT(bts);
/* ^ check if we can access container fields, v check with container data length */ /* ^ check if we can access container fields, v check with container data length */
exp_len = PCUIF_HDR_SIZE + sizeof(pcu_prim->u.container) + osmo_load16be(&pcu_prim->u.container.length); exp_len = PCUIF_HDR_SIZE + sizeof(pcu_prim->u.container) + osmo_load16be(&pcu_prim->u.container.length);
if (prim_len < exp_len) { if (prim_len < exp_len) {

View File

@@ -206,6 +206,12 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
if (params == NULL) if (params == NULL)
return 0; return 0;
/* Average the input RxLev/RxQual samples (if needed). Do this before
* the loop suspension logic to keep the pre-processing state updated. */
ci_meas = lchan_get_ci_thresholds(lchan);
ul_lqual_cb_avg = do_avg_algo(ci_meas, &state->ci_meas_proc, ul_lqual_cb);
rxlev_avg = do_avg_algo(&params->rxlev_meas, &state->rxlev_meas_proc, dbm2rxlev(ul_rssi_dbm));
/* Shall we skip current block based on configured interval? */ /* Shall we skip current block based on configured interval? */
if (ctrl_interval_skip_block(params, state)) if (ctrl_interval_skip_block(params, state))
return 0; return 0;
@@ -225,15 +231,10 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
return 0; return 0;
} }
ci_meas = lchan_get_ci_thresholds(lchan);
/* Is C/I based algo enabled by config? /* Is C/I based algo enabled by config?
* FIXME: this can later be generalized when properly implementing P & N counting. */ * FIXME: this can later be generalized when properly implementing P & N counting. */
ci_on = ci_meas->lower_cmp_n && ci_meas->upper_cmp_n; ci_on = ci_meas->lower_cmp_n && ci_meas->upper_cmp_n;
ul_lqual_cb_avg = do_avg_algo(ci_meas, &state->ci_meas_proc, ul_lqual_cb);
rxlev_avg = do_avg_algo(&params->rxlev_meas, &state->rxlev_meas_proc, dbm2rxlev(ul_rssi_dbm));
/* If computed C/I is enabled and out of acceptable thresholds: */ /* If computed C/I is enabled and out of acceptable thresholds: */
if (ci_on && ul_lqual_cb_avg < ci_meas->lower_thresh * 10) { if (ci_on && ul_lqual_cb_avg < ci_meas->lower_thresh * 10) {
new_dbm = ms_dbm + params->inc_step_size_db; new_dbm = ms_dbm + params->inc_step_size_db;
@@ -334,10 +335,6 @@ int lchan_bs_pwr_ctrl(struct gsm_lchan *lchan,
lchan->tch.dtx.dl_active ? "enabled" : "disabled", lchan->tch.dtx.dl_active ? "enabled" : "disabled",
lchan->tch.dtx.dl_active ? "SUB" : "FULL"); lchan->tch.dtx.dl_active ? "SUB" : "FULL");
/* Shall we skip current block based on configured interval? */
if (ctrl_interval_skip_block(params, state))
return 0;
/* If DTx is active on Downlink, use the '-SUB' */ /* If DTx is active on Downlink, use the '-SUB' */
if (lchan->tch.dtx.dl_active) { if (lchan->tch.dtx.dl_active) {
rxqual = mr->rxqual_sub; rxqual = mr->rxqual_sub;
@@ -347,8 +344,15 @@ int lchan_bs_pwr_ctrl(struct gsm_lchan *lchan,
rxlev = mr->rxlev_full; rxlev = mr->rxlev_full;
} }
/* Average the input RxLev/RxQual samples (if needed). Do this before
* the loop suspension logic to keep the pre-processing state updated. */
rxlev_avg = do_avg_algo(&params->rxlev_meas, &state->rxlev_meas_proc, rxlev); rxlev_avg = do_avg_algo(&params->rxlev_meas, &state->rxlev_meas_proc, rxlev);
rxqual_avg = do_avg_algo(&params->rxqual_meas, &state->rxqual_meas_proc, rxqual); rxqual_avg = do_avg_algo(&params->rxqual_meas, &state->rxqual_meas_proc, rxqual);
/* Shall we skip current block based on configured interval? */
if (ctrl_interval_skip_block(params, state))
return 0;
/* If RxQual > L_RXQUAL_XX_P, try to increase Tx power */ /* If RxQual > L_RXQUAL_XX_P, try to increase Tx power */
if (rxqual_avg > params->rxqual_meas.lower_thresh) { if (rxqual_avg > params->rxqual_meas.lower_thresh) {
/* Increase Tx power by reducing Tx attenuation */ /* Increase Tx power by reducing Tx attenuation */

View File

@@ -205,37 +205,48 @@ static int rsl_handle_chan_mod_ie(struct gsm_lchan *lchan,
/* If octet 4 indicates non-transparent data */ /* If octet 4 indicates non-transparent data */
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_14k5): case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_14k5):
lchan->tch_mode = GSM48_CMODE_DATA_14k5; lchan->tch_mode = GSM48_CMODE_DATA_14k5;
lchan->csd_mode = LCHAN_CSD_M_NT;
break; break;
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_12k0): case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_12k0):
lchan->tch_mode = GSM48_CMODE_DATA_12k0; lchan->tch_mode = GSM48_CMODE_DATA_12k0;
lchan->csd_mode = LCHAN_CSD_M_NT;
break; break;
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_6k0): case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_6k0):
lchan->tch_mode = GSM48_CMODE_DATA_6k0; lchan->tch_mode = GSM48_CMODE_DATA_6k0;
lchan->csd_mode = LCHAN_CSD_M_NT;
break; break;
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_43k5): case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_43k5):
lchan->tch_mode = GSM48_CMODE_DATA_43k5; lchan->tch_mode = GSM48_CMODE_DATA_43k5;
lchan->csd_mode = LCHAN_CSD_M_NT;
break; break;
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_28k8): case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_28k8):
/* 28.8 kbit/s services, 29.0 kbit/s radio interface rate */ /* 28.8 kbit/s services, 29.0 kbit/s radio interface rate */
lchan->tch_mode = GSM48_CMODE_DATA_29k0; lchan->tch_mode = GSM48_CMODE_DATA_29k0;
lchan->csd_mode = LCHAN_CSD_M_NT;
break; break;
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_43k5_14k5): case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_43k5_14k5):
lchan->tch_mode = GSM48_CMODE_DATA_43k5_14k5; lchan->tch_mode = GSM48_CMODE_DATA_43k5_14k5;
lchan->csd_mode = LCHAN_CSD_M_NT;
break; break;
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_29k0_14k5): case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_29k0_14k5):
lchan->tch_mode = GSM48_CMODE_DATA_29k0_14k5; lchan->tch_mode = GSM48_CMODE_DATA_29k0_14k5;
lchan->csd_mode = LCHAN_CSD_M_NT;
break; break;
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_43k5_29k0): case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_43k5_29k0):
lchan->tch_mode = GSM48_CMODE_DATA_43k5_29k0; lchan->tch_mode = GSM48_CMODE_DATA_43k5_29k0;
lchan->csd_mode = LCHAN_CSD_M_NT;
break; break;
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_14k5_43k5): case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_14k5_43k5):
lchan->tch_mode = GSM48_CMODE_DATA_14k5_43k5; lchan->tch_mode = GSM48_CMODE_DATA_14k5_43k5;
lchan->csd_mode = LCHAN_CSD_M_NT;
break; break;
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_14k5_29k0): case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_14k5_29k0):
lchan->tch_mode = GSM48_CMODE_DATA_14k5_29k0; lchan->tch_mode = GSM48_CMODE_DATA_14k5_29k0;
lchan->csd_mode = LCHAN_CSD_M_NT;
break; break;
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_29k0_43k5): case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_29k0_43k5):
lchan->tch_mode = GSM48_CMODE_DATA_29k0_43k5; lchan->tch_mode = GSM48_CMODE_DATA_29k0_43k5;
lchan->csd_mode = LCHAN_CSD_M_NT;
break; break;
/* If octet 4 indicates transparent data */ /* If octet 4 indicates transparent data */
@@ -2067,8 +2078,8 @@ static int rsl_rx_chan_activ(struct msgb *msg)
} }
/* Spec explicitly states BTS should only perform /* Spec explicitly states BTS should only perform
* autonomous MS power control loop in BTS if 'MS Power * autonomous MS power control loop in BTS if 'MS Power
* Parameters' IE is present! */ * Parameters' IE is present! */
lchan->ms_power_ctrl.dpc_params = params; lchan->ms_power_ctrl.dpc_params = params;
} }

View File

@@ -1997,6 +1997,11 @@ static void lchan_dump_full_vty(struct vty *vty, const struct gsm_lchan *lchan)
vty_out(vty, " Channel Mode / Codec: %s%s", vty_out(vty, " Channel Mode / Codec: %s%s",
gsm48_chan_mode_name(lchan->tch_mode), gsm48_chan_mode_name(lchan->tch_mode),
VTY_NEWLINE); VTY_NEWLINE);
if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA) {
vty_out(vty, " CSD mode: %s%s",
lchan_csd_mode_desc(lchan->csd_mode),
VTY_NEWLINE);
}
if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
const struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr; const struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr;
const struct gsm48_multi_rate_conf *mr_conf = const struct gsm48_multi_rate_conf *mr_conf =

View File

@@ -458,7 +458,7 @@ uint32_t trx_get_hlayer1(const struct gsm_bts_trx *trx)
{ {
const struct lc15l1_hdl *fl1h = trx_lc15l1_hdl(trx); const struct lc15l1_hdl *fl1h = trx_lc15l1_hdl(trx);
return fl1h->hLayer1; return (uint32_t)fl1h->hLayer1;
} }
static int trx_close_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg, static int trx_close_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,

View File

@@ -68,8 +68,6 @@ static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len
cur = msgb_put(msg, GSM_FR_BYTES); cur = msgb_put(msg, GSM_FR_BYTES);
memcpy(cur, l1_payload, GSM_FR_BYTES); memcpy(cur, l1_payload, GSM_FR_BYTES);
lchan_set_marker(osmo_fr_is_any_sid(l1_payload), lchan);
return msg; return msg;
} }
@@ -102,8 +100,6 @@ static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload,
cur = msgb_put(msg, GSM_EFR_BYTES); cur = msgb_put(msg, GSM_EFR_BYTES);
memcpy(cur, l1_payload, GSM_EFR_BYTES); memcpy(cur, l1_payload, GSM_EFR_BYTES);
lchan_set_marker(osmo_efr_is_any_sid(l1_payload), lchan);
return msg; return msg;
} }
@@ -134,8 +130,6 @@ static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len
cur = msgb_put(msg, GSM_HR_BYTES); cur = msgb_put(msg, GSM_HR_BYTES);
memcpy(cur, l1_payload, GSM_HR_BYTES); memcpy(cur, l1_payload, GSM_HR_BYTES);
lchan_set_marker(osmo_hr_check_sid(l1_payload, payload_len), lchan);
return msg; return msg;
} }

View File

@@ -473,7 +473,7 @@ uint32_t trx_get_hlayer1(const struct gsm_bts_trx *trx)
{ {
const struct oc2gl1_hdl *fl1h = trx_oc2gl1_hdl(trx); const struct oc2gl1_hdl *fl1h = trx_oc2gl1_hdl(trx);
return fl1h->hLayer1; return (uint32_t)fl1h->hLayer1;
} }
static int trx_close_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg, static int trx_close_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,

View File

@@ -68,8 +68,6 @@ static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len
cur = msgb_put(msg, GSM_FR_BYTES); cur = msgb_put(msg, GSM_FR_BYTES);
memcpy(cur, l1_payload, GSM_FR_BYTES); memcpy(cur, l1_payload, GSM_FR_BYTES);
lchan_set_marker(osmo_fr_is_any_sid(l1_payload), lchan);
return msg; return msg;
} }
@@ -102,8 +100,6 @@ static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload,
cur = msgb_put(msg, GSM_EFR_BYTES); cur = msgb_put(msg, GSM_EFR_BYTES);
memcpy(cur, l1_payload, GSM_EFR_BYTES); memcpy(cur, l1_payload, GSM_EFR_BYTES);
lchan_set_marker(osmo_efr_is_any_sid(l1_payload), lchan);
return msg; return msg;
} }
@@ -134,8 +130,6 @@ static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len
cur = msgb_put(msg, GSM_HR_BYTES); cur = msgb_put(msg, GSM_HR_BYTES);
memcpy(cur, l1_payload, GSM_HR_BYTES); memcpy(cur, l1_payload, GSM_HR_BYTES);
lchan_set_marker(osmo_hr_check_sid(l1_payload, payload_len), lchan);
return msg; return msg;
} }

View File

@@ -7,6 +7,7 @@
#include <osmocom/core/talloc.h> #include <osmocom/core/talloc.h>
#include <osmocom/core/application.h> #include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmo-bts/logging.h> #include <osmo-bts/logging.h>
#include <osmo-bts/abis.h> #include <osmo-bts/abis.h>
#include <osmo-bts/bts.h> #include <osmo-bts/bts.h>
@@ -44,10 +45,14 @@ void parse_cmdline(int argc, char **argv)
static struct option long_options[] = { static struct option long_options[] = {
{"help", 0, 0, 'h'}, {"help", 0, 0, 'h'},
{"features", 1, 0, 'f'}, {"features", 1, 0, 'f'},
{ "debug", 1, 0, 'd' },
{ "disable-color", 0, 0, 's' },
{ "timestamp", 0, 0, 'T' },
{ "log-level", 1, 0, 'e' },
{0} {0}
}; };
c = getopt_long(argc, argv, "hf:", long_options, &option_index); c = getopt_long(argc, argv, "hf:d:sTe:", long_options, &option_index);
if (c == -1) if (c == -1)
break; break;
@@ -58,6 +63,18 @@ void parse_cmdline(int argc, char **argv)
case 'f': case 'f':
cmdline.features = optarg; cmdline.features = optarg;
break; break;
case 's':
log_set_use_color(osmo_stderr_target, 0);
break;
case 'd':
log_parse_category_mask(osmo_stderr_target, optarg);
break;
case 'T':
log_set_print_timestamp(osmo_stderr_target, 1);
break;
case 'e':
log_set_log_level(osmo_stderr_target, atoi(optarg));
break;
default: default:
/* catch unknown options *as well as* missing arguments. */ /* catch unknown options *as well as* missing arguments. */
fprintf(stderr, "Error in command line options. Exiting.\n"); fprintf(stderr, "Error in command line options. Exiting.\n");
@@ -111,12 +128,14 @@ int main(int argc, char **argv)
struct bsc_oml_host *bsc_oml_host; struct bsc_oml_host *bsc_oml_host;
int i; int i;
parse_cmdline(argc, argv);
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context"); tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
msgb_talloc_ctx_init(tall_bts_ctx, 10*1024); msgb_talloc_ctx_init(tall_bts_ctx, 10*1024);
osmo_init_logging2(tall_bts_ctx, &bts_log_info); osmo_init_logging2(tall_bts_ctx, &bts_log_info);
log_set_print_category(osmo_stderr_target, 1);
log_set_print_category_hex(osmo_stderr_target, 0);
parse_cmdline(argc, argv);
g_bts_sm = gsm_bts_sm_alloc(tall_bts_ctx); g_bts_sm = gsm_bts_sm_alloc(tall_bts_ctx);
if (!g_bts_sm) if (!g_bts_sm)

View File

@@ -987,23 +987,28 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
return rc; return rc;
} }
/* If we got FACCH, the RTP clock needs to account for it, /* When TCH UL receiver in sysmoBTS PHY detects FACCH,
* and if we have rtp continuous-streaming enabled, * it behaves as follows (observed):
* an actual BFI packet will be emitted.
* *
* Only the case of TCH/F is currently handled; the task of * - In each 20 ms window (both TCH/F and TCH/H), a single
* supporting TCH/H is left as a FIXME for other/later * PH-DATA.ind arrives.
* developers. The difficulty with supporting FACCH/H is that * - In the case of TCH/F, this PH-DATA.ind carries GsmL1_Sapi_FacchF
* only one GsmL1_Sapi_FacchH message will be received, * instead of GsmL1_Sapi_TchF.
* covering 40 ms of time, but two RTP "tick" output calls * - In the case of TCH/H, the PH-DATA.ind for block 0 carries
* will need to be made, with appropriately adjusted frame * GsmL1_Sapi_FacchH instead of GsmL1_Sapi_TchH. However,
* numbers. As a further consideration for rtp continuous-streaming * in the following 20 ms window (block 1 of FACCH/H)
* mode, having the two RTP BFI packets sent directly back to back, * a PH-DATA.ind with GsmL1_Sapi_TchH arrives as normal.
* as opposed to proper 20 ms timing, may be so undesirable * Typically the latter PHY message carries a BFI (0 length payload),
* that some deployments may rather disable TCH/H (use only TCH/F) * but nonzero payloads have also been observed under some conditions.
* than deal with the extra pain, or use TCH/H only on SDR-based * (The latter case remains to be investigated further.)
* BTS with osmo-bts-trx where correct timing is easily achieved. */ *
if (data_ind->sapi == GsmL1_Sapi_FacchF) * In those 20 ms windows where we receive PH-DATA.ind for FACCH
* instead of the usual TCH kind, we need to pass an empty payload
* to the upper layers so we can produce the correct effect in
* the outgoing RTP stream depending on configuration.
*/
if (data_ind->sapi == GsmL1_Sapi_FacchF ||
data_ind->sapi == GsmL1_Sapi_FacchH)
l1if_tch_rx_facch(trx, chan_nr, l1p_msg); l1if_tch_rx_facch(trx, chan_nr, l1p_msg);
/* fill L1SAP header */ /* fill L1SAP header */

View File

@@ -77,8 +77,6 @@ static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len
cur[0] |= 0xD0; cur[0] |= 0xD0;
#endif /* USE_L1_RTP_MODE */ #endif /* USE_L1_RTP_MODE */
lchan_set_marker(osmo_fr_is_any_sid(l1_payload), lchan);
return msg; return msg;
} }
@@ -132,8 +130,6 @@ static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload,
cur[0] |= 0xC0; cur[0] |= 0xC0;
#endif /* USE_L1_RTP_MODE */ #endif /* USE_L1_RTP_MODE */
lchan_set_marker(osmo_efr_is_any_sid(l1_payload), lchan);
return msg; return msg;
} }
@@ -218,8 +214,6 @@ static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len
osmo_revbytebits_buf(cur, GSM_HR_BYTES); osmo_revbytebits_buf(cur, GSM_HR_BYTES);
#endif /* USE_L1_RTP_MODE */ #endif /* USE_L1_RTP_MODE */
lchan_set_marker(osmo_hr_check_sid(l1_payload, payload_len), lchan);
return msg; return msg;
} }

View File

@@ -196,9 +196,6 @@ int bts_model_trx_init(struct gsm_bts_trx *trx)
trx->support.chan_modes = NM_IPAC_MASK_CHANM_SPEECH trx->support.chan_modes = NM_IPAC_MASK_CHANM_SPEECH
| NM_IPAC_MASK_CHANM_CSD_NT | NM_IPAC_MASK_CHANM_CSD_NT
| NM_IPAC_MASK_CHANM_CSD_T; | NM_IPAC_MASK_CHANM_CSD_T;
/* TODO: missing rate adaptation for TCH/F14.4 (see OS#6167) */
trx->support.chan_modes &= ~NM_IPAC_F_CHANM_CSD_T_14k4;
trx->support.chan_modes &= ~NM_IPAC_F_CHANM_CSD_NT_14k4;
/* The nominal value for each TRX is later overwritten through VTY cmd /* The nominal value for each TRX is later overwritten through VTY cmd
* 'nominal-tx-power' if present, otherwise through TRXC cmd NOMTXPOWER. * 'nominal-tx-power' if present, otherwise through TRXC cmd NOMTXPOWER.

View File

@@ -166,14 +166,10 @@ int rx_tchf_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
case GSM48_CMODE_SPEECH_V1: /* FR */ case GSM48_CMODE_SPEECH_V1: /* FR */
rc = gsm0503_tch_fr_decode(tch_data, BUFTAIL8(bursts_p), rc = gsm0503_tch_fr_decode(tch_data, BUFTAIL8(bursts_p),
1, 0, &n_errors, &n_bits_total); 1, 0, &n_errors, &n_bits_total);
if (rc == GSM_FR_BYTES) /* only for valid *speech* frames */
lchan_set_marker(osmo_fr_is_any_sid(tch_data), lchan); /* DTXu */
break; break;
case GSM48_CMODE_SPEECH_EFR: /* EFR */ case GSM48_CMODE_SPEECH_EFR: /* EFR */
rc = gsm0503_tch_fr_decode(tch_data, BUFTAIL8(bursts_p), rc = gsm0503_tch_fr_decode(tch_data, BUFTAIL8(bursts_p),
1, 1, &n_errors, &n_bits_total); 1, 1, &n_errors, &n_bits_total);
if (rc == GSM_EFR_BYTES) /* only for valid *speech* frames */
lchan_set_marker(osmo_efr_is_any_sid(tch_data), lchan); /* DTXu */
break; break;
case GSM48_CMODE_SPEECH_AMR: /* AMR */ case GSM48_CMODE_SPEECH_AMR: /* AMR */
/* the first FN 0,8,17 defines that CMI is included in frame, /* the first FN 0,8,17 defines that CMI is included in frame,

View File

@@ -117,14 +117,7 @@ static const uint8_t sched_tchh_ul_csd_map[26] = {
/* TDMA frame number of burst 'a' % 26 is the table index. /* TDMA frame number of burst 'a' % 26 is the table index.
* This mapping is valid for both TCH/H(0) and TCH/H(1). */ * This mapping is valid for both TCH/H(0) and TCH/H(1). */
static const uint8_t sched_tchh_dl_csd_map[26] = { extern const uint8_t sched_tchh_dl_csd_map[26];
[0] = 1, /* TCH/H(0): B0(0 ... 19) */
[1] = 1, /* TCH/H(1): B0(1 ... 20) */
[8] = 1, /* TCH/H(0): B1(8 ... 2) */
[9] = 1, /* TCH/H(1): B1(9 ... 3) */
[17] = 1, /* TCH/H(0): B2(17 ... 10) */
[18] = 1, /* TCH/H(1): B2(18 ... 11) */
};
static int decode_hr_facch(struct l1sched_ts *l1ts, static int decode_hr_facch(struct l1sched_ts *l1ts,
const struct trx_ul_burst_ind *bi) const struct trx_ul_burst_ind *bi)
@@ -239,10 +232,6 @@ int rx_tchh_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
rc = gsm0503_tch_hr_decode2(tch_data, BUFTAIL8(bursts_p), rc = gsm0503_tch_hr_decode2(tch_data, BUFTAIL8(bursts_p),
!sched_tchh_ul_facch_map[bi->fn % 26], !sched_tchh_ul_facch_map[bi->fn % 26],
&n_errors, &n_bits_total); &n_errors, &n_bits_total);
if (rc == GSM_HR_BYTES) { /* only for valid *speech* frames */
bool is_sid = osmo_hr_check_sid(tch_data, GSM_HR_BYTES);
lchan_set_marker(is_sid, lchan); /* DTXu */
}
break; break;
case GSM48_CMODE_SPEECH_AMR: /* AMR */ case GSM48_CMODE_SPEECH_AMR: /* AMR */
/* the first FN 0,8,17 or 1,9,18 defines that CMI is included /* the first FN 0,8,17 or 1,9,18 defines that CMI is included
@@ -427,12 +416,6 @@ int tx_tchh_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 2), 20 * BPLEN); memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 2), 20 * BPLEN);
memset(BUFPOS(bursts_p, 20), 0, 2 * BPLEN); memset(BUFPOS(bursts_p, 20), 0, 2 * BPLEN);
/* for half-rate CSD we dequeue every 4th burst */
if (chan_state->rsl_cmode == RSL_CMOD_SPD_DATA) {
if (!sched_tchh_dl_csd_map[br->fn % 26])
goto send_burst;
}
/* dequeue a TCH and/or a FACCH message to be transmitted */ /* dequeue a TCH and/or a FACCH message to be transmitted */
tch_dl_dequeue(l1ts, br, &msg_tch, &msg_facch); tch_dl_dequeue(l1ts, br, &msg_tch, &msg_facch);
@@ -536,17 +519,25 @@ int tx_tchh_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
break; break;
/* CSD (TCH/H4.8): 6.0 kbit/s radio interface rate */ /* CSD (TCH/H4.8): 6.0 kbit/s radio interface rate */
case GSM48_CMODE_DATA_6k0: case GSM48_CMODE_DATA_6k0:
if (msg_tch == NULL) /* for half-rate CSD we run the encoder every 4th burst (like for TCH/F)
msg_tch = tch_dummy_msgb(4 * 60, 0x01); * because the interleaving is done as specified for the TCH/F9.6 */
gsm0503_tch_hr48_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_tch)); if (sched_tchh_dl_csd_map[br->fn % 26]) {
if (msg_tch == NULL)
msg_tch = tch_dummy_msgb(4 * 60, 0x01);
gsm0503_tch_hr48_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_tch));
}
if (msg_facch != NULL) if (msg_facch != NULL)
gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_facch)); gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_facch));
break; break;
/* CSD (TCH/H2.4): 3.6 kbit/s radio interface rate */ /* CSD (TCH/H2.4): 3.6 kbit/s radio interface rate */
case GSM48_CMODE_DATA_3k6: case GSM48_CMODE_DATA_3k6:
if (msg_tch == NULL) /* for half-rate CSD we run the encoder every 4th burst (like for TCH/F)
msg_tch = tch_dummy_msgb(4 * 36, 0x01); * because the interleaving is done as specified for the TCH/F9.6 */
gsm0503_tch_hr24_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_tch)); if (sched_tchh_dl_csd_map[br->fn % 26]) {
if (msg_tch == NULL)
msg_tch = tch_dummy_msgb(4 * 36, 0x01);
gsm0503_tch_hr24_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_tch));
}
if (msg_facch != NULL) if (msg_facch != NULL)
gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_facch)); gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_facch));
break; break;

View File

@@ -411,6 +411,7 @@ static int trx_fn_timer_cb(struct osmo_fd *ofd, unsigned int what)
struct timespec tv_now; struct timespec tv_now;
uint64_t expire_count; uint64_t expire_count;
int64_t elapsed_us, error_us; int64_t elapsed_us, error_us;
const char *reason = NULL;
int rc, i; int rc, i;
if (!(what & OSMO_FD_READ)) if (!(what & OSMO_FD_READ))
@@ -430,8 +431,9 @@ static int trx_fn_timer_cb(struct osmo_fd *ofd, unsigned int what)
/* check if transceiver is still alive */ /* check if transceiver is still alive */
if (tcs->fn_without_clock_ind++ == TRX_LOSS_FRAMES) { if (tcs->fn_without_clock_ind++ == TRX_LOSS_FRAMES) {
LOGP(DL1C, LOGL_NOTICE, "No more clock from transceiver\n"); reason = "No more clock from transceiver";
goto no_clock; LOGP(DL1C, LOGL_ERROR, "%s\n", reason);
goto shutdown;
} }
/* compute actual elapsed time and resulting OS scheduling error */ /* compute actual elapsed time and resulting OS scheduling error */
@@ -446,9 +448,11 @@ static int trx_fn_timer_cb(struct osmo_fd *ofd, unsigned int what)
/* if someone played with clock, or if the process stalled */ /* if someone played with clock, or if the process stalled */
if (elapsed_us > GSM_TDMA_FN_DURATION_uS * MAX_FN_SKEW || elapsed_us < 0) { if (elapsed_us > GSM_TDMA_FN_DURATION_uS * MAX_FN_SKEW || elapsed_us < 0) {
LOGP(DL1C, LOGL_ERROR, "PC clock skew: elapsed_us=%" PRId64 ", error_us=%" PRId64 "\n", LOGP(DL1C, LOGL_ERROR,
elapsed_us, error_us); "PC clock skew: elapsed_us=%" PRId64 ", error_us=%" PRId64 "\n",
goto no_clock; elapsed_us, error_us);
reason = "PC clock skew too high";
goto shutdown;
} }
/* call bts_sched_fn() for all expired FN */ /* call bts_sched_fn() for all expired FN */
@@ -457,9 +461,9 @@ static int trx_fn_timer_cb(struct osmo_fd *ofd, unsigned int what)
return 0; return 0;
no_clock: shutdown:
osmo_timerfd_disable(&tcs->fn_timer_ofd); osmo_timerfd_disable(&tcs->fn_timer_ofd);
bts_shutdown(bts, "No clock from osmo-trx"); bts_shutdown(bts, reason);
return -1; return -1;
} }

View File

@@ -99,9 +99,6 @@ int bts_model_trx_init(struct gsm_bts_trx *trx)
trx->support.chan_modes = NM_IPAC_MASK_CHANM_SPEECH trx->support.chan_modes = NM_IPAC_MASK_CHANM_SPEECH
| NM_IPAC_MASK_CHANM_CSD_NT | NM_IPAC_MASK_CHANM_CSD_NT
| NM_IPAC_MASK_CHANM_CSD_T; | NM_IPAC_MASK_CHANM_CSD_T;
/* TODO: missing rate adaptation for TCH/F14.4 (see OS#6167) */
trx->support.chan_modes &= ~NM_IPAC_F_CHANM_CSD_T_14k4;
trx->support.chan_modes &= ~NM_IPAC_F_CHANM_CSD_NT_14k4;
return 0; return 0;
} }

View File

@@ -53,12 +53,24 @@ static const struct test_case tests[] = {
.tch_mode = GSM48_CMODE_DATA_12k0, .tch_mode = GSM48_CMODE_DATA_12k0,
.csd_mode = LCHAN_CSD_M_T_9600, .csd_mode = LCHAN_CSD_M_T_9600,
}, },
{
.name = "TCH/F9.6 NT",
.lchan_type = GSM_LCHAN_TCH_F,
.tch_mode = GSM48_CMODE_DATA_12k0,
.csd_mode = LCHAN_CSD_M_NT,
},
{ {
.name = "TCH/F4.8", .name = "TCH/F4.8",
.lchan_type = GSM_LCHAN_TCH_F, .lchan_type = GSM_LCHAN_TCH_F,
.tch_mode = GSM48_CMODE_DATA_6k0, .tch_mode = GSM48_CMODE_DATA_6k0,
.csd_mode = LCHAN_CSD_M_T_4800, .csd_mode = LCHAN_CSD_M_T_4800,
}, },
{
.name = "TCH/F4.8 NT",
.lchan_type = GSM_LCHAN_TCH_F,
.tch_mode = GSM48_CMODE_DATA_6k0,
.csd_mode = LCHAN_CSD_M_NT,
},
{ {
.name = "TCH/H4.8", .name = "TCH/H4.8",
.lchan_type = GSM_LCHAN_TCH_H, .lchan_type = GSM_LCHAN_TCH_H,
@@ -81,7 +93,7 @@ static const struct test_case tests[] = {
static void exec_test_case(const struct test_case *tc) static void exec_test_case(const struct test_case *tc)
{ {
const struct csd_v110_frame_desc *desc; const struct csd_v110_lchan_desc *desc;
uint8_t rtp[RFC4040_RTP_PLEN] = { 0 }; uint8_t rtp[RFC4040_RTP_PLEN] = { 0 };
ubit_t data_enc[BBUF_MAX]; ubit_t data_enc[BBUF_MAX];
ubit_t data_dec[BBUF_MAX]; ubit_t data_dec[BBUF_MAX];
@@ -89,13 +101,10 @@ static void exec_test_case(const struct test_case *tc)
/* obtain a V.110 frame description for the given channel type/rate */ /* obtain a V.110 frame description for the given channel type/rate */
OSMO_ASSERT(tc->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc)); OSMO_ASSERT(tc->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc));
if (tc->lchan_type == GSM_LCHAN_TCH_F) desc = &csd_v110_lchan_desc[tc->tch_mode];
desc = &csd_v110_lchan_desc[tc->tch_mode].fr;
else
desc = &csd_v110_lchan_desc[tc->tch_mode].hr;
/* total number of bits carried by a radio interface block */ /* total number of bits carried by a radio interface block */
const unsigned int bit_num = desc->num_bits * desc->num_blocks; const unsigned int bit_num = CSD_V110_NUM_BITS(desc);
if (bit_num == 0) { if (bit_num == 0) {
fprintf(stderr, "[i] Skipping '%s' (not implemented)\n", tc->name); fprintf(stderr, "[i] Skipping '%s' (not implemented)\n", tc->name);
return; return;
@@ -115,7 +124,7 @@ static void exec_test_case(const struct test_case *tc)
data_enc[i] = i & 0x01; data_enc[i] = i & 0x01;
/* encode an RTP frame and print it */ /* encode an RTP frame and print it */
rc = csd_v110_rtp_encode(&lchan, &rtp[0], &data_enc[0], bit_num); rc = csd_v110_rtp_encode(&lchan, &rtp[0], &data_enc[0], bit_num, 0);
fprintf(stderr, "[i] csd_v110_rtp_encode() returns %d\n", rc); fprintf(stderr, "[i] csd_v110_rtp_encode() returns %d\n", rc);
if (rc != RFC4040_RTP_PLEN) if (rc != RFC4040_RTP_PLEN)
return; return;
@@ -124,7 +133,7 @@ static void exec_test_case(const struct test_case *tc)
fprintf(stderr, " %s\n", osmo_hexdump(&rtp[i * 16], 16)); fprintf(stderr, " %s\n", osmo_hexdump(&rtp[i * 16], 16));
/* decode the encoded RTP frame */ /* decode the encoded RTP frame */
rc = csd_v110_rtp_decode(&lchan, &data_dec[0], &rtp[0], sizeof(rtp)); rc = csd_v110_rtp_decode(&lchan, &data_dec[0], NULL, &rtp[0], sizeof(rtp));
fprintf(stderr, "[i] csd_v110_rtp_decode() returns %d\n", rc); fprintf(stderr, "[i] csd_v110_rtp_decode() returns %d\n", rc);
if (rc != bit_num) if (rc != bit_num)
return; return;
@@ -136,10 +145,14 @@ static void exec_test_case(const struct test_case *tc)
i, data_dec[i], data_enc[i]); i, data_dec[i], data_enc[i]);
} }
/* for TCH/F14.4, we always expect a valid block */
if (tc->tch_mode == GSM48_CMODE_DATA_14k5)
return;
fprintf(stderr, "[i] Testing '%s' (IDLE)\n", tc->name); fprintf(stderr, "[i] Testing '%s' (IDLE)\n", tc->name);
/* encode an idle RTP frame and print it */ /* encode an idle RTP frame and print it */
rc = csd_v110_rtp_encode(&lchan, &rtp[0], &data_enc[0], 0); rc = csd_v110_rtp_encode(&lchan, &rtp[0], &data_enc[0], 0, 0);
fprintf(stderr, "[i] csd_v110_rtp_encode() returns %d\n", rc); fprintf(stderr, "[i] csd_v110_rtp_encode() returns %d\n", rc);
if (rc != RFC4040_RTP_PLEN) if (rc != RFC4040_RTP_PLEN)
return; return;

View File

@@ -1,4 +1,16 @@
[i] Skipping 'TCH/F14.4' (not implemented) [i] Testing 'TCH/F14.4' (bitnum=290)
[i] csd_v110_rtp_encode() returns 160
3f 3f 3f 3f 3f 3f 3f 3f bf ff bf 7f bf bf bf bf
bf bf bf bf bf bf bf bf bf bf bf bf bf bf ff 7f
7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
7f bf bf bf bf bf bf bf bf bf bf bf bf bf bf bf
bf bf bf ff 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
7f 7f 7f 7f 7f 7f bf bf bf bf bf bf bf bf bf bf
bf bf bf bf bf bf bf bf ff 7f 7f 7f 7f 7f 7f 7f
7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f bf bf bf bf bf
bf bf bf bf bf bf bf bf bf bf bf bf bf ff 7f 7f
7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
[i] csd_v110_rtp_decode() returns 290
[i] Testing 'TCH/F9.6' (bitnum=240) [i] Testing 'TCH/F9.6' (bitnum=240)
[i] csd_v110_rtp_encode() returns 160 [i] csd_v110_rtp_encode() returns 160
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf 3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf
@@ -13,6 +25,31 @@
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f
[i] csd_v110_rtp_decode() returns 240 [i] csd_v110_rtp_decode() returns 240
[i] Testing 'TCH/F9.6' (IDLE) [i] Testing 'TCH/F9.6' (IDLE)
[i] csd_v110_rtp_encode() returns 160
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff 3f 3f 3f 3f ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff 3f 3f 3f 3f ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[i] Testing 'TCH/F9.6 NT' (bitnum=240)
[i] csd_v110_rtp_encode() returns 160
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf
ff 7f 7f 7f bf 3f 7f 7f bf bf bf bf ff 7f 7f 7f
bf bf bf bf ff 7f 7f 7f 3f 3f 3f 3f bf bf bf bf
ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f bf 7f 7f 7f
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf
ff 7f 7f 7f bf bf 7f 7f bf bf bf bf ff 7f 7f 7f
bf bf bf bf ff 7f 7f 7f 3f 3f 3f 3f bf bf bf bf
ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f bf ff 7f 7f
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f
[i] csd_v110_rtp_decode() returns 240
[i] Testing 'TCH/F9.6 NT' (IDLE)
[i] csd_v110_rtp_encode() returns 160 [i] csd_v110_rtp_encode() returns 160
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff 3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
@@ -49,29 +86,54 @@
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[i] Testing 'TCH/H4.8' (bitnum=240) [i] Testing 'TCH/F4.8 NT' (bitnum=120)
[i] csd_v110_rtp_encode() returns 160 [i] csd_v110_rtp_encode() returns 160
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf 7f 7f 7f 7f 7f 7f 7f 7f ff 7f ff 7f ff 7f ff 7f
ff 7f 7f 7f bf ff 7f 7f bf bf bf bf ff 7f 7f 7f ff ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff 7f
bf bf bf bf ff 7f 7f 7f 3f 3f 3f 3f bf bf bf bf ff ff 7f ff 7f ff 7f ff ff 7f 7f 7f 7f ff 7f ff
ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f bf ff 7f 7f ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf 7f 7f 7f 7f 7f 7f 7f 7f ff 7f ff 7f ff 7f ff 7f
ff 7f 7f 7f bf ff 7f 7f bf bf bf bf ff 7f 7f 7f ff ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff 7f
bf bf bf bf ff 7f 7f 7f 3f 3f 3f 3f bf bf bf bf ff ff 7f ff 7f ff 7f ff ff 7f 7f ff 7f ff 7f ff
ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f bf ff 7f 7f ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
[i] csd_v110_rtp_decode() returns 240 [i] csd_v110_rtp_decode() returns 120
[i] Testing 'TCH/F4.8 NT' (IDLE)
[i] csd_v110_rtp_encode() returns 160
7f 7f 7f 7f 7f 7f 7f 7f ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
7f 7f 7f 7f 7f 7f 7f 7f ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[i] Testing 'TCH/H4.8' (bitnum=120)
[i] csd_v110_rtp_encode() returns 160
7f 7f 7f 7f 7f 7f 7f 7f ff 7f ff 7f ff 7f ff 7f
ff ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff 7f
ff ff 7f ff 7f ff 7f ff ff 7f ff ff 7f ff 7f ff
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
7f 7f 7f 7f 7f 7f 7f 7f ff 7f ff 7f ff 7f ff 7f
ff ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff 7f
ff ff 7f ff 7f ff 7f ff ff 7f ff ff 7f ff 7f ff
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
[i] csd_v110_rtp_decode() returns 120
[i] Testing 'TCH/H4.8' (IDLE) [i] Testing 'TCH/H4.8' (IDLE)
[i] csd_v110_rtp_encode() returns 160 [i] csd_v110_rtp_encode() returns 160
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff 7f 7f 7f 7f 7f 7f 7f 7f ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff 3f 3f 3f 3f ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff 3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
7f 7f 7f 7f 7f 7f 7f 7f ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[i] Testing 'TCH/F2.4' (bitnum=72) [i] Testing 'TCH/F2.4' (bitnum=72)
@@ -99,28 +161,28 @@
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
[i] Testing 'TCH/H2.4' (bitnum=144) [i] Testing 'TCH/H2.4' (bitnum=72)
[i] csd_v110_rtp_encode() returns 160 [i] csd_v110_rtp_encode() returns 160
3f 3f 3f 3f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f 7f 7f 7f 7f 7f 7f 7f 7f ff 7f 7f ff ff 7f 7f ff
bf 7f bf 7f ff 3f 7f 7f bf 7f bf 7f bf 7f bf 7f ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
bf 7f bf 7f bf 7f bf 7f 3f 3f 3f 3f bf 7f bf 7f ff 7f 7f ff ff 7f 7f ff ff ff 7f 7f 7f ff 7f ff
bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f ff 3f 7f 7f ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
3f 3f 3f 3f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f 7f 7f 7f 7f 7f 7f 7f 7f ff 7f 7f ff ff 7f 7f ff
bf 7f bf 7f ff 3f 7f 7f bf 7f bf 7f bf 7f bf 7f ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
bf 7f bf 7f bf 7f bf 7f 3f 3f 3f 3f bf 7f bf 7f ff 7f 7f ff ff 7f 7f ff ff ff 7f 7f 7f ff 7f ff
bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f ff 3f 7f 7f ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
[i] csd_v110_rtp_decode() returns 144 [i] csd_v110_rtp_decode() returns 72
[i] Testing 'TCH/H2.4' (IDLE) [i] Testing 'TCH/H2.4' (IDLE)
[i] csd_v110_rtp_encode() returns 160 [i] csd_v110_rtp_encode() returns 160
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff 7f 7f 7f 7f 7f 7f 7f 7f ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff 3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
7f 7f 7f 7f 7f 7f 7f 7f ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff 3f 3f 3f 3f ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff