Compare commits

..

149 Commits

Author SHA1 Message Date
Harald Welte
f1fb0fa3af Merge branch '201509-trx-rebase' 2015-09-22 16:41:54 +02:00
Harald Welte
caa648d92e TRX: Add missing call to abis_init()
This somehow got lost during the latest rebase.
2015-09-22 16:41:32 +02:00
Harald Welte
88a31e2a99 make osmo-bts-trx provide bts_model_adjst_ms_pwr() 2015-09-22 16:41:32 +02:00
Harald Welte
307bfc81c1 fixup tests after bts_model_adjst_ms_pwr 2015-09-22 16:41:32 +02:00
Alexander Chemeris
5becc4613a tests: Update busrsts_test build.
We've added logging calls to the bursts processing. Add logging facility
initializatoin to the test code.
2015-09-22 16:41:31 +02:00
Alexander Chemeris
b812839dfa trx: fix potential use of uninitialized toa variable.
Not really a bug, as we're smart about it down the stream, but it's better to
be strict here as well.
2015-09-22 16:41:31 +02:00
Alexander Chemeris
ae525a8761 trx: Send POWERON/OFF commands to osmo-bts only for the first channel.
osmo-trx never supported separate power control for trx's, but now it started
to be more strict about it.
2015-09-22 16:41:31 +02:00
Alexander Chemeris
29ea40f538 trx: Assume 100% BER if total decoded bits is 0 in l1if_process_meas_res() 2015-09-22 16:41:31 +02:00
Alexander Chemeris
e9abc5a4f3 trx: Cleanup unused parts of loops.c 2015-09-22 16:41:31 +02:00
Alexander Chemeris
17be7fa73b trx: Remove unused variables. 2015-09-22 16:41:31 +02:00
Alexander Chemeris
68e8b2b1d5 trx: Fix typo in a log message. 2015-09-22 16:41:31 +02:00
Alexander Chemeris
391ff14977 trx: More logging for voice frame decoding functions. 2015-09-22 16:41:31 +02:00
Alexander Chemeris
cf18dcd5fd tests: Update bursts_test to accommodate BER calculations. 2015-09-22 16:41:31 +02:00
Alexander Chemeris
6fceaca584 trx: Implement BER calculations.
A known issue with this code is that BER is not updated for lost TCH frames,
because osmo-trx doesn't send any indication for them and we don't have
a callback to handle this.

Otherwise the code seem to work fine.
2015-09-22 16:41:31 +02:00
Thomas Tsou
ddc0bf14d5 TRX: Remove extra TCH/HS puncturing value
3GPP TS 05.03 "Channel coding" specifies the puncturing matrix (1,0,1)
for class 1 information bits and tail bits valued u(0) to u(103) for a
maximum puncturing index of 311. The puncturing index 313 exceeds the
maximum index and causes osmo_conv_get_output_length() to output the
improper length of 210 instead of 211.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2015-09-22 16:41:31 +02:00
Andreas Eversberg
deb01a2652 TRX: Check if Transceiver indicates an out of range clock
If frame number is out of range (>= 2715648), the scheduler's process
would end up in an infinite loop. This is because the loop would schedule
bursts until the indicated frame number is reached, which would not be
possible.

The openbts, calypso-bts and osmo-trx might send out out of range clock
indications every 3.5 hour.
2015-09-22 16:41:30 +02:00
Andreas Eversberg
3cfc9d5fa3 TRX: Show which TRX does not respond or rejects a command 2015-09-22 16:41:30 +02:00
Andreas Eversberg
a7d0c5ef5a trx: Set lchan inactive, only if the dedicated channel is deactivated 2015-09-22 16:41:30 +02:00
Andreas Eversberg
f39c739bd6 TRX: Activate LCHAN of CCCH when CCCH is configured on time slot 2015-09-22 16:41:30 +02:00
Andreas Eversberg
f66f5b3ddc TRX: Free bust buffer memory to when changing lchan type 2015-09-22 16:41:30 +02:00
Andreas Eversberg
c241afa87c TRX: Add VTY option to allow setting RTS advance in frames
RTS (ready-to-send) must be issued in advance, so BTS core and especially
osmo-pcu can provide downlink data frames early enough. In some cases PCU
might provide frames too late, so they must be dropped. If PCU provides
frames too late, due to high system load, this "RTS advance" setting must
be increased.
2015-09-22 16:41:30 +02:00
Martin Hauke
178d618d5a TRX: fix some typos in comments 2015-09-22 16:41:30 +02:00
Martin Hauke
c9ddb2ba22 build: Use AM_CPPFLAGS in Makefile.am
Since automake 1.13 INCLUDES is depricated and causes a warning
Inspired from similar patches by Alexander Huemer for other osmocom
projects.
2015-09-22 16:41:30 +02:00
Martin Hauke
73d3f46994 tests: make tests for sysmobts conditional 2015-09-22 16:41:30 +02:00
Andreas Eversberg
b2482a8574 Allow TRX 0..254 at VTY, even if less TRX are available
Instead of limiting the number of TRX at VTY to the actual number of
supported TRX, VTY allows to configure any possible number of TRX. If a
TRX is configured, which is not supported by BTS model, an error message is
returned, which states that the given TRX is not supported.
2015-09-22 16:41:30 +02:00
Andreas Eversberg
812fdd92c7 TRX: Changed logging of unserved primitives from LOGL_NOTICE to LOGL_INFO 2015-09-22 16:41:30 +02:00
Andreas Eversberg
ec6225e3e0 TRX: Fixed chan_nr for SACCH/8(7) at scheduler 2015-09-22 16:41:29 +02:00
Andreas Eversberg
ef6eb5442c trx: Add option to set transmit power reduction via OML (BSC) 2015-09-22 16:41:29 +02:00
Andreas Eversberg
f0072a8de8 TRX: Do not send burst on IDLE channels at TRX != C0
This is required, so the transceiver transmits no power.
2015-09-22 16:41:29 +02:00
Andreas Eversberg
3cf28aa924 TRX: Close TRX (shutdown all active channels) on ABIS link failure 2015-09-22 16:41:29 +02:00
Andreas Eversberg
578340c7a7 TRX: Add bts_model_trx_close to TRX implementation 2015-09-22 16:41:29 +02:00
Andreas Eversberg
3caf3b7c45 TRX: Fixup ciphering state names after rebasing 2015-09-22 16:41:29 +02:00
Andreas Eversberg
ee47913389 TRX: No need to set mode and cipher for PDCH 2015-09-22 16:41:29 +02:00
Andreas Eversberg
f5aaf523c5 TRX: If no cipher algorithm is given, or if it is a5/0, reset cipher state 2015-09-22 16:41:29 +02:00
Andreas Eversberg
8c8998e551 TRX: Set ciphering to an initial state when activating channel
Handover and assignment may activate channels with ciphering already set,
so we need to tell scheduler to enable/disable ciphering and set the
correct cipher state.
2015-09-22 16:41:29 +02:00
Andreas Eversberg
da0c44a9db Add test case for successful handover and unsuccessful handover 2015-09-22 16:41:29 +02:00
Andreas Eversberg
798c1bba9d TRX: Process real time scheduling option is now similar to sysmobts 2015-09-22 16:41:29 +02:00
Andreas Eversberg
db0b93ac39 TRX: Disable handover burst detection when closing channel during detection 2015-09-22 16:41:29 +02:00
Andreas Eversberg
86c936cbb1 TRX: Use correct slot type for GSM_PHCAN_BCCH 2015-09-22 16:41:28 +02:00
Andreas Eversberg
b9a917a138 TRX: Handover access burst support 2015-09-22 16:41:28 +02:00
Andreas Eversberg
6527dffc94 TRX: Clear lchan state when resetting TRX 2015-09-22 16:41:28 +02:00
Andreas Eversberg
fb04746bce TRX: Report measurements 2015-09-22 16:41:28 +02:00
Andreas Eversberg
05597a7ddb TRX: Fixed typos tranceiver -> transceiver 2015-09-22 16:41:28 +02:00
Andreas Eversberg
82676c13ee TRX: Fix: Cleanly free TRX instances during initialization in case of an error 2015-09-22 16:41:28 +02:00
Andreas Eversberg
c2ee307fd4 Allow one or more TRX to configure via VTY 2015-09-22 16:41:28 +02:00
Andreas Eversberg
2e4a26a0e9 TRX: Add VTY options to enable and disable SETTSC and SETBSIC 2015-09-22 16:41:28 +02:00
Andreas Eversberg
6508f21130 TRX: Reset ciphering state when closing channel 2015-09-22 16:41:28 +02:00
Andreas Eversberg
c5241c3aa4 TRX: Support for AMR half speech 2015-09-22 16:41:28 +02:00
Andreas Eversberg
c910a332b2 TRX: Support for TCH/H and GSM half rate transcoding 2015-09-22 16:41:27 +02:00
Andreas Eversberg
f62a64e440 TRX: Add AMR Payload handling 2015-09-22 16:41:27 +02:00
Andreas Eversberg
a7f5e07712 TRX: Support for AMR full speech 2015-09-22 16:41:27 +02:00
Andreas Eversberg
5e2341411f Get RSSI from received uplink data and send to PCU 2015-09-22 16:41:27 +02:00
Andreas Eversberg
917cf7018b TRX: Add support for EFR transcoding 2015-09-22 16:41:27 +02:00
Andreas Eversberg
84b9a44535 TRX: Code cleanup, prepare for other codecs than GSM full rate 2015-09-22 16:41:27 +02:00
Andreas Eversberg
7ff22823ca TRX: Use link timeout value from BSC via OML attribute. 2015-09-22 16:41:27 +02:00
Andreas Eversberg
9855e8bd48 TRX: Out of range primitives found in downlink queue are not an error 2015-09-22 16:41:27 +02:00
Andreas Eversberg
219ece83a3 TRX: Implementation of MS power and timing advance loops 2015-09-22 16:41:27 +02:00
Andreas Eversberg
889890da43 TRX: Improved handling of clock indications.
If no clock is received, a POWEROFF is sent until clock is detected.
2015-09-22 16:41:27 +02:00
Andreas Eversberg
23a5183767 TRX: Fixes to TRX interface
Ignore false response to uncritical commands.
2015-09-22 16:41:27 +02:00
Andreas Eversberg
ce0f20b597 TRX: Fix of SCH burst data 2015-09-22 16:41:26 +02:00
Andreas Eversberg
7bd6e8b89b TRX: Ciphering 2015-09-22 16:41:26 +02:00
Andreas Eversberg
d692b6e054 TRX: Replaced GSM 06.10 ordering table by table in libosmocodec 2015-09-22 16:41:26 +02:00
Andreas Eversberg
89e36c0e64 TRX: Cleanup of channel transcoding 2015-09-22 16:41:26 +02:00
Andreas Eversberg
801c182c02 TRX: By default, send 20 frames in advance to tranceiver 2015-09-22 16:41:26 +02:00
Andreas Eversberg
7451ce29a7 TRX: Detect missing received bursts and fill them with zero-sbits 2015-09-22 16:41:26 +02:00
Andreas Eversberg
450d32919a TRX: Add test code for PDTCH transcoding 2015-09-22 16:41:26 +02:00
Andreas Eversberg
78b2080027 TRX: PDTCH (GPRS) works now
Detection and transcoding of all four coding schemes are supported.
2015-09-22 16:41:26 +02:00
Andreas Eversberg
9de67ca962 TRX: Lost TCH frame detection of omitted bursts from tranceiver 2015-09-22 16:41:26 +02:00
Andreas Eversberg
b9880bc812 TRX: Allow transcoding of TCH FR with MSB first (RTP) or LSB first (E1) 2015-09-22 16:41:26 +02:00
Andreas Eversberg
d10eaee4cc TRX: Completed TCH/F full rate support
Full rate is now tested and working.
2015-09-22 16:41:25 +02:00
Andreas Eversberg
b104aed5ec TRX: Fixed swapped stealing bits
Thanx to Sylvain for pointing to this bug.
2015-09-22 16:41:25 +02:00
Andreas Eversberg
cd463dd72a TRX: Minor fixes, especially handle TOA of RACH correctly 2015-09-22 16:41:25 +02:00
Andreas Eversberg
7d684d6866 TRX: Fix, never send confirm for DEACT SACCH request (TS 05.08 4.6)
Sending it would cause BSC to change to a state, where it does not release
rf channel.
2015-09-22 16:41:25 +02:00
Andreas Eversberg
e0959e7929 TRX: Use received TRX clocks to determine availablility of tranceiver
Only if transceiver becomes available, control commands are sent. If
tranceiver is gone, reset scheduler.

The current availability state is sent to BSC via OML state change
commands.
2015-09-22 16:41:25 +02:00
Andreas Eversberg
2ea68e2b7b TRX: Fixes and improvements of scheduler 2015-09-22 16:41:25 +02:00
Andreas Eversberg
2c8787224f Fix: Check right result on bursts_test 2015-09-22 16:41:25 +02:00
Andreas Eversberg
74d63b7212 Add test routing to test transcoding of TCH FR / FACCH frames 2015-09-22 16:41:25 +02:00
Andreas Eversberg
d0603d96e9 TRX: Completed transcoding of TCH with reordering Table 2 of TS 05.03 2015-09-22 16:41:25 +02:00
Andreas Eversberg
414faaca19 TRX: Power down tranceiver and reset scheduler, if abis link is lost
If BTS is gone, TRX is powered down, due to loss of abis link. If link is
esablished again, tranceiver and scheduler are provisioned again by BTS.
2015-09-22 16:41:25 +02:00
Andreas Eversberg
7a0d11dd68 ABIS: Introduce bts_model_abis_close to indicate ABIS link failure.
sysmocom-bts model shuts down on link loss, but other models may not want
this, so shutdown is moved tor bts_model_abis_close of osmo-bts-sysmo.
2015-09-22 16:41:25 +02:00
Andreas Eversberg
cd0581d815 TRX: On negative response of critical commands, shutdown BTS 2015-09-22 16:41:24 +02:00
Andreas Eversberg
1de7085d31 Add test code for testing GSM burst transcoding 2015-09-22 16:41:24 +02:00
Andreas Eversberg
acc71ffb4b TRX: Introduce osmobts-trx, a layer 1 implementation for OpenBTS tranceivers
The code is quite complete, TCH and PDCH channels are not yet tested.
2015-09-22 16:41:24 +02:00
Andreas Eversberg
c64fa4f888 Change to new structure of multirate at gsm_data_shared.h 2015-09-22 16:41:24 +02:00
Andreas Eversberg
79bc80102c Fix: Call e1inp_vty_init() before reading config file 2015-09-22 16:41:24 +02:00
Andreas Eversberg
5fa388c366 Fix: Process all TRX on GSM Time indication, not only C0 2015-09-22 16:41:24 +02:00
Andreas Eversberg
75f105bbb5 Fix: Retrieve ARFCN (from OML) for TRX other than C0 2015-09-22 16:41:24 +02:00
Ivan Kluchnikov
2340b88ede fix: make sysmobts tests only when sysmobts is enabled 2015-09-22 16:41:24 +02:00
Harald Welte
329085a8ff Merge branch '201509-l1sap' 2015-09-22 16:39:55 +02:00
Harald Welte
819b50e1a7 move MS power control handling from sysmobts to common part
MS uplink power control is required in pretty much any BTS, and we
cannot assume that they PHY / L1 will always take care of it by
itself.   So the correspondign code is moved to common/power_control.c
and called from the generic part of L1SAP.

The corresponding VTY paramter has been moved from the sysmobts-specific
trx VTY node to the common BTS VTY node.
2015-09-22 16:39:05 +02:00
Andreas Eversberg
f449842053 Move detection of handover frames from sysmo-bts code to common code 2015-09-22 16:39:05 +02:00
Andreas Eversberg
9cfbf27d4c Remove obsolete gsmtap handling from osmo-bts-sysmo part. 2015-09-22 16:39:05 +02:00
Andreas Eversberg
a450ef73ed Add gsmtap option to command line to main.c of osmo-bts-sysmo 2015-09-22 16:39:04 +02:00
Andreas Eversberg
04b5d65575 Move gsmtap VTY commands from osmo-bts-sysmo to common part 2015-09-22 16:39:04 +02:00
Andreas Eversberg
90e543bd83 Send primitives at PH-/MPH-/TCH-SAP interface via GSMTAP 2015-09-22 16:39:04 +02:00
Andreas Eversberg
75caaf2949 sysmobts: Clean up transitions for lchan cipher state
There are three transitions:

1. LCHAN_CIPH_NONE -> LCHAN_CIPH_RX_REQ -> LCHAN_CIPH_RX_CONF

It is used to enable ciphering in RX (uplink) direction only.

2. LCHAN_CIPH_RX_CONF -> LCHAN_CIPH_RX_CONF_TX_REQ -> LCHAN_CIPH_RXTX_CONF

It is used to additionally enable ciphering in TX (downlink) direction.

3. LCHAN_CIPH_NONE -> LCHAN_CIPH_RXTX_REQ -> LCHAN_CIPH_RX_CONF_TX_REQ
   -> LCHAN_CIPH_RXTX_CONF

It is used to enable ciphering in both TX and RX directions. This is used
when the channel is activated with encryption already enabled. (assignment
or handover)

In order to follow the order of these transitions, the RX direction must
always be set before the TX direction.

If no cipher key is set (A5/0), ciphering is set to ALG 0, but lchan cipher
state remains at LCHAN_CIPH_NONE.
2015-09-22 16:39:04 +02:00
Andreas Eversberg
5027e122a8 Add MEAS (MPH_INFO) IND message to PH-/MPH-/TCH-SAP interface
This part moves processing of measurement infos from osmo-bts-sysmo to
common part.
2015-09-22 16:39:04 +02:00
Harald Welte
a313bb0a47 l1sap: Port code to new ciphering handling
... introduced in 2cc37035d7
2015-09-22 16:39:04 +02:00
Harald Welte
923e324abc sysmobts/l1_if: Sacch/Sdcc/Facch are handled in l1sap/core 2015-09-22 16:39:04 +02:00
Andreas Eversberg
bac087c207 Add SDCCH/SACCH/FACCH messages to PH-/MPH-/TCH-SAP interface
This part moves control channel message primitives from osmo-bts-sysmo to
common part.

In order to control ciphering fo BTS model, CIPHER (MPH_INFO) messages are
used.
2015-09-22 16:39:04 +02:00
Harald Welte
80f039973e l1sap: Avoid compiler warnings regarding uninitialized nmsg 2015-09-22 16:39:04 +02:00
Harald Welte
3a381367a6 l1sap: Use {data,empty}_req_from_l1sap() and avoid code duplication 2015-09-22 16:39:04 +02:00
Andreas Eversberg
12472df8f0 Add TCH messages to PH-/MPH-/TCH-SAP interface
This part moves TCH handling from osmo-bts-sysmo to common part. The RTP
handling is done at the common part, so they can be used by other BTS
models.
2015-09-22 16:39:04 +02:00
Harald Welte
7cc199ea95 l1sap: re-introduce a comment that was lost during l1sap merge 2015-09-22 16:39:03 +02:00
Andreas Eversberg
793e713c4b Move chan act/rel/modify from bts_model to PH-/MPH-/TCH-SAP interface
This part replaces channel activation/deactivation/modification routines
by MPH_INFO messages.
2015-09-22 16:39:03 +02:00
Andreas Eversberg
faba84b9b7 Relace bts_model_get_time() by get_time() at common part 2015-09-22 16:39:03 +02:00
Harald Welte
7cf313c75b l1sap: Re-introduce more correct RACH slot counting
The original code handled both the fact where a TIME indication would be
missed (and thus the frame number be higher than previous + 1), as well
as the two cases for combined / non-combined CCCH.

The L1SAP code removed some of those bits, which I'm re-introducing
here.
2015-09-22 16:39:03 +02:00
Andreas Eversberg
21b5e6318e Add TIME (MPH_INFO) IND messages to PH-/MPH-/TCH-SAP interface
This part moves GSM time handling from osmo-bts-sysmo part to common part.
2015-09-22 16:39:03 +02:00
Harald Welte
4fe00da9f8 l1sap: additional comments explaining l1sap changes in l1_if.c 2015-09-22 16:39:03 +02:00
Andreas Eversberg
75be092b99 Add PDCH messages to PH-/MPH-/TCH-SAP interface
This part moves PDTCH, PACCH and PTCCH message primitives from
osmo-bts-sysmo to common part.
2015-09-22 16:39:03 +02:00
Harald Welte
c9441b3c0b l1sap: Add a warning about assuming BS_AG_BLKS_RES=1
This is a regression of the code compared to the existing
sysmoBTS code, where the L1 tells us whether its AGCH or
PCH.  However, it was not used even in the old code, so
we can afford to simply put a #warning here.
2015-09-22 16:39:03 +02:00
Andreas Eversberg
ace9a8742f Add PCH/AGCH message to PH-/MPH-/TCH-SAP interface
This part moves PCH and AGCH message primitives from osmo-bts-sysmo to
common part.
2015-09-22 16:39:03 +02:00
Harald Welte
54eceac257 l1sap: sysmobts: remove obsolete get_lapdm_chan_by_hl2() 2015-09-22 16:39:03 +02:00
Harald Welte
d410eb9787 l1sap: correctly set chan_nr on PRIM_PH_RACH / INDICATION
In case of a RACH INDICATION on CCCH, we need to set CHAN_NR to
0x88 (RSL_CHAN_RACH).  In other cases, chan_nr needs to reflect
the actual logical channel (TCH/SDCCH) on whcih the handover happened.
2015-09-22 16:39:02 +02:00
Harald Welte
9ae5b50d78 l1sap: RACH: Detect hand-over even on TRX0
I don't understand why we would detect handover only on TRX1-n,
but not on TRX0.  It is perfectly valid for a handover to occur
on TRX0.
2015-09-22 16:39:02 +02:00
Harald Welte
52476fc1d4 l1sap: fix missing include file and resulting compiler warning 2015-09-22 16:39:02 +02:00
Harald Welte
e969f08892 l1sap: fix coding style 2015-09-22 16:39:02 +02:00
Harald Welte
7b1b832618 l1sap: Use L1SAP_IS_CHAN_RACH instead of magic number 0x88 2015-09-22 16:39:02 +02:00
Andreas Eversberg
e0146997a6 Add RACH message to PH-/MPH-/TCH-SAP interface
This part moves RACH message primitives from osmo-bts-sysmo to common
part.
2015-09-22 16:39:02 +02:00
Harald Welte
a391d3691a l1sap: Split ph_data_req() into smaller parts
... in an effort to avoid introducing new/more spaghetti code

Also, use offsetof() instead of pointer calculation to determine
the start of GsmL1_Prim_t.u.phDataReq.msgUnitParam.u8Buffer
2015-09-22 16:39:02 +02:00
Andreas Eversberg
5e90f2a809 Add BCCH message to PH-/MPH-/TCH-SAP interface
This first part moves BCCH message primitives from osmo-bts-sysmo to common
part. A new file "common/l1sap.c" is introduced to implement handling of
layer 1 messages from/to BTS model.
2015-09-22 16:39:02 +02:00
Holger Hans Peter Freyther
1eaa3d72ea audio/rsl: Honor the speech mode and don't send anything
Spotted by Ciaby while debugging an audio issue. Do not
send anything to port==0 to the BSC/NITB. Look at the
upper bits of the speech_mode to determine if sending is
allowed. 0x1 means recv_only and all other modes allow us
to send.

Manually verified with a single phone call with LCR bridge
mode to send a CRCX early but a MDCX sendrecv later. The
audio starts to flow after the MDCX message. Virtual Addr
space didn't increase over 10 calls. The l1p_msg is freed
by the caller.

The code might not re-set speech_mode from one call to
another but if it is ever != 0 it can be expected that
the BSC will always set it. This is because we do not
(and don't want to) allocate the lchan dynamically on
every usage.

Fixes: SYS#2111
2015-09-21 14:34:22 +02:00
Holger Hans Peter Freyther
668f8df3be audio/rsl: Include statistics for one call
Use the new libosmo-abis API to query the session for the
statistics and then send it as a TLV element to the BSC.
This can be used to do post processing about the call. E.g
to figure out if no audio arrived at all.
2015-09-21 14:34:07 +02:00
Holger Hans Peter Freyther
cc4a08bdc7 audio/rsl: Include the connection identifier in the DLCX ind
I have traces that include the connection identifier in the
DLCX indication.
2015-09-21 10:14:11 +02:00
Harald Welte
862807504b update README to bring it more in sync with reality. 2015-08-21 02:30:24 +02:00
Holger Hans Peter Freyther
a7c276b72b meas: Do not send incomplete measurement reports
The RSL_IE_MEAS_RES_NR is mandatory element with a minimum
of 5 octets (two for TL and three for the value). When we
establish a new channel we might not have had enough time
in a TDMA frame to calculate the average. The issue is not
easy to reproduce. At the point we receive the measurement
report we have two uplink measurements queued. As it is not
easy to reproduce and only occurs when a channel is new
I have decided to drop the message instead of sending made
up uplink measurement reports.

As of now lchan_build_rsl_ul_meas will always return 3 and
the condition will never be false.

Avoids: SYS#1781
2015-07-14 09:55:56 +02:00
Holger Hans Peter Freyther
f869a95f3b write_queue: Check the result of osmo_wqueue_enqueue and free
The write_queue is designed to have a maximum amount of pending
messages and will refuse to take new messages when it has been
reached. The caller can decide if it wants to flush the queue
and add the message again, create a log. But in all cases the
ownership of the msgb has not been transferred. Fix the potential
memory leak in the failure situation.
2015-03-28 18:31:10 +01:00
Andreas Eversberg
0ddd4b6c25 Add header file of PH-/MPH-/TCH-SAP interface to common part of osmo-bts
Instead of handling primitives directly at layer 1 specific code,
osmo-bts handles primitives at common code.

When all primitive are moved, the l1sap interface will:
- receive PH-DATA indications and forward them to layer 2.
- check for RF link loss and notify BSC.
- receive TCH indications and forward them via RTP.
- receive PH-RTS indications and send PH-DATA requests with content
  according to its logical channel.
- receive TCH-RTS indications and send TCH requests with content
  received via RTP or loopback from TCH indications.
- send MPH-INFO requests to activate, deactivate and modify logical
  channels and handle their confirms.
- receive MPH-INFO indications with measurements from tranceiver.
- forward received and transmitted PH-DATA to GSMTAP.
2015-03-25 23:22:00 +01:00
Andreas Eversberg
24839068f5 sysmo-bts: Use correct boundaries of L1 msg when forwarding to L1 proxy
In case of a headroom in a message, the 'head' pointer will not point to
the actual data.
2015-03-25 23:19:58 +01:00
Holger Hans Peter Freyther
b631bd21d2 power: Make it possible to force a power level
Use the standard RSL commands to order a logical channel
to use a fixed power level.

The code is not fully verified and there was a last minute
change to invoke bts_model_adjst_ms_pwr.
2015-02-05 22:32:53 +01:00
Holger Hans Peter Freyther
579651bf30 power/sysmobts: Add a manual ms power level control
Currently the DSP is instructed to achieve a given uplink
power target but there are circumstances (e.g. EMV testing)
where we need more control over it. The "manual/software/osmo"
power control can only be implemented per TRX and not per
lchan. Add a very very basic control that checks the MS Power
used by the phone, the actual receive level and then adjust
the power.

The code doesn't take the history into account, if the phone
can not reach the requested power level the code will be stuck
(e.g. no timeout based on multiframes). It has a mode for a
fixed power control but no way to set it yet.

The change of the mode requires a restart of the software.
2015-02-05 22:32:47 +01:00
Holger Hans Peter Freyther
0d6946741c sysmobts: Check mgr->calib.bts_conn for NULL
Check the right variable for NULL.

Fixes: CID 1262214
2015-01-10 18:06:29 +01:00
Holger Hans Peter Freyther
7e10bd6401 misc: Fix up testcase after 5a03e129a6
In 5a03e129a6 we generalized the
structural parser so we need to update the expected behavior of
that routine.
2015-01-10 13:07:49 +01:00
Holger Hans Peter Freyther
84e4dd92d4 sysmobts: Improve some log messages for calib control
* Print the GPS FD that was opened (e.g. to see if it was
  closed again)
* Print the state changes/expectations
* Print the correct to be applied. I wondered if I shouldo do
  a cor = cor * -1.. cor = -cor.. or add CLOCK_CORR(err) macro
  to use it inside the printf and correction and decided the
  gain is not worth the risk.
2015-01-10 09:15:27 +01:00
Holger Hans Peter Freyther
55da9874c0 sysmobts: Create a calibration loop that will be run
Continously run the calibration process. Everytime we call the
reset function classify the outcome. In case of a failure schedule
the next command soon and otherwise wait several hours.

Remember if the process was started through the VTY or the run
loop. In case it can't be started immediately reset and schedule
a new run.
2015-01-09 21:57:13 +01:00
Holger Hans Peter Freyther
d8d5f5904f sysmobts: Start the calibration the first time the link is up
After a reboot the system might have been off for a long time
and the currently used value might be wrong. Remember that we
never ran the calibration and execute it on start.
2015-01-09 21:57:13 +01:00
Holger Hans Peter Freyther
9acc82ce4a sysmobts: Initial version to use libgps to determine FIX state
We should only calibrate the clock if there is a GPS fix. Start
gpsd to determine if there is a fix or not. Work around trimble
decoding issues (sent an email upstream). We need to gain some
more experience to see if there memory leaks. We also need to
re-schedule the calibration depending on the outcome.
2015-01-09 21:57:13 +01:00
Holger Hans Peter Freyther
c017e309c4 sysmobts: The correction for GPS is in the reverse direction
Change the sign before passing it as correction value. The error
is the difference between the TCXO and GPS. We need to correct by
the reverse of the error. This seems to be different depending on
the clock source we have.

This is a last minute untested change.
2015-01-09 21:57:13 +01:00
Holger Hans Peter Freyther
2e59b20204 sysmobts: Use the ctrl interface for calibration
This runs the entire procedure for calibration with reasonable
error and success checking. It can be triggered from the VTY
of the sysmobts-mgr right now.

What is missing is to hook up with GPSD to check if the system
has a fix and provide a mode that will continously run the
calibration command.
2015-01-09 21:57:13 +01:00
Holger Hans Peter Freyther
fd425b1484 sysmobts: Copy more of l1if_rf_clock_info_reset into the CTRL code
The CTRL code should have used/extended the l1_if calibration
code. The sysmobts-mgr code first needs to determine if the
clock adjustment is necessary at all. This is done by first
resetting the counters, then waiting, then asking for the diff
and then applying the correction value. But the reference clock
is only set by the application comand.

Copy more code of l1if_rf_clock_info_reset to set the reference
clock as value. This is leaving some todos inside the code that
will be resolved as part of SYS#835.

Related: SYS#835
2015-01-09 21:57:13 +01:00
Holger Hans Peter Freyther
50131c125e sysmobts: Begin with calib control from the sysmobts manager
In the long run we will connect to GPSD and wait for a fix and
then run the calibration. The first step is to open (and re-open)
the control connection to the BTS.

As the connection is on localhost there should not be a computation
overhead to always have the connection open. When connecting assume
that the ASYNC connect worked directly as otherwise we get no
notification of the failure.

This looks like a "bug" of libosmo-abis that should check if the
socket has been connected or not.
2015-01-09 21:57:13 +01:00
Holger Hans Peter Freyther
5a03e129a6 msg: Generalize the message structure test
This was taken out of LaF0rge's OML router branch and is now
used by the extended calibration feature.
2015-01-09 21:57:13 +01:00
Holger Hans Peter Freyther
b7ebf545e6 cbch: Speculative change to not change CHAN ACK for CBCH
Use the rel_act_kind to not send RSL channel acks for the
CBCH to the BSC. This is similar to what we do for the BCCH
a couple of lines above.
2015-01-06 19:23:02 +01:00
Harald Welte
8fc2630dd4 SMS-CB: Clean up + centralize generation of NULL block 2014-12-30 13:45:02 +01:00
Harald Welte
bd988f6ad3 SMS-CB: Use GSM412_SEQ_NULL_MSG rather than 0xf 2014-12-30 13:34:57 +01:00
Harald Welte
1e245336ec SMS-CB: use gsm412_block_type from libosmocore
.. and not our own local re-definition of the structure.
2014-12-30 13:33:54 +01:00
Harald Welte
4457c0d9ba SMS-CB: Use GSM412_ #defines from libosmocore rather than our own 2014-12-30 13:32:52 +01:00
Harald Welte
660116fb9d CBCH: Implement CBCH block segmentation and RSL_MT_SMS_BC_CMD
* CBCH load indications are not yet sent
* The queue length is not yet limited!
2014-12-30 00:32:13 +01:00
Harald Welte
b15d2c9d2f Initial CBCH support
This should handle OML channel combinations with CBCH and activate the
CBCH SAPI towards the DSP correspondingly.  What is still missing is
sending any actual information over the CBCH in respons to the
PH-RTS.ind coming up from L1.
2014-12-30 00:28:31 +01:00
81 changed files with 13153 additions and 1229 deletions

3
.gitignore vendored
View File

@@ -29,6 +29,7 @@ src/osmo-bts-sysmo/sysmobts
src/osmo-bts-sysmo/sysmobts-remote
src/osmo-bts-sysmo/sysmobts-mgr
src/osmo-bts-trx/osmobts-trx
tests/atconfig
tests/package.m4
@@ -37,6 +38,8 @@ tests/paging/paging_test
tests/cipher/cipher_test
tests/sysmobts/sysmobts_test
tests/misc/misc_test
tests/bursts/bursts_test
tests/handover/handover_test
tests/testsuite
tests/testsuite.log

13
README
View File

@@ -1,10 +1,10 @@
Repository for new the Osmocom BTS implementation.
Repository forw the Osmocom BTS implementation.
This code implementes the Layer 2 and higher of a more or less
conventional GSM BTS (Base Transceiver Station) - however, using an
Abis/IP interface, rather than the old-fashioned E1/T1.
Specificallt, this includes
Specifically, this includes
* BTS-Side implementation of TS 08.58 (RSL) and TS 12.21 (OML)
* BTS-Side implementation of LAPDm (using libosmocore/libosmogsm)
* A somewhat separated interface between those higher layer parts
@@ -19,12 +19,12 @@ for the Calypso DSP code have not been written yet. This would still
require a lot of work.
Some additional work is being done in using some parts of the OpenBTS
L1FEC and glue it against omso-bts. However, this is also still in an
early, experimental stage.
L1FEC and glue it against omso-bts. This code is called osmo-trx and
requires the jolly/trx branch of this repository.
== Known Limitations ==
As of June 3, 2012, the following known limitations exist in this
As of August 20, 2015, the following known limitations exist in this
implementation:
=== Common Core ===
@@ -37,7 +37,6 @@ implementation:
* No support for frequency hopping
* No reporting of interference levels as part of TS 08.58 RF RES IND
* No error reporting in case PAGING COMMAND fails due to queue overflow
* No hand-over support (planned)
* No use of TS 08.58 BS Power and MS Power parameters
* No support of TS 08.58 MultiRate Control
* No support of TS 08.58 Supported Codec Types
@@ -45,13 +44,11 @@ implementation:
=== osmo-bts-sysmo ===
* No CSD / ECSD support (not planned)
* No GPRS/EDGE support (planned)
* GSM-R frequency band supported, but no NCH/ASCI/SoLSA
* All timeslots on one TRX have to use same training sequence (TSC)
* No multi-TRX support yet, though hardware+L1 support stacking
* Makes no use of 12.21 Intave Parameters and Interference
Level Boundaries
* Makes no use of TS 12.21 T3105
* Doesn't yet include MAC address in Abis/IP Identity message
* MphConfig.CNF can be returned to the wrong callback. E.g. with Tx Power
and ciphering. The dispatch should take a look at the hLayer3.

View File

@@ -27,6 +27,8 @@ PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 0.0.7)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis)
PKG_CHECK_MODULES(LIBGPS, libgps)
PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec)
AC_MSG_CHECKING([whether to enable sysmocom-bts hardware support])
AC_ARG_ENABLE(sysmocom-bts,
@@ -36,6 +38,14 @@ AC_ARG_ENABLE(sysmocom-bts,
AC_MSG_RESULT([$enable_sysmocom_bts])
AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes")
AC_MSG_CHECKING([whether to enable trx hardware support])
AC_ARG_ENABLE(trx,
AC_HELP_STRING([--enable-trx],
[enable code for trx hardware [default=no]]),
[enable_trx="yes"],[enable_trx="no"])
AC_MSG_RESULT([$enable_trx])
AM_CONDITIONAL(ENABLE_TRX, test "x$enable_trx" = "xyes")
# We share gsm_data.h with OpenBSC and need to be pointed to the source
# directory of OpenBSC for now.
AC_ARG_WITH([openbsc],
@@ -69,6 +79,7 @@ AC_OUTPUT(
src/Makefile
src/common/Makefile
src/osmo-bts-sysmo/Makefile
src/osmo-bts-trx/Makefile
include/Makefile
include/osmo-bts/Makefile
tests/Makefile
@@ -77,4 +88,6 @@ AC_OUTPUT(
tests/cipher/Makefile
tests/sysmobts/Makefile
tests/misc/Makefile
tests/bursts/Makefile
tests/handover/Makefile
Makefile)

View File

@@ -1,3 +1,4 @@
noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \
oml.h paging.h rsl.h signal.h vty.h amr.h pcu_if.h pcuif_proto.h \
handover.h msg_utils.h tx_power.h control_if.h cbch.h
handover.h msg_utils.h tx_power.h control_if.h cbch.h l1sap.h \
power_control.h

View File

@@ -39,5 +39,9 @@ void load_timer_start(struct gsm_bts *bts);
void bts_update_status(enum bts_global_status which, int on);
int trx_ms_pwr_ctrl_is_osmo(struct gsm_bts_trx *trx);
struct gsm_time *get_time(struct gsm_bts *bts);
#endif /* _BTS_H */

View File

@@ -12,8 +12,6 @@
int bts_model_init(struct gsm_bts *bts);
struct gsm_time *bts_model_get_time(struct gsm_bts *bts);
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
struct tlv_parsed *old_attr, struct tlv_parsed *new_attr,
void *obj);
@@ -27,18 +25,9 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
void *obj, uint8_t adm_state);
int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp);
int bts_model_rsl_chan_rel(struct gsm_lchan *lchan);
int bts_model_rsl_chan_mod(struct gsm_lchan *lchan);
int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan);
int bts_model_rsl_mode_modify(struct gsm_lchan *lchan);
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx);
int bts_model_trx_close(struct gsm_bts_trx *trx);
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
unsigned int rtp_pl_len);
int bts_model_vty_init(struct gsm_bts *bts);
void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts);
@@ -47,5 +36,10 @@ void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx);
int bts_model_oml_estab(struct gsm_bts *bts);
int bts_model_change_power(struct gsm_bts_trx *trx, int p_trxout_mdBm);
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan);
int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap);
void bts_model_abis_close(struct gsm_bts *bts);
#endif

View File

@@ -81,11 +81,13 @@ struct gsm_bts_role_bts {
struct {
uint8_t tc4_ctr;
} si;
struct gsm_time gsm_time;
uint8_t radio_link_timeout;
int ul_power_target; /* Uplink Rx power target */
/* used by the sysmoBTS to adjust band */
uint8_t auto_band;
uint8_t unitid_use_eeprom;
struct {
struct llist_head queue; /* list of struct smscb_msg */
@@ -97,8 +99,9 @@ enum lchan_ciph_state {
LCHAN_CIPH_NONE,
LCHAN_CIPH_RX_REQ,
LCHAN_CIPH_RX_CONF,
LCHAN_CIPH_TXRX_REQ,
LCHAN_CIPH_TXRX_CONF,
LCHAN_CIPH_RXTX_REQ,
LCHAN_CIPH_RX_CONF_TX_REQ,
LCHAN_CIPH_RXTX_CONF,
};
#define bts_role_bts(x) ((struct gsm_bts_role_bts *)(x)->role)
@@ -112,6 +115,13 @@ static inline struct femtol1_hdl *trx_femtol1_hdl(struct gsm_bts_trx *trx)
return trx->role_bts.l1h;
}
struct trx_l1h;
static inline struct trx_l1h *trx_l1h_hdl(struct gsm_bts_trx *trx)
{
return trx->role_bts.l1h;
}
void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state);

73
include/osmo-bts/l1sap.h Normal file
View File

@@ -0,0 +1,73 @@
#ifndef L1SAP_H
#define L1SAP_H
/* timeslot and subslot from chan_nr */
#define L1SAP_CHAN2TS(chan_nr) (chan_nr & 7)
#define L1SAP_CHAN2SS_TCHH(chan_nr) ((chan_nr >> 3) & 1)
#define L1SAP_CHAN2SS_SDCCH4(chan_nr) ((chan_nr >> 3) & 3)
#define L1SAP_CHAN2SS_SDCCH8(chan_nr) ((chan_nr >> 3) & 7)
/* logical channel from chan_nr + link_id */
#define L1SAP_IS_LINK_SACCH(link_id) ((link_id & 0xC0) == 0x40)
#define L1SAP_IS_CHAN_TCHF(chan_nr) ((chan_nr & 0xf8) == 0x08)
#define L1SAP_IS_CHAN_TCHH(chan_nr) ((chan_nr & 0xf0) == 0x10)
#define L1SAP_IS_CHAN_SDCCH4(chan_nr) ((chan_nr & 0xe0) == 0x20)
#define L1SAP_IS_CHAN_SDCCH8(chan_nr) ((chan_nr & 0xc0) == 0x40)
#define L1SAP_IS_CHAN_BCCH(chan_nr) ((chan_nr & 0xf8) == 0x80)
#define L1SAP_IS_CHAN_RACH(chan_nr) ((chan_nr & 0xf8) == 0x88)
#define L1SAP_IS_CHAN_AGCH_PCH(chan_nr) ((chan_nr & 0xf8) == 0x90)
/* rach type from ra */
#define L1SAP_IS_PACKET_RACH(ra) ((ra & 0xf0) == 0x70)
/* CCCH block from frame number */
#define L1SAP_FN2CCCHBLOCK(fn) ((fn % 51) / 5 - 1)
/* PTCH layout from frame number */
#define L1SAP_FN2MACBLOCK(fn) ((fn % 52) / 4)
#define L1SAP_FN2PTCCHBLOCK(fn) ((fn / 52) & 7)
#define L1SAP_IS_PTCCH(fn) ((fn % 52) == 12)
/* subslot from any chan_nr */
static inline uint8_t l1sap_chan2ss(uint8_t chan_nr)
{
if (L1SAP_IS_CHAN_SDCCH8(chan_nr))
return L1SAP_CHAN2SS_SDCCH8(chan_nr);
if (L1SAP_IS_CHAN_SDCCH4(chan_nr))
return L1SAP_CHAN2SS_SDCCH4(chan_nr);
if (L1SAP_IS_CHAN_TCHH(chan_nr))
return L1SAP_CHAN2SS_TCHH(chan_nr);
return 0;
}
/* allocate a msgb containing a osmo_phsap_prim + optional l2 data */
struct msgb *l1sap_msgb_alloc(unsigned int l2_len);
/* any L1 prim received from bts model */
int l1sap_up(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap);
/* pcu (socket interface) sends us a data request primitive */
int l1sap_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
/* call-back function for incoming RTP */
void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
unsigned int rtp_pl_len);
/* channel control */
int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *tp);
int l1sap_chan_rel(struct gsm_bts_trx *trx, uint8_t chan_nr);
int l1sap_chan_deact_sacch(struct gsm_bts_trx *trx, uint8_t chan_nr);
int l1sap_chan_modify(struct gsm_bts_trx *trx, uint8_t chan_nr);
extern const struct value_string gsmtap_sapi_names[];
extern struct gsmtap_inst *gsmtap;
extern uint32_t gsmtap_sapi_mask;
extern uint8_t gsmtap_sapi_acch;
#define msgb_l1sap_prim(msg) ((struct osmo_phsap_prim *)(msg)->l1h)
int bts_check_for_first_ciphrd(struct gsm_lchan *lchan,
uint8_t *data, int len);
#endif /* L1SAP_H */

View File

@@ -16,6 +16,8 @@ enum {
DDSP,
DPCU,
DHO,
DTRX,
DLOOP,
DABIS,
DRTP,
DSUM,

View File

@@ -49,10 +49,4 @@ int paging_group_queue_empty(struct paging_state *ps, uint8_t group);
int paging_queue_length(struct paging_state *ps);
int paging_buffer_space(struct paging_state *ps);
extern uint8_t etws_segment_data[5][17];
extern uint8_t etws_segment_len[5];
extern uint8_t etws_nr_seg;
extern uint8_t etws_data[60];
extern size_t etws_len;
#endif

View File

@@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
#include <osmo-bts/gsm_data.h>
int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
const uint8_t ms_power, const int rxLevel);

View File

@@ -11,13 +11,15 @@ enum {
LCHAN_REL_ACT_OML,
};
int msgb_queue_flush(struct llist_head *list);
int down_rsl(struct gsm_bts_trx *trx, struct msgb *msg);
int rsl_tx_rf_res(struct gsm_bts_trx *trx);
int rsl_tx_chan_rqd(struct gsm_bts_trx *trx, struct gsm_time *gtime,
uint8_t ra, uint8_t acc_delay);
int rsl_tx_est_ind(struct gsm_lchan *lchan, uint8_t link_id, uint8_t *data, int len);
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime);
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan);
int rsl_tx_chan_act_nack(struct gsm_lchan *lchan, uint8_t cause);
int rsl_tx_conn_fail(struct gsm_lchan *lchan, uint8_t cause);
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan);

View File

@@ -15,7 +15,7 @@ extern struct cmd_element ournode_end_cmd;
enum node_type bts_vty_go_parent(struct vty *vty);
int bts_vty_is_config_node(struct vty *vty, int node);
int bts_vty_init(const struct log_info *cat);
int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat);
extern struct vty_app_info bts_vty_info;

View File

@@ -3,3 +3,6 @@ SUBDIRS = common
if ENABLE_SYSMOBTS
SUBDIRS += osmo-bts-sysmo
endif
if ENABLE_TRX
SUBDIRS += osmo-bts-trx
endif

View File

@@ -8,4 +8,4 @@ libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \
load_indication.c pcu_sock.c handover.c msg_utils.c \
load_indication.c pcu_sock.c handover.c msg_utils.c \
tx_power.c bts_ctrl_commands.c bts_ctrl_lookup.c \
etws_p1.c cbch.c
l1sap.c cbch.c power_control.c

View File

@@ -47,6 +47,7 @@
#include <osmo-bts/bts.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/oml.h>
#include <osmo-bts/bts_model.h>
static struct gsm_bts *g_bts;
@@ -114,7 +115,7 @@ static void sign_link_down(struct e1inp_line *line)
e1inp_sign_link_destroy(g_bts->oml_link);
g_bts->oml_link = NULL;
bts_shutdown(g_bts, "Abis close");
bts_model_abis_close(g_bts);
}
@@ -203,7 +204,6 @@ void abis_init(struct gsm_bts *bts)
g_bts = bts;
oml_init();
e1inp_vty_init();
libosmo_abis_init(NULL);
osmo_signal_register_handler(SS_L_INPUT, &inp_s_cbfn, bts);

View File

@@ -16,8 +16,9 @@ void amr_log_mr_conf(int ss, int logl, const char *pfx,
for (i = 0; i < amr_mrc->num_modes; i++)
LOGPC(ss, logl, ", mode[%u] = %u/%u/%u",
i, amr_mrc->mode[i].mode, amr_mrc->mode[i].threshold,
amr_mrc->mode[i].hysteresis);
i, amr_mrc->mode[i].mode,
amr_mrc->mode[i].threshold_bts,
amr_mrc->mode[i].hysteresis_bts);
LOGPC(ss, logl, "\n");
}
@@ -68,18 +69,18 @@ int amr_parse_mr_conf(struct amr_multirate_conf *amr_mrc,
}
if (num_codecs >= 2) {
amr_mrc->mode[0].threshold = mr_conf[1] & 0x3F;
amr_mrc->mode[0].hysteresis = mr_conf[2] >> 4;
amr_mrc->mode[0].threshold_bts = mr_conf[1] & 0x3F;
amr_mrc->mode[0].hysteresis_bts = mr_conf[2] >> 4;
}
if (num_codecs >= 3) {
amr_mrc->mode[1].threshold =
amr_mrc->mode[1].threshold_bts =
((mr_conf[2] & 0xF) << 2) | (mr_conf[3] >> 6);
amr_mrc->mode[1].hysteresis = (mr_conf[3] >> 2) & 0x7;
amr_mrc->mode[1].hysteresis_bts = (mr_conf[3] >> 2) & 0xF;
}
if (num_codecs >= 4) {
amr_mrc->mode[3].threshold =
amr_mrc->mode[2].threshold_bts =
((mr_conf[3] & 0x3) << 4) | (mr_conf[4] >> 4);
amr_mrc->mode[3].hysteresis = mr_conf[4] & 0xF;
amr_mrc->mode[2].hysteresis_bts = mr_conf[4] & 0xF;
}
return num_codecs;
@@ -94,10 +95,12 @@ ret_einval:
unsigned int amr_get_initial_mode(struct gsm_lchan *lchan)
{
struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr;
struct gsm48_multi_rate_conf *mr_conf =
(struct gsm48_multi_rate_conf *) amr_mrc->gsm48_ie;
if (lchan->mr_conf.icmi) {
if (mr_conf->icmi) {
/* initial mode given, coding in TS 05.09 3.4.1 */
return lchan->mr_conf.smod;
return mr_conf->smod;
} else {
/* implicit rule according to TS 05.09 Chapter 3.4.3 */
switch (amr_mrc->num_modes) {

View File

@@ -99,6 +99,7 @@ int bts_init(struct gsm_bts *bts)
/* configurable via VTY */
btsb->paging_state = paging_init(btsb, 200, 0);
btsb->ul_power_target = -75; /* dBm default */
/* configurable via OML */
btsb->load.ccch.load_ind_period = 112;
@@ -241,6 +242,8 @@ int trx_link_estab(struct gsm_bts_trx *trx)
if (link)
rsl_tx_rf_res(trx);
else
bts_model_trx_deact_rf(trx);
return 0;
}
@@ -606,3 +609,15 @@ int bts_supports_cipher(struct gsm_bts_role_bts *bts, int rsl_cipher)
sup = (1 << (rsl_cipher - 2)) & bts->support.ciphers;
return sup > 0;
}
int trx_ms_pwr_ctrl_is_osmo(struct gsm_bts_trx *trx)
{
return trx->ms_power_control == 1;
}
struct gsm_time *get_time(struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts->role;
return &btsb->gsm_time;
}

View File

@@ -21,66 +21,63 @@
#include <errno.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/protocol/gsm_04_12.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/cbch.h>
#include <osmo-bts/logging.h>
#define SMS_CB_MSG_LEN 88 /* TS 04.12 Section 3.1 */
#define SMS_CB_BLOCK_LEN 22 /* TS 04.12 Section 3.1 */
struct smscb_msg {
struct llist_head list; /* list in smscb_state.queue */
uint8_t msg[SMS_CB_MSG_LEN]; /* message buffer */
uint8_t msg[GSM412_MSG_LEN]; /* message buffer */
uint8_t next_seg; /* next segment number */
uint8_t num_segs; /* total number of segments */
};
/* Figure 3/3GPP TS 04.12 */
struct sms_cb_block_type {
uint8_t seq_nr:4, /* 0=first, 1=2nd, ... f=null */
lb:1, /* last block */
lpd:2, /* always 01 */
spare:1;
};
static int get_smscb_null_block(uint8_t *out)
{
struct gsm412_block_type *block_type = (struct gsm412_block_type *) out;
block_type->spare = 0;
block_type->lpd = 1;
block_type->seq_nr = GSM412_SEQ_NULL_MSG;
block_type->lb = 0;
memset(out+1, GSM_MACBLOCK_PADDING, GSM412_BLOCK_LEN);
return 0;
}
/* get the next block of the current CB message */
static int get_smscb_block(struct gsm_bts *bts, uint8_t *out)
{
int to_copy;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
struct sms_cb_block_type *block_type = (struct sms_cb_block_type *) out++;
struct gsm412_block_type *block_type;
struct smscb_msg *msg = btsb->smscb_state.cur_msg;
/* LPD is always 01 */
block_type->lpd = 1;
if (!msg) {
/* No message: Send NULL mesage */
block_type->seq_nr = 0xf;
block_type->lb = 0;
/* padding */
memset(out, GSM_MACBLOCK_PADDING, SMS_CB_BLOCK_LEN);
return 0;
return get_smscb_null_block(out);
}
DEBUGP(DLSMS, "Current SMS-CB %s: ",
osmo_hexdump_nospc(msg->msg, sizeof(msg->msg)));
block_type = (struct gsm412_block_type *) out++;
/* LPD is always 01 */
block_type->spare = 0;
block_type->lpd = 1;
/* determine how much data to copy */
to_copy = SMS_CB_MSG_LEN - (msg->next_seg * SMS_CB_BLOCK_LEN);
if (to_copy > SMS_CB_BLOCK_LEN)
to_copy = SMS_CB_BLOCK_LEN;
to_copy = GSM412_MSG_LEN - (msg->next_seg * GSM412_BLOCK_LEN);
if (to_copy > GSM412_BLOCK_LEN)
to_copy = GSM412_BLOCK_LEN;
/* copy data and increment index */
memcpy(out, &msg->msg[msg->next_seg * SMS_CB_BLOCK_LEN], to_copy);
memcpy(out, &msg->msg[msg->next_seg * GSM412_BLOCK_LEN], to_copy);
/* set + increment sequence number */
block_type->seq_nr = msg->next_seg++;
DEBUGP(DLSMS, "sending block %u: %s\n",
block_type->seq_nr, osmo_hexdump_nospc(out, to_copy));
/* determine if this is the last block */
if (block_type->seq_nr + 1 == msg->num_segs)
block_type->lb = 1;
@@ -114,7 +111,7 @@ int bts_process_smscb_cmd(struct gsm_bts *bts,
if (msg_len > sizeof(scm->msg)) {
LOGP(DLSMS, LOGL_ERROR,
"Cannot process SMSCB of %u bytes (max %u)\n",
"Cannot process SMSCB of %u bytes (max %lu)\n",
msg_len, sizeof(scm->msg));
return -EINVAL;
}
@@ -140,8 +137,6 @@ int bts_process_smscb_cmd(struct gsm_bts *bts,
break;
}
DEBUGP(DLSMS, "Enqueuing SMS-CB %s from RSL",
osmo_hexdump_nospc(scm->msg, sizeof(scm->msg)));
llist_add_tail(&scm->list, &btsb->smscb_state.queue);
return 0;
@@ -193,8 +188,7 @@ int bts_cbch_get(struct gsm_bts *bts, uint8_t *outbuf, struct gsm_time *g_time)
break;
case 4: case 5: case 6: case 7:
/* always send NULL frame in extended CBCH for now */
outbuf[0] = 0x2f;
memset(outbuf+1, GSM_MACBLOCK_PADDING, SMS_CB_BLOCK_LEN);
rc = get_smscb_null_block(outbuf);
break;
}

View File

@@ -1,111 +0,0 @@
/* Paging Rest Octets / ETWS support code */
/*
* (C) 2014 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <osmocom/core/bits.h>
#include <osmocom/core/bitvec.h>
/*! \brief construct the Paging 1 Rest Octets
* \param[in] bitvec Bit Vector (destionation)
* \param[in] seg_nr segment number 0...n
* \param[in] num_segs total number of segments
* \param[in] payload binary payload for this segment
* \param[in] len_of_seg length of the segment
*
* Put together the P1 Rest Octets according to 10.5.2.23
*/
int construct_p1_rest_octets(struct bitvec *bv, int etws_will_follow)
{
/* no NLN */
bitvec_set_bit(bv, L);
/* no Priority 1 */
bitvec_set_bit(bv, L);
/* no Priority 2 */
bitvec_set_bit(bv, L);
/* Group Call Information */
bitvec_set_bit(bv, L);
/* Packet Page Indication 1 */
bitvec_set_bit(bv, L);
/* Packet Page Indication 2 */
bitvec_set_bit(bv, L);
/* No Rel6 extensions */
bitvec_set_bit(bv, L);
/* No Rel7 extensions */
bitvec_set_bit(bv, L);
/* Rel8 extensions */
bitvec_set_bit(bv, H);
/* priority_uplink_access bit: Use RACH */
bitvec_set_bit(bv, L);
if (etws_will_follow) {
/* We do have ETWS Primary Notification */
bitvec_set_bit(bv, ONE);
} else {
bitvec_set_bit(bv, ZERO);
}
return 0;
}
/*! \brief construct a ETWS Primary Notification struct
* \param[in] bitvec Bit Vector (destionation)
* \param[in] pni Primary Notification Identifier
* \param[in] seg_nr Segment Number 0...n
* \param[in] num_segs Total Number of Segments
* \param[in] payload Binary Payload for this Segment
* \param[in] num_payload_bits Length of the Segment
*/
int construct_etws_prim_notif(struct bitvec *bv, uint8_t pni,
uint8_t seg_nr, uint8_t num_segs,
const uint8_t *payload,
uint8_t num_payload_bits)
{
uint8_t payload_ubits[num_payload_bits];
/* Put together a "ETWS Primary Notification struct" as per TS
* 44.018 */
if (seg_nr == 0) {
bitvec_set_bit(bv, ZERO);
bitvec_set_uint(bv, num_segs, 4);
} else {
bitvec_set_bit(bv, ONE);
/* seg_nr is coounting 1..n, not 0..n */
bitvec_set_uint(bv, seg_nr + 1, 4);
}
bitvec_set_bit(bv, pni ? ONE : ZERO);
bitvec_set_uint(bv, num_payload_bits, 7);
/* expand packed payload bits to unpacked bits and set them in
* the bit vector */
osmo_pbit2ubit(payload_ubits, payload, num_payload_bits);
int i;
for (i = 0; i < num_payload_bits; ++i)
bitvec_set_bit(bv, payload_ubits[i]);
// bitvec_set_bits(bv, (enum bit_value *) payload_ubits, num_payload_bits);
return 0;
}

View File

@@ -33,6 +33,7 @@
#include <osmo-bts/rsl.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/handover.h>
#include <osmo-bts/l1sap.h>
/* Transmit a handover related PHYS INFO on given lchan */
static int ho_tx_phys_info(struct gsm_lchan *lchan)
@@ -114,7 +115,7 @@ void handover_rach(struct gsm_lchan *lchan, uint8_t ra, uint8_t acc_delay)
/* Stop handover detection, wait for valid frame */
lchan->ho.active = HANDOVER_WAIT_FRAME;
if (bts_model_rsl_chan_mod(lchan) != 0) {
if (l1sap_chan_modify(lchan->ts->trx, gsm_lchan2chan_nr(lchan)) != 0) {
LOGP(DHO, LOGL_ERROR,
"%s failed to modify channel after handover\n",
gsm_lchan_name(lchan));

1084
src/common/l1sap.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -107,6 +107,18 @@ static struct log_info_cat bts_log_info_cat[] = {
.color = "\033[0;37m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DTRX] = {
.name = "DTRX",
.description = "TRX interface",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DLOOP] = {
.name = "DLOOP",
.description = "Control loops",
.color = "\033[0;34m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
#if 0
[DNS] = {
.name = "DNS",

View File

@@ -103,13 +103,6 @@ int msg_verify_ipa_structure(struct msgb *msg)
hh = (struct ipaccess_head *) msg->l1h;
if (hh->proto != IPAC_PROTO_OML) {
LOGP(DL1C, LOGL_ERROR,
"Incorrect ipa header protocol 0x%x 0x%x\n",
hh->proto, IPAC_PROTO_OML);
return -1;
}
if (ntohs(hh->len) != msgb_l1len(msg) - sizeof(struct ipaccess_head)) {
LOGP(DL1C, LOGL_ERROR,
"Incorrect ipa header msg size %d %d\n",
@@ -117,7 +110,16 @@ int msg_verify_ipa_structure(struct msgb *msg)
return -1;
}
msg->l2h = hh->data;
if (hh->proto == IPAC_PROTO_OSMO) {
struct ipaccess_head_ext *hh_ext = (struct ipaccess_head_ext *) hh->data;
if (ntohs(hh->len) < sizeof(*hh_ext)) {
LOGP(DL1C, LOGL_ERROR, "IPA length shorter than OSMO header\n");
return -1;
}
msg->l2h = hh_ext->data;
} else
msg->l2h = hh->data;
return 0;
}

View File

@@ -590,6 +590,25 @@ static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg)
trx->arfcn_num = length;
} else
trx->arfcn_num = 0;
#else
if (trx != trx->bts->c0 && TLVP_PRESENT(&tp, NM_ATT_ARFCN_LIST)) {
const uint8_t *value = TLVP_VAL(&tp, NM_ATT_ARFCN_LIST);
uint16_t _value;
uint16_t length = TLVP_LEN(&tp, NM_ATT_ARFCN_LIST);
uint16_t arfcn;
if (length != 2) {
LOGP(DOML, LOGL_ERROR, "Expecting only one ARFCN, "
"because hopping not supported\n");
/* FIXME: send NACK */
return -ENOTSUP;
}
memcpy(&_value, value, 2);
arfcn = ntohs(_value);
value += 2;
if (arfcn > 1024)
return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL);
trx->arfcn = arfcn;
}
#endif
/* call into BTS driver to apply new attributes to hardware */
return bts_model_apply_oml(trx->bts, msg, tp_merged, NM_OC_RADIO_CARRIER, trx);

View File

@@ -46,9 +46,6 @@
#define MAX_PAGING_BLOCKS_CCCH 9
#define MAX_BS_PA_MFRMS 9
static const uint8_t empty_id_lv[] = { 0x01, 0xF0 };
enum paging_record_type {
PAGING_RECORD_PAGING,
PAGING_RECORD_IMM_ASS
@@ -265,14 +262,7 @@ int paging_add_imm_ass(struct paging_state *ps, const uint8_t *data,
#define L2_PLEN(len) (((len - 1) << 2) | 0x01)
static int current_segment[MAX_PAGING_BLOCKS_CCCH*MAX_BS_PA_MFRMS];
uint8_t etws_segment_data[5][17];
uint8_t etws_segment_len[5];
uint8_t etws_nr_seg;
uint8_t etws_data[60];
size_t etws_len;
static int fill_paging_type_1(int group, uint8_t *out_buf, const uint8_t *identity1_lv,
static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv,
uint8_t chan1, const uint8_t *identity2_lv,
uint8_t chan2)
{
@@ -289,20 +279,7 @@ static int fill_paging_type_1(int group, uint8_t *out_buf, const uint8_t *identi
cur = lv_put(pt1->data, identity1_lv[0], identity1_lv+1);
if (identity2_lv)
cur = lv_put(cur, identity2_lv[0], identity2_lv+1);
else if (identity1_lv == empty_id_lv && etws_nr_seg > 0) {
int cur_segment = current_segment[group];
current_segment[group] += 1;
current_segment[group] %= etws_nr_seg;
LOGP(DPAG, LOGL_NOTICE, "paging group(%d) segment(%d)\n",
group, cur_segment);
/* move the pointer */
memcpy(cur, etws_segment_data[cur_segment], etws_segment_len[cur_segment]);
cur += etws_segment_len[cur_segment];
}
/* do we need to include it */
pt1->l2_plen = L2_PLEN(cur - out_buf);
return cur - out_buf;
@@ -359,6 +336,8 @@ static int fill_paging_type_3(uint8_t *out_buf, const uint8_t *tmsi1_lv,
return cur - out_buf;
}
static const uint8_t empty_id_lv[] = { 0x01, 0xF0 };
static struct paging_record *dequeue_pr(struct llist_head *group_q)
{
struct paging_record *pr;
@@ -421,7 +400,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
/* There is nobody to be paged, send Type1 with two empty ID */
if (llist_empty(group_q)) {
//DEBUGP(DPAG, "Tx PAGING TYPE 1 (empty)\n");
len = fill_paging_type_1(group, out_buf, empty_id_lv, 0,
len = fill_paging_type_1(out_buf, empty_id_lv, 0,
NULL, 0);
*is_empty = 1;
} else {
@@ -495,7 +474,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
}
} else if (num_pr == 1) {
DEBUGP(DPAG, "Tx PAGING TYPE 1 (1 xMSI,1 empty)\n");
len = fill_paging_type_1(group, out_buf,
len = fill_paging_type_1(out_buf,
pr[0]->u.paging.identity_lv,
pr[0]->u.paging.chan_needed,
NULL, 0);
@@ -503,7 +482,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
/* 2 (any type) or
* 3 or 4, of which only 2 will be sent */
DEBUGP(DPAG, "Tx PAGING TYPE 1 (2 xMSI)\n");
len = fill_paging_type_1(group, out_buf,
len = fill_paging_type_1(out_buf,
pr[0]->u.paging.identity_lv,
pr[0]->u.paging.chan_needed,
pr[1]->u.paging.identity_lv,

View File

@@ -39,7 +39,7 @@
#include <osmo-bts/bts.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/signal.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/l1sap.h>
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx);
@@ -57,10 +57,6 @@ static const char *sapi_string[] = {
[PCU_IF_SAPI_PTCCH] = "PTCCH",
};
/* FIXME: common l1if include ? */
int l1if_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
static int pcu_sock_send(struct gsm_network *net, struct msgb *msg);
/* FIXME: move this to libosmocore */
int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path);
@@ -334,6 +330,7 @@ int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
data_ind = &pcu_prim->u.data_ind;
data_ind->sapi = (is_ptcch) ? PCU_IF_SAPI_PTCCH : PCU_IF_SAPI_PDTCH;
data_ind->rssi = rssi;
data_ind->fn = fn;
data_ind->arfcn = arfcn;
data_ind->trx_nr = ts->trx->nr;
@@ -512,7 +509,7 @@ static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
}
ts = &trx->ts[data_req->ts_nr];
is_ptcch = (data_req->sapi == PCU_IF_SAPI_PTCCH);
rc = l1if_pdch_req(ts, is_ptcch, data_req->fn, data_req->arfcn,
rc = l1sap_pdch_req(ts, is_ptcch, data_req->fn, data_req->arfcn,
data_req->block_nr, data_req->data, data_req->len);
break;
default:
@@ -546,9 +543,9 @@ static int pcu_rx_act_req(struct gsm_bts *bts,
return -EINVAL;
}
if (act_req->activate)
bts_model_rsl_chan_act(lchan, NULL);
l1sap_chan_act(trx, gsm_lchan2chan_nr(lchan), NULL);
else
bts_model_rsl_chan_rel(lchan);
l1sap_chan_rel(trx, gsm_lchan2chan_nr(lchan));
return 0;
}
@@ -653,7 +650,8 @@ static void pcu_sock_close(struct pcu_sock_state *state)
if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
&& ts->pchan == GSM_PCHAN_PDCH) {
ts->lchan->rel_act_kind = LCHAN_REL_ACT_PCU;
bts_model_rsl_chan_rel(ts->lchan);
l1sap_chan_rel(trx,
gsm_lchan2chan_nr(ts->lchan));
}
}
}

View File

@@ -0,0 +1,99 @@
/* MS Power Control Loop L1 */
/* (C) 2014 by Holger Hans Peter Freyther
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/measurement.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/l1sap.h>
/*
* Check if manual power control is needed
* Check if fixed power was selected
* Check if the MS is already using our level if not
* the value is bogus..
* TODO: Add a timeout.. e.g. if the ms is not capable of reaching
* the value we have set.
*/
int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
const uint8_t ms_power, const int rxLevel)
{
int rx;
int cur_dBm, new_dBm, new_pwr;
struct gsm_bts *bts = lchan->ts->trx->bts;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
const enum gsm_band band = bts->band;
if (!trx_ms_pwr_ctrl_is_osmo(lchan->ts->trx))
return 0;
if (lchan->ms_power_ctrl.fixed)
return 0;
/* The phone hasn't reached the power level yet */
if (lchan->ms_power_ctrl.current != ms_power)
return 0;
/*
* What is the difference between what we want and received?
* Ignore a margin that is within the range of measurement
* and MS output issues.
*/
rx = btsb->ul_power_target - rxLevel;
if (rx >= 0 && rx < 1)
return 0;
if (rx < 0 && rx > -1)
return 0;
/* We don't really care about the truncation of int + float */
cur_dBm = ms_pwr_dbm(band, ms_power);
new_dBm = cur_dBm + rx;
/* Clamp negative values and do it depending on the band */
if (new_dBm < 0)
new_dBm = 0;
switch (band) {
case GSM_BAND_1800:
/* If MS_TX_PWR_MAX_CCH is set the values 29,
* 30, 31 are not used. Avoid specifying a dBm
* that would lead to these power levels. The
* phone might not be able to reach them. */
if (new_dBm > 30)
new_dBm = 30;
break;
default:
break;
}
new_pwr = ms_pwr_ctl_lvl(band, new_dBm);
if (lchan->ms_power_ctrl.current != new_pwr) {
lchan->ms_power_ctrl.current = new_pwr;
bts_model_adjst_ms_pwr(lchan);
return 1;
}
return 0;
}

View File

@@ -42,11 +42,11 @@
#include <osmo-bts/oml.h>
#include <osmo-bts/amr.h>
#include <osmo-bts/signal.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/measurement.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/handover.h>
#include <osmo-bts/cbch.h>
#include <osmo-bts/l1sap.h>
//#define FAKE_CIPH_MODE_COMPL
@@ -543,8 +543,9 @@ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
}
/* 8.4.2 sending CHANnel ACTIVation ACKnowledge */
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime)
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan)
{
struct gsm_time *gtime = get_time(lchan->ts->trx->bts);
struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
uint8_t ie[2];
@@ -735,6 +736,11 @@ static int rsl_rx_chan_activ(struct msgb *msg)
return rsl_tx_chan_act_nack(lchan, RSL_ERR_EQUIPMENT_FAIL);
}
/* Initialize channel defaults */
lchan->ms_power = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, 0);
lchan->ms_power_ctrl.current = lchan->ms_power;
lchan->ms_power_ctrl.fixed = 0;
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
/* 9.3.3 Activation Type */
@@ -774,8 +780,11 @@ static int rsl_rx_chan_activ(struct msgb *msg)
if (TLVP_PRESENT(&tp, RSL_IE_BS_POWER))
lchan->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER);
/* 9.3.13 MS Power */
if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER))
if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER)) {
lchan->ms_power = *TLVP_VAL(&tp, RSL_IE_MS_POWER);
lchan->ms_power_ctrl.current = lchan->ms_power;
lchan->ms_power_ctrl.fixed = 0;
}
/* 9.3.24 Timing Advance */
if (TLVP_PRESENT(&tp, RSL_IE_TIMING_ADVANCE))
lchan->rqd_ta = *TLVP_VAL(&tp, RSL_IE_TIMING_ADVANCE);
@@ -828,12 +837,12 @@ static int rsl_rx_chan_activ(struct msgb *msg)
}
/* 9.3.52 MultiRate Configuration */
if (TLVP_PRESENT(&tp, RSL_IE_MR_CONFIG)) {
if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_conf)) {
if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_bts_lv) - 1) {
LOGP(DRSL, LOGL_ERROR, "Error parsing MultiRate conf IE\n");
return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT);
}
memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
memcpy(lchan->mr_bts_lv, TLVP_VAL(&tp, RSL_IE_MR_CONFIG) - 1,
TLVP_LEN(&tp, RSL_IE_MR_CONFIG) + 1);
amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
@@ -848,7 +857,7 @@ static int rsl_rx_chan_activ(struct msgb *msg)
/* actually activate the channel in the BTS */
lchan->rel_act_kind = LCHAN_REL_ACT_RSL;
rc = bts_model_rsl_chan_act(msg->lchan, &tp);
rc = l1sap_chan_act(lchan->ts->trx, dch->chan_nr, &tp);
if (rc < 0)
return rsl_tx_chan_act_nack(lchan, -rc);
@@ -856,10 +865,8 @@ static int rsl_rx_chan_activ(struct msgb *msg)
}
/* 8.4.14 RF CHANnel RELease is received */
static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan)
static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan, uint8_t chan_nr)
{
int rc;
if (lchan->abis_ip.rtp_socket) {
rsl_tx_ipac_dlcx_ind(lchan, RSL_ERR_NORMAL_UNSPEC);
osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
@@ -871,9 +878,11 @@ static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan)
handover_reset(lchan);
lchan->rel_act_kind = LCHAN_REL_ACT_RSL;
rc = bts_model_rsl_chan_rel(lchan);
l1sap_chan_rel(lchan->ts->trx, chan_nr);
return rc;
lapdm_channel_exit(&lchan->lapdm_ch);
return 0;
}
#ifdef FAKE_CIPH_MODE_COMPL
@@ -1056,10 +1065,10 @@ static int rsl_tx_mode_modif_ack(struct gsm_lchan *lchan)
/* 8.4.9 MODE MODIFY */
static int rsl_rx_mode_modif(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
struct gsm_lchan *lchan = msg->lchan;
struct rsl_ie_chan_mode *cm;
struct tlv_parsed tp;
int rc;
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
@@ -1085,12 +1094,12 @@ static int rsl_rx_mode_modif(struct msgb *msg)
/* 9.3.52 MultiRate Configuration */
if (TLVP_PRESENT(&tp, RSL_IE_MR_CONFIG)) {
if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_conf)) {
if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_bts_lv) - 1) {
LOGP(DRSL, LOGL_ERROR, "Error parsing MultiRate conf IE\n");
return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT);
}
memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
memcpy(lchan->mr_bts_lv, TLVP_VAL(&tp, RSL_IE_MR_CONFIG) - 1,
TLVP_LEN(&tp, RSL_IE_MR_CONFIG) + 1);
amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
@@ -1100,12 +1109,32 @@ static int rsl_rx_mode_modif(struct msgb *msg)
/* 9.3.53 MultiRate Control */
/* 9.3.54 Supported Codec Types */
rc = bts_model_rsl_mode_modify(msg->lchan);
l1sap_chan_modify(lchan->ts->trx, dch->chan_nr);
/* FIXME: delay this until L1 says OK? */
rsl_tx_mode_modif_ack(msg->lchan);
rsl_tx_mode_modif_ack(lchan);
return rc;
return 0;
}
/* 8.4.15 MS POWER CONTROL */
static int rsl_rx_ms_pwr_ctrl(struct msgb *msg)
{
struct gsm_lchan *lchan = msg->lchan;
struct tlv_parsed tp;
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER)) {
uint8_t pwr = *TLVP_VAL(&tp, RSL_IE_MS_POWER) & 0x1F;
lchan->ms_power_ctrl.fixed = 1;
lchan->ms_power_ctrl.current = pwr;
LOGP(DRSL, LOGL_NOTICE, "%s forcing power to %d\n",
gsm_lchan_name(lchan), lchan->ms_power_ctrl.current);
bts_model_adjst_ms_pwr(lchan);
}
return 0;
}
/* 8.4.20 SACCH INFO MODify */
@@ -1163,6 +1192,37 @@ static int rsl_rx_sacch_inf_mod(struct msgb *msg)
/*
* ip.access related messages
*/
static void rsl_add_rtp_stats(struct gsm_lchan *lchan, struct msgb *msg)
{
struct ipa_stats {
uint32_t packets_sent;
uint32_t octets_sent;
uint32_t packets_recv;
uint32_t octets_recv;
uint32_t packets_lost;
uint32_t arrival_jitter;
uint32_t avg_tx_delay;
} __attribute__((packed));
struct ipa_stats stats;
memset(&stats, 0, sizeof(stats));
osmo_rtp_socket_stats(lchan->abis_ip.rtp_socket,
&stats.packets_sent, &stats.octets_sent,
&stats.packets_recv, &stats.octets_recv,
&stats.packets_lost, &stats.arrival_jitter);
/* convert to network byte order */
stats.packets_sent = htonl(stats.packets_sent);
stats.octets_sent = htonl(stats.octets_sent);
stats.packets_recv = htonl(stats.packets_recv);
stats.octets_recv = htonl(stats.octets_recv);
stats.packets_lost = htonl(stats.packets_lost);
msgb_tlv_put(msg, RSL_IE_IPAC_CONN_STAT, sizeof(stats), (uint8_t *) &stats);
}
int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause)
{
@@ -1175,6 +1235,8 @@ int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause)
if (!nmsg)
return -ENOMEM;
msgb_tv16_put(nmsg, RSL_IE_IPAC_CONN_ID, htons(lchan->abis_ip.conn_id));
rsl_add_rtp_stats(lchan, nmsg);
msgb_tlv_put(nmsg, RSL_IE_CAUSE, 1, &cause);
rsl_ipa_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND, gsm_lchan2chan_nr(lchan));
@@ -1246,8 +1308,10 @@ static int rsl_tx_ipac_dlcx_ack(struct gsm_lchan *lchan, int inc_conn_id)
if (!msg)
return -ENOMEM;
if (inc_conn_id)
if (inc_conn_id) {
msgb_tv_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
rsl_add_rtp_stats(lchan, msg);
}
rsl_ipa_push_hdr(msg, RSL_MT_IPAC_DLCX_ACK, chan_nr);
msg->trx = lchan->ts->trx;
@@ -1416,7 +1480,7 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
OSMO_RTP_P_JITBUF,
btsb->rtp_jitter_buf_ms);
lchan->abis_ip.rtp_socket->priv = lchan;
lchan->abis_ip.rtp_socket->rx_cb = &bts_model_rtp_rx_cb;
lchan->abis_ip.rtp_socket->rx_cb = &l1sap_rtp_rx_cb;
if (connect_ip && connect_port) {
/* if CRCX specifies a remote IP, we can bind()
@@ -1526,11 +1590,11 @@ static int rsl_rx_ipac_dlcx(struct msgb *msg)
if (TLVP_PRESENT(&tp, RSL_IE_IPAC_CONN_ID))
inc_conn_id = 1;
rc = rsl_tx_ipac_dlcx_ack(lchan, inc_conn_id);
osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
lchan->abis_ip.rtp_socket = NULL;
msgb_queue_flush(&lchan->dl_tch_queue);
return rsl_tx_ipac_dlcx_ack(lchan, inc_conn_id);
return rc;
}
/*
@@ -1611,22 +1675,25 @@ static int rslms_is_meas_rep(struct msgb *msg)
static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len)
{
struct msgb *msg;
uint8_t meas_res[16];
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
int res_valid = lchan->meas.flags & LC_UL_M_F_RES_VALID;
LOGP(DRSL, LOGL_NOTICE, "%s Tx MEAS RES\n", gsm_lchan_name(lchan));
LOGP(DRSL, LOGL_NOTICE, "%s Tx MEAS RES valid(%d)\n",
gsm_lchan_name(lchan), res_valid);
if (!res_valid)
return -EINPROGRESS;
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr++);
if (lchan->meas.flags & LC_UL_M_F_RES_VALID) {
uint8_t meas_res[16];
int ie_len = lchan_build_rsl_ul_meas(lchan, meas_res);
if (ie_len >= 3) {
msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, ie_len, meas_res);
lchan->meas.flags &= ~LC_UL_M_F_RES_VALID;
}
int ie_len = lchan_build_rsl_ul_meas(lchan, meas_res);
if (ie_len >= 3) {
msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, ie_len, meas_res);
lchan->meas.flags &= ~LC_UL_M_F_RES_VALID;
}
msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->meas.bts_tx_pwr);
if (lchan->meas.flags & LC_UL_M_F_L1_VALID) {
@@ -1758,13 +1825,13 @@ static int rsl_rx_dchan(struct gsm_bts_trx *trx, struct msgb *msg)
ret = rsl_rx_chan_activ(msg);
break;
case RSL_MT_RF_CHAN_REL:
ret = rsl_rx_rf_chan_rel(msg->lchan);
ret = rsl_rx_rf_chan_rel(msg->lchan, dch->chan_nr);
break;
case RSL_MT_SACCH_INFO_MODIFY:
ret = rsl_rx_sacch_inf_mod(msg);
break;
case RSL_MT_DEACTIVATE_SACCH:
ret = bts_model_rsl_deact_sacch(msg->lchan);
ret = l1sap_chan_deact_sacch(trx, dch->chan_nr);
break;
case RSL_MT_ENCR_CMD:
ret = rsl_rx_encr_cmd(msg);
@@ -1772,6 +1839,9 @@ static int rsl_rx_dchan(struct gsm_bts_trx *trx, struct msgb *msg)
case RSL_MT_MODE_MODIFY_REQ:
ret = rsl_rx_mode_modif(msg);
break;
case RSL_MT_MS_POWER_CONTROL:
ret = rsl_rx_ms_pwr_ctrl(msg);
break;
case RSL_MT_PHY_CONTEXT_REQ:
case RSL_MT_PREPROC_CONFIG:
case RSL_MT_RTD_REP:

View File

@@ -24,12 +24,15 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/abis_nm.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/misc.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/trau/osmo_ortp.h>
@@ -44,8 +47,7 @@
#include <osmo-bts/bts_model.h>
#include <osmo-bts/measurement.h>
#include <osmo-bts/vty.h>
#include <osmo-bts/paging.h>
#include <osmo-bts/l1sap.h>
enum node_type bts_vty_go_parent(struct vty *vty)
{
@@ -139,7 +141,7 @@ static struct cmd_node trx_node = {
};
DEFUN(cfg_bts_trx, cfg_bts_trx_cmd,
"trx <0-0>",
"trx <0-254>",
"Select a TRX to configure\n" "TRX number\n")
{
int trx_nr = atoi(argv[0]);
@@ -148,7 +150,8 @@ DEFUN(cfg_bts_trx, cfg_bts_trx_cmd,
trx = gsm_bts_trx_num(bts, trx_nr);
if (!trx) {
vty_out(vty, "Unknown TRX %u%s", trx_nr, VTY_NEWLINE);
vty_out(vty, "Unknown TRX %u. Aavialable TRX are: 0..%d%s",
trx_nr, bts->num_trx - 1, VTY_NEWLINE);
return CMD_WARNING;
}
@@ -159,10 +162,38 @@ DEFUN(cfg_bts_trx, cfg_bts_trx_cmd,
return CMD_SUCCESS;
}
/* FIXME: move to libosmocore ? */
static char buf_casecnvt[256];
char *osmo_str_tolower(const char *in)
{
int len, i;
if (!in)
return NULL;
len = strlen(in);
if (len > sizeof(buf_casecnvt))
len = sizeof(buf_casecnvt);
for (i = 0; i < len; i++) {
buf_casecnvt[i] = tolower(in[i]);
if (in[i] == '\0')
break;
}
if (i < sizeof(buf_casecnvt))
buf_casecnvt[i] = '\0';
/* just to make sure we're always zero-terminated */
buf_casecnvt[sizeof(buf_casecnvt)-1] = '\0';
return buf_casecnvt;
}
static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
struct gsm_bts_trx *trx;
int i;
vty_out(vty, "bts %u%s", bts->nr, VTY_NEWLINE);
if (bts->description)
@@ -177,6 +208,7 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
VTY_NEWLINE);
vty_out(vty, " paging lifetime %u%s", paging_get_lifetime(btsb->paging_state),
VTY_NEWLINE);
vty_out(vty, " uplink-power-target %d%s", btsb->ul_power_target, VTY_NEWLINE);
if (btsb->agch_queue_thresh_level != GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT
|| btsb->agch_queue_low_level != GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT
|| btsb->agch_queue_high_level != GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT)
@@ -184,6 +216,17 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
btsb->agch_queue_thresh_level, btsb->agch_queue_low_level,
btsb->agch_queue_high_level, VTY_NEWLINE);
for (i = 0; i < 32; i++) {
if (gsmtap_sapi_mask & (1 << i)) {
const char *name = get_value_string(gsmtap_sapi_names, i);
vty_out(vty, " gsmtap-sapi %s%s", osmo_str_tolower(name), VTY_NEWLINE);
}
}
if (gsmtap_sapi_acch) {
const char *name = get_value_string(gsmtap_sapi_names, GSMTAP_CHANNEL_ACCH);
vty_out(vty, " gsmtap-sapi %s%s", osmo_str_tolower(name), VTY_NEWLINE);
}
bts_model_config_write_bts(vty, bts);
llist_for_each_entry(trx, &bts->trx_list, list) {
@@ -199,6 +242,9 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
tpp->ramp.step_size_mdB, VTY_NEWLINE);
vty_out(vty, " power-ramp step-interval %d%s",
tpp->ramp.step_interval_sec, VTY_NEWLINE);
vty_out(vty, " ms-power-control %s%s",
trx->ms_power_control == 0 ? "dsp" : "osmo",
VTY_NEWLINE);
bts_model_config_write_trx(vty, trx);
}
@@ -397,6 +443,19 @@ DEFUN(cfg_bts_agch_queue_mgmt_default,
return CMD_SUCCESS;
}
DEFUN(cfg_bts_ul_power_target, cfg_bts_ul_power_target_cmd,
"uplink-power-target <-110-0>",
"Set the nominal target Rx Level for uplink power control loop\n"
"Target uplink Rx level in dBm\n")
{
struct gsm_bts *bts = vty->index;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
btsb->ul_power_target = atoi(argv[0]);
return CMD_SUCCESS;
}
#define DB_DBM_STR \
"Unit is dB (decibels)\n" \
"Unit is mdB (milli-decibels, or rather 1/10000 bel)\n"
@@ -461,6 +520,16 @@ DEFUN(cfg_trx_pr_step_interval, cfg_trx_pr_step_interval_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_trx_ms_power_control, cfg_trx_ms_power_control_cmd,
"ms-power-control (dsp|osmo)",
"Mobile Station Power Level Control (change requires restart)\n"
"Handled by DSP\n" "Handled by OsmoBTS\n")
{
struct gsm_bts_trx *trx = vty->index;
trx->ms_power_control = argv[0][0] == 'd' ? 0 : 1;
return CMD_SUCCESS;
}
/* ======================================================================
@@ -602,6 +671,36 @@ static struct gsm_lchan *resolve_lchan(struct gsm_network *net,
"logical channel commands\n" \
"logical channel number\n"
DEFUN(cfg_trx_gsmtap_sapi, cfg_trx_gsmtap_sapi_cmd,
"HIDDEN", "HIDDEN")
{
int sapi;
sapi = get_string_value(gsmtap_sapi_names, argv[0]);
if (sapi == GSMTAP_CHANNEL_ACCH)
gsmtap_sapi_acch = 1;
else
gsmtap_sapi_mask |= (1 << sapi);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_no_gsmtap_sapi, cfg_trx_no_gsmtap_sapi_cmd,
"HIDDEN", "HIDDEN")
{
int sapi;
sapi = get_string_value(gsmtap_sapi_names, argv[0]);
if (sapi == GSMTAP_CHANNEL_ACCH)
gsmtap_sapi_acch = 0;
else
gsmtap_sapi_mask &= ~(1 << sapi);
return CMD_SUCCESS;
}
DEFUN(bts_t_t_l_jitter_buf,
bts_t_t_l_jitter_buf_cmd,
"bts <0-0> trx <0-0> ts <0-7> lchan <0-1> rtp jitter-buffer <0-10000>",
@@ -628,61 +727,58 @@ DEFUN(bts_t_t_l_jitter_buf,
return CMD_SUCCESS;
}
extern int construct_p1_rest_octets(struct bitvec *bv, int etws_will_follow);
extern int construct_etws_prim_notif(struct bitvec *bv, uint8_t pni,
uint8_t seg_nr, uint8_t num_segs,
const uint8_t *payload,
uint8_t num_payload_bits);
DEFUN(bts_etsw_idle, bts_etsw_idle_cmd,
"etsw-message MESSAGE",
"ETSW Message\nMessage in Hex\n")
DEFUN(bts_t_t_l_loopback,
bts_t_t_l_loopback_cmd,
"bts <0-0> trx <0-0> ts <0-7> lchan <0-1> loopback",
BTS_T_T_L_STR "Set loopback\n")
{
int rc;
int segment = 0, rest = 56;
rc = osmo_hexparse(argv[0], etws_data, sizeof(etws_data));
if (rc != 56) {
vty_out(vty, "%%we expect 56 bytes of the data.%s", VTY_NEWLINE);
struct gsm_network *net = gsmnet_from_vty(vty);
struct gsm_lchan *lchan;
lchan = resolve_lchan(net, argv, 0);
if (!lchan) {
vty_out(vty, "%% can't find BTS%s", VTY_NEWLINE);
return CMD_WARNING;
}
lchan->loopback = 1;
vty_out(vty, "%% parsed: %s%s",
osmo_hexdump(etws_data, rc), VTY_NEWLINE);
etws_len = rc;
for (segment = 0; segment < 5; ++segment) {
struct bitvec bv = { 0, };
bv.data_len = 14;
bv.data = &etws_segment_data[segment][0];
LOGP(DPAG, LOGL_NOTICE, "Goint to create segment(%d) offset %d len %d\n",
segment, segment * 12,
rest >= 12 ? 12 : rest);
construct_p1_rest_octets(&bv, 1);
printf("CUR BIT: %d %s\n", bv.cur_bit,
osmo_hexdump(&etws_data[segment * 12],
rest >= 12 ? 12 : rest));
construct_etws_prim_notif(&bv, 1, segment, 5,
&etws_data[segment * 12],
rest >= 12 ? 12 * 8 : rest * 8);
etws_segment_len[segment] = (bv.cur_bit + 7) / 8;
rest -= 12;
LOGP(DPAG, LOGL_NOTICE,
"Created segment(%d) with len %d %s\n",
segment, etws_segment_len[segment],
osmo_hexdump(etws_segment_data[segment], etws_segment_len[segment]));
}
etws_nr_seg = 5;
return CMD_SUCCESS;
}
int bts_vty_init(const struct log_info *cat)
DEFUN(no_bts_t_t_l_loopback,
no_bts_t_t_l_loopback_cmd,
"no bts <0-0> trx <0-0> ts <0-7> lchan <0-1> loopback",
NO_STR BTS_T_T_L_STR "Set loopback\n")
{
struct gsm_network *net = gsmnet_from_vty(vty);
struct gsm_lchan *lchan;
lchan = resolve_lchan(net, argv, 0);
if (!lchan) {
vty_out(vty, "%% can't find BTS%s", VTY_NEWLINE);
return CMD_WARNING;
}
lchan->loopback = 0;
return CMD_SUCCESS;
}
int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat)
{
cfg_trx_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names,
"gsmtap-sapi (",
"|",")", VTY_DO_LOWER);
cfg_trx_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names,
"GSMTAP SAPI\n",
"\n", "", 0);
cfg_trx_no_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names,
"no gsmtap-sapi (",
"|",")", VTY_DO_LOWER);
cfg_trx_no_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names,
NO_STR "GSMTAP SAPI\n",
"\n", "", 0);
install_element_ve(&show_bts_cmd);
logging_vty_add_cmds(cat);
@@ -701,6 +797,10 @@ int bts_vty_init(const struct log_info *cat)
install_element(BTS_NODE, &cfg_bts_paging_lifetime_cmd);
install_element(BTS_NODE, &cfg_bts_agch_queue_mgmt_default_cmd);
install_element(BTS_NODE, &cfg_bts_agch_queue_mgmt_params_cmd);
install_element(BTS_NODE, &cfg_bts_ul_power_target_cmd);
install_element(BTS_NODE, &cfg_trx_gsmtap_sapi_cmd);
install_element(BTS_NODE, &cfg_trx_no_gsmtap_sapi_cmd);
/* add and link to TRX config node */
install_element(BTS_NODE, &cfg_bts_trx_cmd);
@@ -711,9 +811,9 @@ int bts_vty_init(const struct log_info *cat)
install_element(TRX_NODE, &cfg_trx_pr_max_initial_cmd);
install_element(TRX_NODE, &cfg_trx_pr_step_size_cmd);
install_element(TRX_NODE, &cfg_trx_pr_step_interval_cmd);
install_element(TRX_NODE, &cfg_trx_ms_power_control_cmd);
install_element(ENABLE_NODE, &bts_t_t_l_jitter_buf_cmd);
install_element(ENABLE_NODE, &bts_etsw_idle_cmd);
return 0;
}

View File

@@ -1,5 +1,5 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOCTRL_CFLAGS)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBGPS_CFLAGS)
COMMON_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCTRL_LIBS) -lortp
EXTRA_DIST = misc/sysmobts_mgr.h misc/sysmobts_misc.h misc/sysmobts_par.h \
@@ -27,8 +27,9 @@ sysmobts_mgr_SOURCES = \
misc/sysmobts_mgr_vty.c \
misc/sysmobts_mgr_nl.c \
misc/sysmobts_mgr_temp.c \
misc/sysmobts_mgr_calib.c \
eeprom.c
sysmobts_mgr_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS)
sysmobts_mgr_LDADD = $(LIBGPS_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCTRL_LIBS) $(top_builddir)/src/common/libbts.a
sysmobts_util_SOURCES = misc/sysmobts_util.c misc/sysmobts_par.c eeprom.c
sysmobts_util_LDADD = $(LIBOSMOCORE_LIBS)

View File

@@ -77,7 +77,12 @@ int l1if_handle_l1prim(int wq, struct femtol1_hdl *fl1h, struct msgb *msg)
struct l1fwd_hdl *l1fh = fl1h->priv;
/* Enqueue message to UDP socket */
return osmo_wqueue_enqueue(&l1fh->udp_wq[wq], msg);
if (osmo_wqueue_enqueue(&l1fh->udp_wq[wq], msg) != 0) {
LOGP(DL1C, LOGL_ERROR, "Write queue %d full. dropping msg\n", wq);
msgb_free(msg);
return -EAGAIN;
}
return 0;
}
/* callback when there's a new SYS primitive coming in from the HW */
@@ -86,7 +91,12 @@ int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg)
struct l1fwd_hdl *l1fh = fl1h->priv;
/* Enqueue message to UDP socket */
return osmo_wqueue_enqueue(&l1fh->udp_wq[MQ_SYS_WRITE], msg);
if (osmo_wqueue_enqueue(&l1fh->udp_wq[MQ_SYS_WRITE], msg) != 0) {
LOGP(DL1C, LOGL_ERROR, "MQ_SYS_WRITE ful. dropping msg\n");
msgb_free(msg);
return -EAGAIN;
}
return 0;
}
@@ -121,9 +131,13 @@ static int udp_read_cb(struct osmo_fd *ofd)
ofd->priv_nr);
/* put the message into the right queue */
rc = osmo_wqueue_enqueue(&fl1h->write_q[ofd->priv_nr], msg);
return rc;
if (osmo_wqueue_enqueue(&fl1h->write_q[ofd->priv_nr], msg) != 0) {
LOGP(DL1C, LOGL_ERROR, "Write queue %d full. dropping msg\n",
ofd->priv_nr);
msgb_free(msg);
return -EAGAIN;
}
return 0;
}
/* callback when we can write to the UDP socket */

File diff suppressed because it is too large Load Diff

View File

@@ -46,16 +46,12 @@ struct femtol1_hdl {
uint32_t dsp_trace_f;
uint8_t clk_use_eeprom;
int clk_cal;
int ul_power_target;
uint8_t clk_src;
float min_qual_rach;
float min_qual_norm;
char *calib_path;
struct llist_head wlc_list;
struct gsmtap_inst *gsmtap;
uint32_t gsmtap_sapi_mask;
void *priv; /* user reference */
struct osmo_timer_list alive_timer;
@@ -110,7 +106,9 @@ uint32_t l1if_lchan_to_hLayer(struct gsm_lchan *lchan);
struct gsm_lchan *l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer);
/* tch.c */
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
void l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
const uint8_t *rtp_pl, unsigned int rtp_pl_len);
int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg);
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
@@ -119,6 +117,13 @@ int l1if_set_ciphering(struct femtol1_hdl *fl1h,
struct gsm_lchan *lchan,
int dir_downlink);
/* channel control */
int l1if_rsl_chan_act(struct gsm_lchan *lchan);
int l1if_rsl_chan_rel(struct gsm_lchan *lchan);
int l1if_rsl_chan_mod(struct gsm_lchan *lchan);
int l1if_rsl_deact_sacch(struct gsm_lchan *lchan);
int l1if_rsl_mode_modify(struct gsm_lchan *lchan);
/* calibration loading */
int calib_load(struct femtol1_hdl *fl1h);
@@ -129,7 +134,6 @@ int l1if_rf_clock_info_correct(struct femtol1_hdl *fl1h);
/* public helpers for test */
int bts_check_for_ciph_cmd(struct femtol1_hdl *fl1h,
struct msgb *msg, struct gsm_lchan *lchan);
void bts_check_for_first_ciphrd(struct femtol1_hdl *fl1h,
GsmL1_MsgUnitParam_t *msgUnitParam,
struct gsm_lchan *lchan);
inline int l1if_ms_pwr_ctrl(struct gsm_lchan *lchan, const int uplink_target,
const uint8_t ms_power, const float rxLevel);
#endif /* _FEMTO_L1_H */

View File

@@ -95,7 +95,7 @@ static int fwd_read_cb(struct osmo_fd *ofd)
static int prim_write_cb(struct osmo_fd *ofd, struct msgb *msg)
{
/* write to the fd */
return write(ofd->fd, msg->head, msg->len);
return write(ofd->fd, msg->l1h, msgb_l1len(msg));
}
int l1if_transport_open(int q, struct femtol1_hdl *fl1h)

View File

@@ -38,6 +38,8 @@
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/ports.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/gsmtap.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
@@ -47,6 +49,7 @@
#include <osmo-bts/bts_model.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/control_if.h>
#include <osmo-bts/l1sap.h>
#define SYSMOBTS_RF_LOCK_PATH "/var/lock/bts_rf_lock"
@@ -62,6 +65,7 @@ static const char *config_file = "osmo-bts.cfg";
static int daemonize = 0;
static unsigned int dsp_trace = 0x71c00020;
static int rt_prio = -1;
static char *gsmtap_ip = 0;
int bts_model_init(struct gsm_bts *bts)
{
@@ -165,6 +169,7 @@ static void print_help()
" -w --hw-version Print the targeted HW Version\n"
" -M --pcu-direct Force PCU to access message queue for "
"PDCH dchannel directly\n"
" -i --gsmtap-ip The destination IP used for GSMTAP.\n"
);
}
@@ -196,10 +201,11 @@ static void handle_options(int argc, char **argv)
{ "hw-version", 0, 0, 'w' },
{ "pcu-direct", 0, 0, 'M' },
{ "realtime", 1, 0, 'r' },
{ "gsmtap-ip", 1, 0, 'i' },
{ 0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "hc:d:Dc:sTVe:p:w:Mr:",
c = getopt_long(argc, argv, "hc:d:Dc:sTVe:p:w:Mr:i:",
long_options, &option_idx);
if (c == -1)
break;
@@ -244,6 +250,9 @@ static void handle_options(int argc, char **argv)
case 'r':
rt_prio = atoi(optarg);
break;
case 'i':
gsmtap_ip = optarg;
break;
default:
break;
}
@@ -308,8 +317,10 @@ int main(int argc, char **argv)
bts_log_init(NULL);
bts = gsm_bts_alloc(tall_bts_ctx);
vty_init(&bts_vty_info);
bts_vty_init(&bts_log_info);
e1inp_vty_init();
bts_vty_init(bts, &bts_log_info);
handle_options(argc, argv);
@@ -325,7 +336,15 @@ int main(int argc, char **argv)
}
}
bts = gsm_bts_alloc(tall_bts_ctx);
if (gsmtap_ip) {
gsmtap = gsmtap_source_init(gsmtap_ip, GSMTAP_UDP_PORT, 1);
if (!gsmtap) {
fprintf(stderr, "Failed during gsmtap_init()\n");
exit(1);
}
gsmtap_source_add_sink(gsmtap);
}
if (bts_init(bts) < 0) {
fprintf(stderr, "unable to open bts\n");
exit(1);
@@ -401,3 +420,9 @@ int main(int argc, char **argv)
osmo_select_main(0);
}
}
void bts_model_abis_close(struct gsm_bts *bts)
{
/* for now, we simply terminate the program and re-spawn */
bts_shutdown(bts, "Abis close");
}

View File

@@ -224,6 +224,12 @@ static struct log_info_cat mgr_log_info_cat[] = {
.color = "\033[1;37m",
.enabled = 1, .loglevel = LOGL_INFO,
},
[DCALIB] = {
.name = "DCALIB",
.description = "Calibration handling",
.color = "\033[1;37m",
.enabled = 1, .loglevel = LOGL_INFO,
},
};
static const struct log_info mgr_log_info = {
@@ -292,6 +298,9 @@ int main(int argc, char **argv)
/* Initialize the temperature control */
sysmobts_mgr_temp_init(&manager);
if (sysmobts_mgr_calib_init(&manager) != 0)
exit(3);
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {

View File

@@ -4,10 +4,18 @@
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include <gps.h>
#include <stdint.h>
enum {
DTEMP,
DFW,
DFIND,
DCALIB,
};
@@ -74,6 +82,27 @@ struct sysmobts_mgr_instance {
int action_crit;
enum sysmobts_temp_state state;
struct {
int initial_calib_started;
int is_up;
struct osmo_timer_list recon_timer;
struct ipa_client_conn *bts_conn;
int state;
struct osmo_timer_list timer;
uint32_t last_seqno;
/* gps structure to see if there is a fix */
int gps_open;
struct osmo_fd gpsfd;
struct gps_data_t gpsdata;
struct osmo_timer_list fix_timeout;
/* Loop/Re-try control */
int calib_from_loop;
struct osmo_timer_list calib_timeout;
} calib;
};
int sysmobts_mgr_vty_init(void);
@@ -82,4 +111,11 @@ int sysmobts_mgr_nl_init(void);
int sysmobts_mgr_temp_init(struct sysmobts_mgr_instance *mgr);
const char *sysmobts_mgr_temp_get_state(enum sysmobts_temp_state state);
int sysmobts_mgr_calib_init(struct sysmobts_mgr_instance *mgr);
int sysmobts_mgr_calib_run(struct sysmobts_mgr_instance *mgr);
extern void *tall_mgr_ctx;
#endif

View File

@@ -0,0 +1,538 @@
/* OCXO/TCXO calibration control for SysmoBTS management daemon */
/*
* (C) 2014,2015 by Holger Hans Peter Freyther
* (C) 2014 by Harald Welte for the IPA code from the oml router
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "misc/sysmobts_mgr.h"
#include "misc/sysmobts_misc.h"
#include "osmo-bts/msg_utils.h"
#include <osmocom/core/logging.h>
#include <osmocom/core/select.h>
#include <osmocom/ctrl/control_cmd.h>
#include <osmocom/gsm/ipa.h>
#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmocom/abis/abis.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/abis/ipa.h>
static int calib_run(struct sysmobts_mgr_instance *mgr, int from_loop);
static void calib_state_reset(struct sysmobts_mgr_instance *mgr, int reason);
static void request_clock_reset(struct sysmobts_mgr_instance *mgr);
static void bts_updown_cb(struct ipa_client_conn *link, int up);
enum calib_state {
CALIB_INITIAL,
CALIB_GPS_WAIT_FOR_FIX,
CALIB_CTR_RESET,
CALIB_CTR_WAIT,
CALIB_COR_SET,
};
enum calib_result {
CALIB_FAIL_START,
CALIB_FAIL_GPS,
CALIB_FAIL_CTRL,
CALIB_SUCESS,
};
static void calib_loop_run(void *_data)
{
int rc;
struct sysmobts_mgr_instance *mgr = _data;
LOGP(DCALIB, LOGL_NOTICE, "Going to calibrate the system.\n");
rc = calib_run(mgr, 1);
if (rc != 0)
calib_state_reset(mgr, CALIB_FAIL_START);
}
static void mgr_gps_close(struct sysmobts_mgr_instance *mgr)
{
if (!mgr->calib.gps_open)
return;
osmo_timer_del(&mgr->calib.fix_timeout);
osmo_fd_unregister(&mgr->calib.gpsfd);
gps_close(&mgr->calib.gpsdata);
memset(&mgr->calib.gpsdata, 0, sizeof(mgr->calib.gpsdata));
mgr->calib.gps_open = 0;
}
static void mgr_gps_checkfix(struct sysmobts_mgr_instance *mgr)
{
struct gps_data_t *data = &mgr->calib.gpsdata;
/* No 2D fix yet */
if (data->fix.mode < MODE_2D) {
LOGP(DCALIB, LOGL_DEBUG, "Fix mode not enough: %d\n",
data->fix.mode);
return;
}
/* The trimble driver is broken...add some sanity checking */
if (data->satellites_used < 1) {
LOGP(DCALIB, LOGL_DEBUG, "Not enough satellites used: %d\n",
data->satellites_used);
return;
}
LOGP(DCALIB, LOGL_NOTICE, "Got a GPS fix continuing.\n");
osmo_timer_del(&mgr->calib.fix_timeout);
mgr_gps_close(mgr);
request_clock_reset(mgr);
}
static int mgr_gps_read(struct osmo_fd *fd, unsigned int what)
{
int rc;
struct sysmobts_mgr_instance *mgr = fd->data;
rc = gps_read(&mgr->calib.gpsdata);
if (rc == -1) {
LOGP(DCALIB, LOGL_ERROR, "gpsd vanished during read.\n");
calib_state_reset(mgr, CALIB_FAIL_GPS);
return -1;
}
if (rc > 0)
mgr_gps_checkfix(mgr);
return 0;
}
static void mgr_gps_fix_timeout(void *_data)
{
struct sysmobts_mgr_instance *mgr = _data;
LOGP(DCALIB, LOGL_ERROR, "Failed to acquire GPRS fix.\n");
calib_state_reset(mgr, CALIB_FAIL_GPS);
}
static void mgr_gps_open(struct sysmobts_mgr_instance *mgr)
{
int rc;
rc = gps_open("localhost", DEFAULT_GPSD_PORT, &mgr->calib.gpsdata);
if (rc != 0) {
LOGP(DCALIB, LOGL_ERROR, "Failed to connect to GPS %d\n", rc);
calib_state_reset(mgr, CALIB_FAIL_GPS);
return;
}
mgr->calib.gps_open = 1;
gps_stream(&mgr->calib.gpsdata, WATCH_ENABLE, NULL);
mgr->calib.gpsfd.data = mgr;
mgr->calib.gpsfd.cb = mgr_gps_read;
mgr->calib.gpsfd.when = BSC_FD_READ | BSC_FD_EXCEPT;
mgr->calib.gpsfd.fd = mgr->calib.gpsdata.gps_fd;
if (osmo_fd_register(&mgr->calib.gpsfd) < 0) {
LOGP(DCALIB, LOGL_ERROR, "Failed to register GPSD fd\n");
calib_state_reset(mgr, CALIB_FAIL_GPS);
}
mgr->calib.state = CALIB_GPS_WAIT_FOR_FIX;
mgr->calib.fix_timeout.data = mgr;
mgr->calib.fix_timeout.cb = mgr_gps_fix_timeout;
osmo_timer_schedule(&mgr->calib.fix_timeout, 60, 0);
LOGP(DCALIB, LOGL_NOTICE,
"Opened the GPSD connection waiting for fix: %d\n",
mgr->calib.gpsfd.fd);
}
static void send_ctrl_cmd(struct sysmobts_mgr_instance *mgr,
struct msgb *msg)
{
ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
ipa_prepend_header(msg, IPAC_PROTO_OSMO);
ipa_client_conn_send(mgr->calib.bts_conn, msg);
}
static void send_set_ctrl_cmd_int(struct sysmobts_mgr_instance *mgr,
const char *key, const int val)
{
struct msgb *msg;
int ret;
msg = msgb_alloc_headroom(1024, 128, "CTRL SET");
ret = snprintf((char *) msg->data, 4096, "SET %u %s %d",
mgr->calib.last_seqno++, key, val);
msg->l2h = msgb_put(msg, ret);
return send_ctrl_cmd(mgr, msg);
}
static void send_set_ctrl_cmd(struct sysmobts_mgr_instance *mgr,
const char *key, const char *val)
{
struct msgb *msg;
int ret;
msg = msgb_alloc_headroom(1024, 128, "CTRL SET");
ret = snprintf((char *) msg->data, 4096, "SET %u %s %s",
mgr->calib.last_seqno++, key, val);
msg->l2h = msgb_put(msg, ret);
return send_ctrl_cmd(mgr, msg);
}
static void send_get_ctrl_cmd(struct sysmobts_mgr_instance *mgr,
const char *key)
{
struct msgb *msg;
int ret;
msg = msgb_alloc_headroom(1024, 128, "CTRL GET");
ret = snprintf((char *) msg->data, 4096, "GET %u %s",
mgr->calib.last_seqno++, key);
msg->l2h = msgb_put(msg, ret);
return send_ctrl_cmd(mgr, msg);
}
static int calib_run(struct sysmobts_mgr_instance *mgr, int from_loop)
{
if (!mgr->calib.is_up) {
LOGP(DCALIB, LOGL_ERROR, "Control interface not connected.\n");
return -1;
}
if (mgr->calib.state != CALIB_INITIAL) {
LOGP(DCALIB, LOGL_ERROR, "Calib is already in progress.\n");
return -2;
}
mgr->calib.calib_from_loop = from_loop;
/* From now on everything will be handled from the failure */
mgr->calib.initial_calib_started = 1;
mgr_gps_open(mgr);
return 0;
}
int sysmobts_mgr_calib_run(struct sysmobts_mgr_instance *mgr)
{
return calib_run(mgr, 0);
}
static void request_clock_reset(struct sysmobts_mgr_instance *mgr)
{
send_set_ctrl_cmd(mgr, "trx.0.clock-info", "1");
mgr->calib.state = CALIB_CTR_RESET;
}
static void calib_state_reset(struct sysmobts_mgr_instance *mgr, int outcome)
{
if (mgr->calib.calib_from_loop) {
/*
* In case of success calibrate in two hours again
* and in case of a failure in some minutes.
*/
int timeout = 2 * 60 * 60;
if (outcome != CALIB_SUCESS)
timeout = 5 * 60;
mgr->calib.calib_timeout.data = mgr;
mgr->calib.calib_timeout.cb = calib_loop_run;
osmo_timer_schedule(&mgr->calib.calib_timeout, timeout, 0);
}
mgr->calib.state = CALIB_INITIAL;
osmo_timer_del(&mgr->calib.timer);
mgr_gps_close(mgr);
}
static void calib_get_clock_err_cb(void *_data)
{
struct sysmobts_mgr_instance *mgr = _data;
LOGP(DCALIB, LOGL_DEBUG,
"Requesting current clock-info.\n");
send_get_ctrl_cmd(mgr, "trx.0.clock-info");
}
static void handle_ctrl_reset_resp(
struct sysmobts_mgr_instance *mgr,
struct ctrl_cmd *cmd)
{
if (strcmp(cmd->variable, "trx.0.clock-info") != 0) {
LOGP(DCALIB, LOGL_ERROR,
"Unexpected variable: %s\n", cmd->variable);
calib_state_reset(mgr, CALIB_FAIL_CTRL);
return;
}
if (strcmp(cmd->reply, "success") != 0) {
LOGP(DCALIB, LOGL_ERROR,
"Unexpected reply: %s\n", cmd->variable);
calib_state_reset(mgr, CALIB_FAIL_CTRL);
return;
}
mgr->calib.state = CALIB_CTR_WAIT;
mgr->calib.timer.cb = calib_get_clock_err_cb;
mgr->calib.timer.data = mgr;
osmo_timer_schedule(&mgr->calib.timer, 60, 0);
LOGP(DCALIB, LOGL_DEBUG,
"Reset the calibration counter. Waiting 60 seconds.\n");
}
static void handle_ctrl_get_resp(
struct sysmobts_mgr_instance *mgr,
struct ctrl_cmd *cmd)
{
char *saveptr = NULL;
char *clk_cur;
char *clk_src;
char *cal_err;
char *cal_res;
char *cal_src;
int cal_err_int;
if (strcmp(cmd->variable, "trx.0.clock-info") != 0) {
LOGP(DCALIB, LOGL_ERROR,
"Unexpected variable: %s\n", cmd->variable);
calib_state_reset(mgr, CALIB_FAIL_CTRL);
return;
}
clk_cur = strtok_r(cmd->reply, ",", &saveptr);
clk_src = strtok_r(NULL, ",", &saveptr);
cal_err = strtok_r(NULL, ",", &saveptr);
cal_res = strtok_r(NULL, ",", &saveptr);
cal_src = strtok_r(NULL, ",", &saveptr);
if (!clk_cur || !clk_src || !cal_err || !cal_res || !cal_src) {
LOGP(DCALIB, LOGL_ERROR, "Parse error on clock-info reply\n");
calib_state_reset(mgr, CALIB_FAIL_CTRL);
return;
}
cal_err_int = atoi(cal_err);
LOGP(DCALIB, LOGL_NOTICE,
"Calibration CUR(%s) SRC(%s) ERR(%s/%d) RES(%s) SRC(%s)\n",
clk_cur, clk_src, cal_err, cal_err_int, cal_res, cal_src);
if (strcmp(cal_res, "0") == 0) {
LOGP(DCALIB, LOGL_ERROR, "Invalid clock resolution. Giving up\n");
calib_state_reset(mgr, CALIB_FAIL_CTRL);
return;
}
/* Now we can finally set the new value */
LOGP(DCALIB, LOGL_NOTICE,
"Going to apply %d as new clock correction.\n",
-cal_err_int);
send_set_ctrl_cmd_int(mgr, "trx.0.clock-correction", -cal_err_int);
mgr->calib.state = CALIB_COR_SET;
}
static void handle_ctrl_set_cor(
struct sysmobts_mgr_instance *mgr,
struct ctrl_cmd *cmd)
{
if (strcmp(cmd->variable, "trx.0.clock-correction") != 0) {
LOGP(DCALIB, LOGL_ERROR,
"Unexpected variable: %s\n", cmd->variable);
calib_state_reset(mgr, CALIB_FAIL_CTRL);
return;
}
if (strcmp(cmd->reply, "success") != 0) {
LOGP(DCALIB, LOGL_ERROR,
"Unexpected reply: %s\n", cmd->variable);
calib_state_reset(mgr, CALIB_FAIL_CTRL);
return;
}
LOGP(DCALIB, LOGL_NOTICE,
"Calibration process completed\n");
calib_state_reset(mgr, CALIB_SUCESS);
}
static void handle_ctrl(struct sysmobts_mgr_instance *mgr, struct msgb *msg)
{
struct ctrl_cmd *cmd = ctrl_cmd_parse(tall_mgr_ctx, msg);
if (!cmd) {
LOGP(DCALIB, LOGL_ERROR, "Failed to parse command/response\n");
return;
}
switch (cmd->type) {
case CTRL_TYPE_GET_REPLY:
switch (mgr->calib.state) {
case CALIB_CTR_WAIT:
handle_ctrl_get_resp(mgr, cmd);
break;
default:
LOGP(DCALIB, LOGL_ERROR,
"Unhandled response in state: %d %s/%s\n",
mgr->calib.state, cmd->variable, cmd->reply);
calib_state_reset(mgr, CALIB_FAIL_CTRL);
break;
};
break;
case CTRL_TYPE_SET_REPLY:
switch (mgr->calib.state) {
case CALIB_CTR_RESET:
handle_ctrl_reset_resp(mgr, cmd);
break;
case CALIB_COR_SET:
handle_ctrl_set_cor(mgr, cmd);
break;
default:
LOGP(DCALIB, LOGL_ERROR,
"Unhandled response in state: %d %s/%s\n",
mgr->calib.state, cmd->variable, cmd->reply);
calib_state_reset(mgr, CALIB_FAIL_CTRL);
break;
};
break;
case CTRL_TYPE_TRAP:
/* ignore any form of trap */
break;
default:
LOGP(DCALIB, LOGL_ERROR,
"Unhandled CTRL response: %d. Resetting state\n",
cmd->type);
calib_state_reset(mgr, CALIB_FAIL_CTRL);
break;
}
talloc_free(cmd);
}
/* Schedule a connect towards the BTS */
static void schedule_bts_connect(struct sysmobts_mgr_instance *mgr)
{
DEBUGP(DLCTRL, "Scheduling BTS connect\n");
osmo_timer_schedule(&mgr->calib.recon_timer, 1, 0);
}
/* BTS re-connect timer call-back */
static void bts_recon_timer_cb(void *data)
{
int rc;
struct sysmobts_mgr_instance *mgr = data;
/* The connection failures are to be expected during boot */
mgr->calib.bts_conn->ofd->when |= BSC_FD_WRITE;
rc = ipa_client_conn_open(mgr->calib.bts_conn);
if (rc < 0) {
LOGP(DLCTRL, LOGL_NOTICE, "Failed to connect to BTS.\n");
schedule_bts_connect(mgr);
}
}
static int bts_read_cb(struct ipa_client_conn *link, struct msgb *msg)
{
int rc;
struct ipaccess_head *hh = (struct ipaccess_head *) msgb_l1(msg);
struct ipaccess_head_ext *hh_ext;
DEBUGP(DCALIB, "Received data from BTS: %s\n",
osmo_hexdump(msgb_data(msg), msgb_length(msg)));
/* regular message handling */
rc = msg_verify_ipa_structure(msg);
if (rc < 0) {
LOGP(DCALIB, LOGL_ERROR,
"Invalid IPA message from BTS (rc=%d)\n", rc);
goto err;
}
switch (hh->proto) {
case IPAC_PROTO_IPACCESS:
/* handle the core IPA CCM messages in libosmoabis */
ipa_ccm_rcvmsg_bts_base(msg, link->ofd);
msgb_free(msg);
break;
case IPAC_PROTO_OSMO:
hh_ext = (struct ipaccess_head_ext *) hh->data;
switch (hh_ext->proto) {
case IPAC_PROTO_EXT_CTRL:
handle_ctrl(link->data, msg);
break;
default:
LOGP(DCALIB, LOGL_NOTICE,
"Unhandled osmo ID %u from BTS\n", hh_ext->proto);
};
msgb_free(msg);
break;
default:
LOGP(DCALIB, LOGL_NOTICE,
"Unhandled stream ID %u from BTS\n", hh->proto);
msgb_free(msg);
break;
}
return 0;
err:
msgb_free(msg);
return -1;
}
/* link to BSC has gone up or down */
static void bts_updown_cb(struct ipa_client_conn *link, int up)
{
struct sysmobts_mgr_instance *mgr = link->data;
LOGP(DLCTRL, LOGL_INFO, "BTS connection %s\n", up ? "up" : "down");
if (up) {
mgr->calib.is_up = 1;
mgr->calib.last_seqno = 0;
if (!mgr->calib.initial_calib_started)
calib_run(mgr, 1);
} else {
mgr->calib.is_up = 0;
schedule_bts_connect(mgr);
calib_state_reset(mgr, CALIB_FAIL_CTRL);
}
}
int sysmobts_mgr_calib_init(struct sysmobts_mgr_instance *mgr)
{
if (!is_sbts2050_master()) {
LOGP(DCALIB, LOGL_NOTICE,
"Calib is only possible on the sysmoBTS2050 master\n");
return 0;
}
mgr->calib.bts_conn = ipa_client_conn_create(tall_mgr_ctx, NULL, 0,
"localhost", 4238,
bts_updown_cb, bts_read_cb,
NULL, mgr);
if (!mgr->calib.bts_conn) {
LOGP(DCALIB, LOGL_ERROR,
"Failed to create IPA connection\n");
return -1;
}
mgr->calib.recon_timer.cb = bts_recon_timer_cb;
mgr->calib.recon_timer.data = mgr;
schedule_bts_connect(mgr);
return 0;
}

View File

@@ -375,6 +375,8 @@ DEFUN(cfg_no_action_slave_off, cfg_no_action_slave_off_cmd,
DEFUN(show_mgr, show_mgr_cmd, "show manager",
SHOW_STR "Display information about the manager")
{
vty_out(vty, "BTS Control Interface: %s%s",
s_mgr->calib.is_up ? "connected" : "disconnected", VTY_NEWLINE);
vty_out(vty, "Temperature control state: %s%s",
sysmobts_mgr_temp_get_state(s_mgr->state), VTY_NEWLINE);
vty_out(vty, "Current Temperatures%s", VTY_NEWLINE);
@@ -422,6 +424,18 @@ DEFUN(show_mgr, show_mgr_cmd, "show manager",
return CMD_SUCCESS;
}
DEFUN(calibrate_trx, calibrate_trx_cmd,
"trx 0 calibrate-clock",
"Transceiver commands\n" "Transceiver 0\n"
"Calibrate clock against GPS PPS\n")
{
if (sysmobts_mgr_calib_run(s_mgr) < 0) {
vty_out(vty, "%%Failed to start calibration.%s", VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
static void register_limit(int limit)
{
install_element(limit, &cfg_thresh_warning_cmd);
@@ -462,6 +476,8 @@ int sysmobts_mgr_vty_init(void)
install_element_ve(&show_mgr_cmd);
install_element(ENABLE_NODE, &calibrate_trx_cmd);
install_node(&mgr_node, config_write_mgr);
install_element(CONFIG_NODE, &cfg_mgr_cmd);
vty_install_default(MGR_NODE);

View File

@@ -37,11 +37,27 @@
#include <osmo-bts/bts.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/handover.h>
#include <osmo-bts/l1sap.h>
#include "l1_if.h"
#include "femtobts.h"
#include "utils.h"
static int mph_info_chan_confirm(struct gsm_lchan *lchan,
enum osmo_mph_info_type type, uint8_t cause)
{
struct osmo_phsap_prim l1sap;
memset(&l1sap, 0, sizeof(l1sap));
osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_CONFIRM,
NULL);
l1sap.u.info.type = type;
l1sap.u.info.u.act_cnf.chan_nr = gsm_lchan2chan_nr(lchan);
l1sap.u.info.u.act_cnf.cause = cause;
return l1sap_up(lchan->ts->trx, &l1sap);
}
enum sapi_cmd_type {
SAPI_CMD_ACTIVATE,
SAPI_CMD_CONFIG_CIPHERING,
@@ -213,8 +229,10 @@ static int opstart_compl(struct gsm_abis_mo *mo, struct msgb *l1_msg)
DEBUGP(DL1C, "====> trying to activate lchans of BCCH\n");
mo->bts->c0->ts[0].lchan[4].rel_act_kind = LCHAN_REL_ACT_OML;
lchan_activate(&mo->bts->c0->ts[0].lchan[4]);
if (cbch)
if (cbch) {
cbch->rel_act_kind = LCHAN_REL_ACT_OML;
lchan_activate(cbch);
}
}
/* Send OPSTART ack */
@@ -306,6 +324,7 @@ static const uint8_t trx_rqd_attr[] = { NM_ATT_RF_MAXPOWR_R };
static int trx_init(struct gsm_bts_trx *trx)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
struct gsm_bts_role_bts *btsb = bts_role_bts(trx->bts);
struct msgb *msg;
GsmL1_MphInitReq_t *mi_req;
GsmL1_DeviceParam_t *dev_par;
@@ -333,7 +352,8 @@ static int trx_init(struct gsm_bts_trx *trx)
dev_par->u16Arfcn = trx->arfcn;
dev_par->u16BcchArfcn = trx->bts->c0->arfcn;
dev_par->u8NbTsc = trx->bts->bsic & 7;
dev_par->fRxPowerLevel = fl1h->ul_power_target;
dev_par->fRxPowerLevel = trx_ms_pwr_ctrl_is_osmo(trx)
? 0.0 : btsb->ul_power_target;
dev_par->fTxPowerLevel = 0.0;
LOGP(DL1C, LOGL_NOTICE, "Init TRX (ARFCN %u, TSC %u, RxPower % 2f dBm, "
@@ -798,6 +818,9 @@ static void set_payload_format(GsmL1_LogChParam_t *lch_par)
static void lchan2lch_par(GsmL1_LogChParam_t *lch_par, struct gsm_lchan *lchan)
{
struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr;
struct gsm48_multi_rate_conf *mr_conf =
(struct gsm48_multi_rate_conf *) amr_mrc->gsm48_ie;
int j;
LOGP(DL1C, LOGL_INFO, "%s: %s tch_mode=0x%02x\n",
@@ -837,41 +860,41 @@ static void lchan2lch_par(GsmL1_LogChParam_t *lch_par, struct gsm_lchan *lchan)
lch_par->tch.amrActiveCodecSet[j] = GsmL1_AmrCodec_Unset;
j = 0;
if (lchan->mr_conf.m4_75)
if (mr_conf->m4_75)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_4_75;
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
break;
if (lchan->mr_conf.m5_15)
if (mr_conf->m5_15)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_5_15;
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
break;
if (lchan->mr_conf.m5_90)
if (mr_conf->m5_90)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_5_9;
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
break;
if (lchan->mr_conf.m6_70)
if (mr_conf->m6_70)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_6_7;
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
break;
if (lchan->mr_conf.m7_40)
if (mr_conf->m7_40)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_7_4;
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
break;
if (lchan->mr_conf.m7_95)
if (mr_conf->m7_95)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_7_95;
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
break;
if (lchan->mr_conf.m10_2)
if (mr_conf->m10_2)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_10_2;
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
break;
if (lchan->mr_conf.m12_2)
if (mr_conf->m12_2)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_12_2;
break;
case GSM48_CMODE_DATA_14k5:
@@ -920,12 +943,16 @@ static int mph_send_activate_req(struct gsm_lchan *lchan, struct sapi_cmd *cmd)
case GsmL1_Sapi_Prach:
lch_par->prach.u8Bsic = lchan->ts->trx->bts->bsic;
break;
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
case GsmL1_Sapi_Sacch:
/*
* TODO: For the SACCH we need to set the u8MsPowerLevel when
* doing manual MS power control. */
* For the SACCH we need to set the u8MsPowerLevel when
* doing manual MS power control.
*/
if (trx_ms_pwr_ctrl_is_osmo(lchan->ts->trx))
lch_par->sacch.u8MsPowerLevel = lchan->ms_power_ctrl.current;
/* fall through */
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
/*
* Be sure that every packet is received, even if it
* fails. In this case the length might be lower or 0.
@@ -967,7 +994,7 @@ static int sapi_activate_cb(struct gsm_lchan *lchan, int status)
gsm_lchan_name(lchan), status);
lchan_set_state(lchan, LCHAN_S_BROKEN);
sapi_clear_queue(&lchan->sapi_cmds);
rsl_tx_chan_act_nack(lchan, RSL_ERR_PROCESSOR_OVERLOAD);
mph_info_chan_confirm(lchan, PRIM_INFO_ACTIVATE, RSL_ERR_PROCESSOR_OVERLOAD);
return -1;
}
@@ -977,14 +1004,16 @@ static int sapi_activate_cb(struct gsm_lchan *lchan, int status)
if (lchan->state != LCHAN_S_ACT_REQ)
return 0;
struct gsm_time *time;
lchan_set_state(lchan, LCHAN_S_ACTIVE);
time = bts_model_get_time(lchan->ts->trx->bts);
rsl_tx_chan_act_ack(lchan, time);
mph_info_chan_confirm(lchan, PRIM_INFO_ACTIVATE, 0);
/* set the initial ciphering parameters for both directions */
l1if_set_ciphering(fl1h, lchan, 0);
l1if_set_ciphering(fl1h, lchan, 1);
l1if_set_ciphering(fl1h, lchan, 0);
if (lchan->encr.alg_id)
lchan->ciph_state = LCHAN_CIPH_RXTX_REQ;
else
lchan->ciph_state = LCHAN_CIPH_NONE;
return 0;
}
@@ -1002,7 +1031,6 @@ static void enqueue_sapi_act_cmd(struct gsm_lchan *lchan, int sapi, int dir)
int lchan_activate(struct gsm_lchan *lchan)
{
struct gsm_bts_role_bts *btsb = lchan->ts->trx->bts->role;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
const struct lchan_sapis *s4l = &sapis_for_lchan[lchan->type];
unsigned int i;
@@ -1036,8 +1064,6 @@ int lchan_activate(struct gsm_lchan *lchan)
#warning "FIXME: Should this be in sapi_activate_cb?"
lchan_init_lapdm(lchan);
lchan->s = btsb->radio_link_timeout;
return 0;
}
@@ -1136,9 +1162,16 @@ static int chmod_modif_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
LOGPC(DL1C, LOGL_INFO, "RX_REQ -> RX_CONF\n");
lchan->ciph_state = LCHAN_CIPH_RX_CONF;
break;
case LCHAN_CIPH_TXRX_REQ:
LOGPC(DL1C, LOGL_INFO, "TX_REQ -> TX_CONF\n");
lchan->ciph_state = LCHAN_CIPH_TXRX_CONF;
case LCHAN_CIPH_RX_CONF_TX_REQ:
LOGPC(DL1C, LOGL_INFO, "RX_CONF_TX_REQ -> RXTX_CONF\n");
lchan->ciph_state = LCHAN_CIPH_RXTX_CONF;
break;
case LCHAN_CIPH_RXTX_REQ:
LOGPC(DL1C, LOGL_INFO, "RXTX_REQ -> RX_CONF_TX_REQ\n");
lchan->ciph_state = LCHAN_CIPH_RX_CONF_TX_REQ;
break;
case LCHAN_CIPH_NONE:
LOGPC(DL1C, LOGL_INFO, "\n");
break;
default:
LOGPC(DL1C, LOGL_INFO, "unhandled state %u\n", lchan->ciph_state);
@@ -1167,7 +1200,8 @@ err:
static int mph_send_config_logchpar(struct gsm_lchan *lchan, struct sapi_cmd *cmd)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
struct gsm_bts_trx *trx = lchan->ts->trx;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
struct msgb *msg = l1p_msgb_alloc();
GsmL1_MphConfigReq_t *conf_req;
GsmL1_LogChParam_t *lch_par;
@@ -1177,7 +1211,7 @@ static int mph_send_config_logchpar(struct gsm_lchan *lchan, struct sapi_cmd *cm
/* update multi-rate config */
conf_req = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphConfigReq, fl1h);
conf_req->cfgParamId = GsmL1_ConfigParamId_SetLogChParams;
conf_req->cfgParams.setLogChParams.sapi = lchan_to_GsmL1_Sapi_t(lchan);
conf_req->cfgParams.setLogChParams.sapi = cmd->sapi;
conf_req->cfgParams.setLogChParams.u8Tn = lchan->ts->nr;
conf_req->cfgParams.setLogChParams.subCh = lchan_to_GsmL1_SubCh_t(lchan);
conf_req->cfgParams.setLogChParams.dir = cmd->dir;
@@ -1186,6 +1220,10 @@ static int mph_send_config_logchpar(struct gsm_lchan *lchan, struct sapi_cmd *cm
lch_par = &conf_req->cfgParams.setLogChParams.logChParams;
lchan2lch_par(lch_par, lchan);
/* Update the MS Power Level */
if (cmd->sapi == GsmL1_Sapi_Sacch && trx_ms_pwr_ctrl_is_osmo(trx))
lch_par->sacch.u8MsPowerLevel = lchan->ms_power_ctrl.current;
/* FIXME: update encryption */
LOGP(DL1C, LOGL_INFO, "%s MPH-CONFIG.req (%s) ",
@@ -1203,19 +1241,19 @@ static int mph_send_config_logchpar(struct gsm_lchan *lchan, struct sapi_cmd *cm
return l1if_gsm_req_compl(fl1h, msg, chmod_modif_compl_cb, NULL);
}
static void enqueue_sapi_logchpar_cmd(struct gsm_lchan *lchan, int dir)
static void enqueue_sapi_logchpar_cmd(struct gsm_lchan *lchan, int dir, GsmL1_Sapi_t sapi)
{
struct sapi_cmd *cmd = talloc_zero(lchan->ts->trx, struct sapi_cmd);
cmd->dir = dir;
cmd->sapi = sapi;
cmd->type = SAPI_CMD_CONFIG_LOGCH_PARAM;
queue_sapi_command(lchan, cmd);
}
static int tx_confreq_logchpar(struct gsm_lchan *lchan, uint8_t direction)
{
enqueue_sapi_logchpar_cmd(lchan, direction);
enqueue_sapi_logchpar_cmd(lchan, direction, lchan_to_GsmL1_Sapi_t(lchan));
return 0;
}
@@ -1298,7 +1336,16 @@ int l1if_set_ciphering(struct femtol1_hdl *fl1h,
return 0;
}
int bts_model_rsl_mode_modify(struct gsm_lchan *lchan)
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan)
{
if (lchan->state != LCHAN_S_ACTIVE)
return -1;
enqueue_sapi_logchpar_cmd(lchan, GsmL1_Dir_RxUplink, GsmL1_Sapi_Sacch);
return 0;
}
int l1if_rsl_mode_modify(struct gsm_lchan *lchan)
{
if (lchan->state != LCHAN_S_ACTIVE)
return -1;
@@ -1408,7 +1455,7 @@ static int sapi_deactivate_cb(struct gsm_lchan *lchan, int status)
gsm_lchan_name(lchan));
lchan_set_state(lchan, LCHAN_S_BROKEN);
sapi_clear_queue(&lchan->sapi_cmds);
rsl_tx_rf_rel_ack(lchan);
mph_info_chan_confirm(lchan, PRIM_INFO_DEACTIVATE, 0);
return -1;
}
@@ -1420,7 +1467,7 @@ static int sapi_deactivate_cb(struct gsm_lchan *lchan, int status)
return 0;
lchan_set_state(lchan, LCHAN_S_NONE);
rsl_tx_rf_rel_ack(lchan);
mph_info_chan_confirm(lchan, PRIM_INFO_DEACTIVATE, 0);
return 0;
}
@@ -1495,7 +1542,7 @@ static int lchan_deactivate_sapis(struct gsm_lchan *lchan)
LOGP(DL1C, LOGL_ERROR, "%s all SAPIs already released?\n",
gsm_lchan_name(lchan));
lchan_set_state(lchan, LCHAN_S_BROKEN);
rsl_tx_rf_rel_ack(lchan);
mph_info_chan_confirm(lchan, PRIM_INFO_DEACTIVATE, 0);
}
return res;
@@ -1535,13 +1582,6 @@ static int lchan_deactivate_sacch(struct gsm_lchan *lchan)
return 0;
}
struct gsm_time *bts_model_get_time(struct gsm_bts *bts)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(bts->c0);
return &fl1h->gsm_time;
}
/* callback from OML */
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
struct tlv_parsed *old_attr, struct tlv_parsed *new_attr,
@@ -1665,30 +1705,11 @@ int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
return oml_mo_statechg_nack(mo, NM_NACK_REQ_NOT_GRANT);
}
int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp)
int l1if_rsl_chan_act(struct gsm_lchan *lchan)
{
//uint8_t mode = *TLVP_VAL(tp, RSL_IE_CHAN_MODE);
//uint8_t type = *TLVP_VAL(tp, RSL_IE_ACT_TYPE);
struct gsm48_chan_desc *cd;
/* osmo-pcu calls this without a valid 'tp' parameter, so we
* need to make sure we don't crash here */
if (tp && TLVP_PRESENT(tp, GSM48_IE_CHANDESC_2) &&
TLVP_LEN(tp, GSM48_IE_CHANDESC_2) >= sizeof(*cd)) {
cd = (struct gsm48_chan_desc *)
TLVP_VAL(tp, GSM48_IE_CHANDESC_2);
/* our L1 only supports one global TSC for all channels
* one one TRX, so we need to make sure not to activate
* channels with a different TSC!! */
if (cd->h0.tsc != (lchan->ts->trx->bts->bsic & 7)) {
LOGP(DRSL, LOGL_ERROR, "lchan TSC %u != BSIC-TSC %u\n",
cd->h0.tsc, lchan->ts->trx->bts->bsic & 7);
return -RSL_ERR_SERV_OPT_UNIMPL;
}
}
lchan->sacch_deact = 0;
lchan_activate(lchan);
return 0;
}
@@ -1697,7 +1718,7 @@ int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp)
* Modify the given lchan in the handover scenario. This is a lot like
* second channel activation but with some additional activation.
*/
int bts_model_rsl_chan_mod(struct gsm_lchan *lchan)
int l1if_rsl_chan_mod(struct gsm_lchan *lchan)
{
const struct lchan_sapis *s4l = &sapis_for_lchan[lchan->type];
unsigned int i;
@@ -1721,7 +1742,7 @@ int bts_model_rsl_chan_mod(struct gsm_lchan *lchan)
return 0;
}
int bts_model_rsl_chan_rel(struct gsm_lchan *lchan)
int l1if_rsl_chan_rel(struct gsm_lchan *lchan)
{
/* A duplicate RF Release Request, ignore it */
if (lchan->state == LCHAN_S_REL_REQ) {
@@ -1734,7 +1755,7 @@ int bts_model_rsl_chan_rel(struct gsm_lchan *lchan)
return 0;
}
int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan)
int l1if_rsl_deact_sacch(struct gsm_lchan *lchan)
{
/* Only de-activate the SACCH if the lchan is active */
if (lchan->state != LCHAN_S_ACTIVE)

View File

@@ -121,6 +121,13 @@ static int ctrl_set_clkinfo_cb(struct gsm_bts_trx *trx, struct msgb *resp,
return 0;
}
static int clock_setup_cb(struct gsm_bts_trx *trx, struct msgb *resp,
void *data)
{
msgb_free(resp);
return 0;
}
static int set_clock_info(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts_trx *trx = cmd->node;
@@ -132,6 +139,16 @@ static int set_clock_info(struct ctrl_cmd *cmd, void *data)
/* geneate a deferred control command */
cd = ctrl_cmd_def_make(fl1h, cmd, NULL, 10);
/* Set GPS/PPS as reference */
sysp->id = SuperFemto_PrimId_RfClockSetupReq;
sysp->u.rfClockSetupReq.rfTrx.iClkCor = fl1h->clk_cal; /* !!! use get_clk_cal */
sysp->u.rfClockSetupReq.rfTrx.clkSrc = fl1h->clk_src;
sysp->u.rfClockSetupReq.rfTrxClkCal.clkSrc = SuperFemto_ClkSrcId_GpsPps;
l1if_req_compl(fl1h, msg, clock_setup_cb, NULL);
/* Reset the error counters */
msg = sysp_msgb_alloc();
sysp = msgb_sysprim(msg);
sysp->id = SuperFemto_PrimId_RfClockInfoReq;
sysp->u.rfClockInfoReq.u8RstClkCal = 1;

View File

@@ -43,7 +43,6 @@
#include <osmo-bts/logging.h>
#include <osmo-bts/vty.h>
#include "misc/sysmobts_par.h"
#include "femtobts.h"
#include "l1_if.h"
#include "utils.h"
@@ -85,75 +84,6 @@ DEFUN(cfg_bts_no_auto_band, cfg_bts_no_auto_band_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_bts_unit_id, cfg_bts_unit_id_cmd,
"ipa unit-id eeprom",
"ip.access RSL commands\n"
"Set the Unit ID of this BTS\n"
"Use serial number as Unit ID\n")
{
int serial_nr, rc;
struct gsm_bts *bts = vty->index;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
rc = sysmobts_par_get_int(SYSMOBTS_PAR_SERNR, &serial_nr);
if (rc != 0) {
vty_out(vty, "Failed to read serial number%s", VTY_NEWLINE);
return CMD_WARNING;
}
if (serial_nr < 0) {
vty_out(vty, "Serial number(%d) not valid%s",
serial_nr, VTY_NEWLINE);
return CMD_WARNING;
}
btsb->unitid_use_eeprom = 1;
bts->ip_access.bts_id = 0;
bts->ip_access.site_id = serial_nr;
return CMD_SUCCESS;
}
DEFUN(cfg_bts_no_unit_id, cfg_bts_no_unit_id_cmd,
"no ipa unit-id eeprom",
NO_STR "ip.access RSL commands\n"
"Set the Unit ID of this BTS\n"
"Use serial number as Unit ID\n")
{
struct gsm_bts *bts = vty->index;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
btsb->unitid_use_eeprom = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_trx_gsmtap_sapi, cfg_trx_gsmtap_sapi_cmd,
"HIDDEN", "HIDDEN")
{
struct gsm_bts_trx *trx = vty->index;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
int sapi;
sapi = get_string_value(femtobts_l1sapi_names, argv[0]);
fl1h->gsmtap_sapi_mask |= (1 << sapi);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_no_gsmtap_sapi, cfg_trx_no_gsmtap_sapi_cmd,
"HIDDEN", "HIDDEN")
{
struct gsm_bts_trx *trx = vty->index;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
int sapi;
sapi = get_string_value(femtobts_l1sapi_names, argv[0]);
fl1h->gsmtap_sapi_mask &= ~(1 << sapi);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_clkcal_eeprom, cfg_trx_clkcal_eeprom_cmd,
"clock-calibration eeprom",
"Use the eeprom clock calibration value\n")
@@ -245,15 +175,15 @@ DEFUN(cfg_trx_cal_path, cfg_trx_cal_path_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_trx_ul_power_target, cfg_trx_ul_power_target_cmd,
DEFUN_DEPRECATED(cfg_trx_ul_power_target, cfg_trx_ul_power_target_cmd,
"uplink-power-target <-110-0>",
"Set the nominal target Rx Level for uplink power control loop\n"
"Obsolete alias for bts uplink-power-target\n"
"Target uplink Rx level in dBm\n")
{
struct gsm_bts_trx *trx = vty->index;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
struct gsm_bts_role_bts *btsb = bts_role_bts(trx->bts);
fl1h->ul_power_target = atoi(argv[0]);
btsb->ul_power_target = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -537,41 +467,11 @@ void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts)
if (btsb->auto_band)
vty_out(vty, " auto-band%s", VTY_NEWLINE);
if (btsb->unitid_use_eeprom)
vty_out(vty, " ipa unit-id eeprom%s", VTY_NEWLINE);
}
/* FIXME: move to libosmocore ? */
static char buf_casecnvt[256];
char *osmo_str_tolower(const char *in)
{
int len, i;
if (!in)
return NULL;
len = strlen(in);
if (len > sizeof(buf_casecnvt))
len = sizeof(buf_casecnvt);
for (i = 0; i < len; i++) {
buf_casecnvt[i] = tolower(in[i]);
if (in[i] == '\0')
break;
}
if (i < sizeof(buf_casecnvt))
buf_casecnvt[i] = '\0';
/* just to make sure we're always zero-terminated */
buf_casecnvt[sizeof(buf_casecnvt)-1] = '\0';
return buf_casecnvt;
}
void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
int i;
if (fl1h->clk_use_eeprom)
vty_out(vty, " clock-calibration eeprom%s", VTY_NEWLINE);
@@ -584,8 +484,6 @@ void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
vty_out(vty, " clock-source %s%s",
get_value_string(femtobts_clksrc_names, fl1h->clk_src),
VTY_NEWLINE);
vty_out(vty, " uplink-power-target %d%s", fl1h->ul_power_target,
VTY_NEWLINE);
vty_out(vty, " min-qual-rach %.0f%s", fl1h->min_qual_rach * 10.0f,
VTY_NEWLINE);
vty_out(vty, " min-qual-norm %.0f%s", fl1h->min_qual_norm * 10.0f,
@@ -593,14 +491,6 @@ void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
if (trx->nominal_power != sysmobts_get_nominal_power(trx))
vty_out(vty, " nominal-tx-power %d%s", trx->nominal_power,
VTY_NEWLINE);
for (i = 0; i < 32; i++) {
if (fl1h->gsmtap_sapi_mask & (1 << i)) {
const char *name = get_value_string(femtobts_l1sapi_names, i);
vty_out(vty, " gsmtap-sapi %s%s", osmo_str_tolower(name),
VTY_NEWLINE);
}
}
}
int bts_model_vty_init(struct gsm_bts *bts)
@@ -622,20 +512,6 @@ int bts_model_vty_init(struct gsm_bts *bts)
NO_STR TRX_STR DSP_TRACE_F_STR,
"\n", "", 0);
cfg_trx_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
"gsmtap-sapi (",
"|",")", VTY_DO_LOWER);
cfg_trx_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
"GSMTAP SAPI\n",
"\n", "", 0);
cfg_trx_no_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
"no gsmtap-sapi (",
"|",")", VTY_DO_LOWER);
cfg_trx_no_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
NO_STR "GSMTAP SAPI\n",
"\n", "", 0);
install_element_ve(&show_dsp_trace_f_cmd);
install_element_ve(&show_sys_info_cmd);
install_element_ve(&show_trx_clksrc_cmd);
@@ -652,17 +528,12 @@ int bts_model_vty_init(struct gsm_bts *bts)
install_element(BTS_NODE, &cfg_bts_auto_band_cmd);
install_element(BTS_NODE, &cfg_bts_no_auto_band_cmd);
install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
install_element(BTS_NODE, &cfg_bts_no_unit_id_cmd);
install_element(TRX_NODE, &cfg_trx_clkcal_cmd);
install_element(TRX_NODE, &cfg_trx_clkcal_eeprom_cmd);
install_element(TRX_NODE, &cfg_trx_clkcal_def_cmd);
install_element(TRX_NODE, &cfg_trx_clksrc_cmd);
install_element(TRX_NODE, &cfg_trx_cal_path_cmd);
install_element(TRX_NODE, &cfg_trx_gsmtap_sapi_cmd);
install_element(TRX_NODE, &cfg_trx_no_gsmtap_sapi_cmd);
install_element(TRX_NODE, &cfg_trx_ul_power_target_cmd);
install_element(TRX_NODE, &cfg_trx_min_qual_rach_cmd);
install_element(TRX_NODE, &cfg_trx_min_qual_norm_cmd);
install_element(TRX_NODE, &cfg_trx_nominal_power_cmd);

View File

@@ -40,6 +40,7 @@
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/measurement.h>
#include <osmo-bts/amr.h>
#include <osmo-bts/l1sap.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h>
@@ -425,7 +426,7 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
#define RTP_MSGB_ALLOC_SIZE 512
/*! \brief call-back function for incoming RTP
/*! \brief function for incoming RTP via TCH.req
* \param rs RTP Socket
* \param[in] rtp_pl buffer containing RTP payload
* \param[in] rtp_pl_len length of \a rtp_pl
@@ -437,34 +438,18 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
* yet, as things like the frame number, etc. are unknown at the time we
* pre-fill the primtive.
*/
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
unsigned int rtp_pl_len)
void l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
const uint8_t *rtp_pl, unsigned int rtp_pl_len)
{
struct gsm_lchan *lchan = rs->priv;
struct msgb *msg;
GsmL1_Prim_t *l1p;
GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param;
uint8_t *payload_type;
uint8_t *l1_payload;
int rc;
/* skip processing of incoming RTP frames if we are in loopback mode */
if (lchan->loopback)
return;
DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan),
osmo_hexdump(rtp_pl, rtp_pl_len));
msg = l1p_msgb_alloc();
if (!msg) {
LOGP(DRTP, LOGL_ERROR, "%s: Failed to allocate Rx payload.\n",
gsm_lchan_name(lchan));
return;
}
l1p = msgb_l1prim(msg);
data_req = &l1p->u.phDataReq;
msu_param = &data_req->msgUnitParam;
payload_type = &msu_param->u8Buffer[0];
l1_payload = &msu_param->u8Buffer[1];
payload_type = &data[0];
l1_payload = &data[1];
switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_V1:
@@ -499,38 +484,22 @@ void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
if (rc < 0) {
LOGP(DRTP, LOGL_ERROR, "%s unable to parse RTP payload\n",
gsm_lchan_name(lchan));
msgb_free(msg);
return;
}
msu_param->u8Size = rc + 1;
*len = rc + 1;
DEBUGP(DRTP, "%s RTP->L1: %s\n", gsm_lchan_name(lchan),
osmo_hexdump(data, *len));
}
/* make sure the number of entries in the dl_tch_queue is never
* more than 3 */
{
struct msgb *tmp;
int count = 0;
llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
count++;
DEBUGP(DL1C, "%s DL TCH queue length = %u\n",
gsm_lchan_name(lchan), count);
while (count >= 2) {
tmp = msgb_dequeue(&lchan->dl_tch_queue);
msgb_free(tmp);
count--;
}
}
/* enqueue msgb to be transmitted to L1 */
msgb_enqueue(&lchan->dl_tch_queue, msg);
static int is_recv_only(uint8_t speech_mode)
{
return (speech_mode & 0xF0) == (1 << 4);
}
/*! \brief receive a traffic L1 primitive for a given lchan */
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg)
{
GsmL1_Prim_t *l1p = msgb_l1prim(l1p_msg);
GsmL1_PhDataInd_t *data_ind = &l1p->u.phDataInd;
@@ -538,50 +507,18 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
uint8_t *payload = data_ind->msgUnitParam.u8Buffer + 1;
uint8_t payload_len;
struct msgb *rmsg = NULL;
struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)].lchan[l1sap_chan2ss(chan_nr)];
if (is_recv_only(lchan->abis_ip.speech_mode))
return -EAGAIN;
if (data_ind->msgUnitParam.u8Size < 1) {
LOGP(DL1C, LOGL_ERROR, "%s Rx Payload size 0\n",
gsm_lchan_name(lchan));
LOGP(DL1C, LOGL_ERROR, "chan_nr %d Rx Payload size 0\n",
chan_nr);
return -EINVAL;
}
payload_len = data_ind->msgUnitParam.u8Size - 1;
if (lchan->loopback) {
GsmL1_Prim_t *rl1p;
GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param;
struct msgb *tmp;
int count = 0;
/* generate a new msgb from the paylaod */
rmsg = l1p_msgb_alloc();
if (!rmsg)
return -ENOMEM;
rl1p = msgb_l1prim(rmsg);
data_req = &rl1p->u.phDataReq;
msu_param = &data_req->msgUnitParam;
memcpy(msu_param->u8Buffer,
data_ind->msgUnitParam.u8Buffer,
data_ind->msgUnitParam.u8Size);
msu_param->u8Size = data_ind->msgUnitParam.u8Size;
/* make sure the queue doesn't get too long */
llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
count++;
while (count >= 1) {
tmp = msgb_dequeue(&lchan->dl_tch_queue);
msgb_free(tmp);
count--;
}
msgb_enqueue(&lchan->dl_tch_queue, rmsg);
return 0;
}
switch (payload_type) {
case GsmL1_TchPlType_Fr:
#if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
@@ -625,11 +562,20 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
}
if (rmsg) {
/* hand rmsg to RTP code for transmission */
if (lchan->abis_ip.rtp_socket)
osmo_rtp_send_frame(lchan->abis_ip.rtp_socket,
rmsg->data, rmsg->len, 160);
msgb_free(rmsg);
struct osmo_phsap_prim *l1sap;
LOGP(DL1C, LOGL_DEBUG, "%s Rx -> RTP: %s\n",
gsm_lchan_name(lchan), osmo_hexdump(rmsg->data, rmsg->len));
/* add l1sap header */
rmsg->l2h = rmsg->data;
msgb_push(rmsg, sizeof(*l1sap));
rmsg->l1h = rmsg->data;
l1sap = msgb_l1sap_prim(rmsg);
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_INDICATION, rmsg);
l1sap->u.tch.chan_nr = chan_nr;
return l1sap_up(trx, l1sap);
}
return 0;

View File

@@ -0,0 +1,11 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
AM_CFLAGS = -Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS)
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) -lortp
EXTRA_DIST = trx_if.h l1_if.h scheduler.h gsm0503_parity.h gsm0503_conv.h gsm0503_interleaving.h gsm0503_mapping.h gsm0503_coding.h gsm0503_tables.h loops.h amr.h
bin_PROGRAMS = osmobts-trx
osmobts_trx_SOURCES = main.c trx_if.c l1_if.c scheduler.c trx_vty.c gsm0503_parity.c gsm0503_conv.c gsm0503_interleaving.c gsm0503_mapping.c gsm0503_coding.c gsm0503_tables.c loops.c amr.c
osmobts_trx_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)

81
src/osmo-bts-trx/amr.c Normal file
View File

@@ -0,0 +1,81 @@
/* AMR support for OsmoBTS-TRX */
/* (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 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
static int amr_len_by_ft[16] = {
12, 13, 15, 17, 19, 20, 26, 31,
0, 0, 0, 0, 0, 0, 0, 0
};
int amr_decompose_payload(uint8_t *payload, int payload_len, uint8_t *_cmr,
uint8_t *_ft, uint8_t *_bfi)
{
uint8_t cmr, f, ft, q;
if (payload_len < 2)
return -EINVAL;
cmr = payload[0] >> 4;
if (_cmr)
*_cmr = cmr;
f = payload[1] >> 7;
ft = (payload[1] >> 3) & 0xf;
if (_ft)
*_ft = ft;
q = (payload[1] >> 2) & 0x1;
if (_bfi)
*_bfi = !q;
if (f) {
fprintf(stderr, "%s: multiple payloads not supported\n",
__func__);
return -ENOTSUP;
}
if (payload_len - 2 < amr_len_by_ft[ft])
return -EINVAL;
return 2 + amr_len_by_ft[ft];
}
int amr_compose_payload(uint8_t *payload, uint8_t cmr, uint8_t ft, uint8_t bfi)
{
if (cmr >= 16)
return -EINVAL;
if (ft >= 16)
return -EINVAL;
payload[0] = cmr << 4;
payload[1] = (ft << 3) | ((!bfi) << 2); /* F = 0 */
return 2 + amr_len_by_ft[ft];
}

8
src/osmo-bts-trx/amr.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef _TRX_AMR_H
#define _TRX_AMR_H
int amr_decompose_payload(uint8_t *payload, int payload_len, uint8_t *_cmr,
uint8_t *_ft, uint8_t *_bfi);
int amr_compose_payload(uint8_t *payload, uint8_t cmr, uint8_t ft, uint8_t bfi);
#endif /* _TRX_AMR_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,53 @@
/* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _0503_CODING_H
#define _0503_CODING_H
int xcch_decode(uint8_t *l2_data, sbit_t *bursts,
int *n_errors, int *n_bits_total);
int xcch_encode(ubit_t *bursts, uint8_t *l2_data);
int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p,
int *n_errors, int *n_bits_total);
int pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len);
int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order,
int efr, int *n_errors, int *n_bits_total);
int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int net_order);
int tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd,
int *n_errors, int *n_bits_total);
int tch_hr_encode(ubit_t *bursts, uint8_t *tch_data, int len);
int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req,
uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr,
int *n_errors, int *n_bits_total);
int tch_afs_encode(ubit_t *bursts, uint8_t *tch_data, int len,
int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft,
uint8_t cmr);
int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd,
int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
uint8_t *cmr, int *n_errors, int *n_bits_total);
int tch_ahs_encode(ubit_t *bursts, uint8_t *tch_data, int len,
int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft,
uint8_t cmr);
int rach_decode(uint8_t *ra, sbit_t *burst, uint8_t bsic);
int rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic);
int sch_decode(uint8_t *sb_info, sbit_t *burst);
int sch_encode(ubit_t *burst, uint8_t *sb_info);
#endif /* _0503_CODING_H */

View File

@@ -0,0 +1,950 @@
#include <stdint.h>
#include <osmocom/core/conv.h>
#include "gsm0503_conv.h"
/*
* GSM convolutional coding
*
* G_0 = 1 + x^3 + x^4
* G_1 = 1 + x + x^3 + x^4
*/
static const uint8_t conv_xcch_next_output[][2] = {
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
{ 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
};
static const uint8_t conv_xcch_next_state[][2] = {
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
};
const struct osmo_conv_code gsm0503_conv_xcch = {
.N = 2,
.K = 5,
.len = 224,
.next_output = conv_xcch_next_output,
.next_state = conv_xcch_next_state,
};
const struct osmo_conv_code gsm0503_conv_cs2 = {
.N = 2,
.K = 5,
.len = 290,
.next_output = conv_xcch_next_output,
.next_state = conv_xcch_next_state,
};
const struct osmo_conv_code gsm0503_conv_cs3 = {
.N = 2,
.K = 5,
.len = 334,
.next_output = conv_xcch_next_output,
.next_state = conv_xcch_next_state,
};
const struct osmo_conv_code gsm0503_conv_rach = {
.N = 2,
.K = 5,
.len = 14,
.next_output = conv_xcch_next_output,
.next_state = conv_xcch_next_state,
};
const struct osmo_conv_code gsm0503_conv_sch = {
.N = 2,
.K = 5,
.len = 35,
.next_output = conv_xcch_next_output,
.next_state = conv_xcch_next_state,
};
const struct osmo_conv_code gsm0503_conv_tch_fr = {
.N = 2,
.K = 5,
.len = 185,
.next_output = conv_xcch_next_output,
.next_state = conv_xcch_next_state,
};
/*
* GSM HR convolutional coding
*
* G_0 = 1 + x^2 + x^3 + x^5 + x^6
* G_1 = 1 + x + x^4 + x^6
* G_3 = 1 + x + x^2 + x^3 + x^4 + x^6
*/
static const uint8_t conv_tch_hr_next_output[][2] = {
{ 0, 7 }, { 3, 4 }, { 5, 2 }, { 6, 1 },
{ 5, 2 }, { 6, 1 }, { 0, 7 }, { 3, 4 },
{ 3, 4 }, { 0, 7 }, { 6, 1 }, { 5, 2 },
{ 6, 1 }, { 5, 2 }, { 3, 4 }, { 0, 7 },
{ 4, 3 }, { 7, 0 }, { 1, 6 }, { 2, 5 },
{ 1, 6 }, { 2, 5 }, { 4, 3 }, { 7, 0 },
{ 7, 0 }, { 4, 3 }, { 2, 5 }, { 1, 6 },
{ 2, 5 }, { 1, 6 }, { 7, 0 }, { 4, 3 },
{ 7, 0 }, { 4, 3 }, { 2, 5 }, { 1, 6 },
{ 2, 5 }, { 1, 6 }, { 7, 0 }, { 4, 3 },
{ 4, 3 }, { 7, 0 }, { 1, 6 }, { 2, 5 },
{ 1, 6 }, { 2, 5 }, { 4, 3 }, { 7, 0 },
{ 3, 4 }, { 0, 7 }, { 6, 1 }, { 5, 2 },
{ 6, 1 }, { 5, 2 }, { 3, 4 }, { 0, 7 },
{ 0, 7 }, { 3, 4 }, { 5, 2 }, { 6, 1 },
{ 5, 2 }, { 6, 1 }, { 0, 7 }, { 3, 4 },
};
static const uint8_t conv_tch_hr_next_state[][2] = {
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
{ 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 },
{ 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 },
{ 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 },
{ 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 },
{ 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 },
{ 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
{ 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 },
{ 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 },
{ 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 },
{ 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 },
{ 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 },
{ 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
};
static const int conv_tch_hr_puncture[] = {
/* Class 1 bits */
1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34,
37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70,
73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106,
109, 112, 115, 118, 121, 124, 127, 130, 133, 136, 139, 142,
145, 148, 151, 154, 157, 160, 163, 166, 169, 172, 175, 178,
181, 184, 187, 190, 193, 196, 199, 202, 205, 208, 211, 214,
217, 220, 223, 226, 229, 232, 235, 238, 241, 244, 247, 250,
253, 256, 259, 262, 265, 268, 271, 274, 277, 280, 283,
/* Tail bits */
295, 298, 301, 304, 307, 310,
/* End */
-1,
};
const struct osmo_conv_code gsm0503_conv_tch_hr = {
.N = 3,
.K = 7,
.len = 98,
.next_output = conv_tch_hr_next_output,
.next_state = conv_tch_hr_next_state,
.puncture = conv_tch_hr_puncture,
};
/* TCH/AFS12.2 */
/* ----------- */
static const uint8_t conv_tch_afs_12_2_next_output[][2] = {
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
};
static const uint8_t conv_tch_afs_12_2_next_state[][2] = {
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
{ 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
{ 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
};
static const uint8_t conv_tch_afs_12_2_next_term_output[] = {
0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1,
};
static const uint8_t conv_tch_afs_12_2_next_term_state[] = {
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
};
static int conv_tch_afs_12_2_puncture[] = {
321, 325, 329, 333, 337, 341, 345, 349, 353, 357, 361, 363,
365, 369, 373, 377, 379, 381, 385, 389, 393, 395, 397, 401,
405, 409, 411, 413, 417, 421, 425, 427, 429, 433, 437, 441,
443, 445, 449, 453, 457, 459, 461, 465, 469, 473, 475, 477,
481, 485, 489, 491, 493, 495, 497, 499, 501, 503, 505, 507,
-1, /* end */
};
const struct osmo_conv_code gsm0503_conv_tch_afs_12_2 = {
.N = 2,
.K = 5,
.len = 250,
.next_output = conv_tch_afs_12_2_next_output,
.next_state = conv_tch_afs_12_2_next_state,
.next_term_output = conv_tch_afs_12_2_next_term_output,
.next_term_state = conv_tch_afs_12_2_next_term_state,
.puncture = conv_tch_afs_12_2_puncture,
};
/* TCH/AFS10.2 */
/* ----------- */
static const uint8_t conv_tch_afs_10_2_next_output[][2] = {
{ 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
{ 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
{ 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
{ 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
};
static const uint8_t conv_tch_afs_10_2_next_state[][2] = {
{ 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
{ 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
{ 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
{ 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
};
static const uint8_t conv_tch_afs_10_2_next_term_output[] = {
0, 5, 3, 6, 5, 0, 6, 3, 7, 2, 4, 1, 2, 7, 1, 4,
};
static const uint8_t conv_tch_afs_10_2_next_term_state[] = {
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
};
static int conv_tch_afs_10_2_puncture[] = {
1, 4, 7, 10, 16, 19, 22, 28, 31, 34, 40, 43,
46, 52, 55, 58, 64, 67, 70, 76, 79, 82, 88, 91,
94, 100, 103, 106, 112, 115, 118, 124, 127, 130, 136, 139,
142, 148, 151, 154, 160, 163, 166, 172, 175, 178, 184, 187,
190, 196, 199, 202, 208, 211, 214, 220, 223, 226, 232, 235,
238, 244, 247, 250, 256, 259, 262, 268, 271, 274, 280, 283,
286, 292, 295, 298, 304, 307, 310, 316, 319, 322, 325, 328,
331, 334, 337, 340, 343, 346, 349, 352, 355, 358, 361, 364,
367, 370, 373, 376, 379, 382, 385, 388, 391, 394, 397, 400,
403, 406, 409, 412, 415, 418, 421, 424, 427, 430, 433, 436,
439, 442, 445, 448, 451, 454, 457, 460, 463, 466, 469, 472,
475, 478, 481, 484, 487, 490, 493, 496, 499, 502, 505, 508,
511, 514, 517, 520, 523, 526, 529, 532, 535, 538, 541, 544,
547, 550, 553, 556, 559, 562, 565, 568, 571, 574, 577, 580,
583, 586, 589, 592, 595, 598, 601, 604, 607, 609, 610, 613,
616, 619, 621, 622, 625, 627, 628, 631, 633, 634, 636, 637,
639, 640,
-1, /* end */
};
const struct osmo_conv_code gsm0503_conv_tch_afs_10_2 = {
.N = 3,
.K = 5,
.len = 210,
.next_output = conv_tch_afs_10_2_next_output,
.next_state = conv_tch_afs_10_2_next_state,
.next_term_output = conv_tch_afs_10_2_next_term_output,
.next_term_state = conv_tch_afs_10_2_next_term_state,
.puncture = conv_tch_afs_10_2_puncture,
};
/* TCH/AFS7.95 */
/* ----------- */
static const uint8_t conv_tch_afs_7_95_next_output[][2] = {
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
};
static const uint8_t conv_tch_afs_7_95_next_state[][2] = {
{ 0, 1 }, { 2, 3 }, { 5, 4 }, { 7, 6 },
{ 9, 8 }, { 11, 10 }, { 12, 13 }, { 14, 15 },
{ 16, 17 }, { 18, 19 }, { 21, 20 }, { 23, 22 },
{ 25, 24 }, { 27, 26 }, { 28, 29 }, { 30, 31 },
{ 33, 32 }, { 35, 34 }, { 36, 37 }, { 38, 39 },
{ 40, 41 }, { 42, 43 }, { 45, 44 }, { 47, 46 },
{ 49, 48 }, { 51, 50 }, { 52, 53 }, { 54, 55 },
{ 56, 57 }, { 58, 59 }, { 61, 60 }, { 63, 62 },
{ 1, 0 }, { 3, 2 }, { 4, 5 }, { 6, 7 },
{ 8, 9 }, { 10, 11 }, { 13, 12 }, { 15, 14 },
{ 17, 16 }, { 19, 18 }, { 20, 21 }, { 22, 23 },
{ 24, 25 }, { 26, 27 }, { 29, 28 }, { 31, 30 },
{ 32, 33 }, { 34, 35 }, { 37, 36 }, { 39, 38 },
{ 41, 40 }, { 43, 42 }, { 44, 45 }, { 46, 47 },
{ 48, 49 }, { 50, 51 }, { 53, 52 }, { 55, 54 },
{ 57, 56 }, { 59, 58 }, { 60, 61 }, { 62, 63 },
};
static const uint8_t conv_tch_afs_7_95_next_term_output[] = {
0, 3, 5, 6, 5, 6, 0, 3, 3, 0, 6, 5, 6, 5, 3, 0,
4, 7, 1, 2, 1, 2, 4, 7, 7, 4, 2, 1, 2, 1, 7, 4,
7, 4, 2, 1, 2, 1, 7, 4, 4, 7, 1, 2, 1, 2, 4, 7,
3, 0, 6, 5, 6, 5, 3, 0, 0, 3, 5, 6, 5, 6, 0, 3,
};
static const uint8_t conv_tch_afs_7_95_next_term_state[] = {
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
};
static int conv_tch_afs_7_95_puncture[] = {
1, 2, 4, 5, 8, 22, 70, 118, 166, 214, 262, 310,
317, 319, 325, 332, 334, 341, 343, 349, 356, 358, 365, 367,
373, 380, 382, 385, 389, 391, 397, 404, 406, 409, 413, 415,
421, 428, 430, 433, 437, 439, 445, 452, 454, 457, 461, 463,
469, 476, 478, 481, 485, 487, 490, 493, 500, 502, 503, 505,
506, 508, 509, 511, 512,
-1, /* end */
};
const struct osmo_conv_code gsm0503_conv_tch_afs_7_95 = {
.N = 3,
.K = 7,
.len = 165,
.next_output = conv_tch_afs_7_95_next_output,
.next_state = conv_tch_afs_7_95_next_state,
.next_term_output = conv_tch_afs_7_95_next_term_output,
.next_term_state = conv_tch_afs_7_95_next_term_state,
.puncture = conv_tch_afs_7_95_puncture,
};
/* TCH/AFS7.4 */
/* ---------- */
static const uint8_t conv_tch_afs_7_4_next_output[][2] = {
{ 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
{ 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
{ 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
{ 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
};
static const uint8_t conv_tch_afs_7_4_next_state[][2] = {
{ 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
{ 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
{ 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
{ 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
};
static const uint8_t conv_tch_afs_7_4_next_term_output[] = {
0, 5, 3, 6, 5, 0, 6, 3, 7, 2, 4, 1, 2, 7, 1, 4,
};
static const uint8_t conv_tch_afs_7_4_next_term_state[] = {
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
};
static int conv_tch_afs_7_4_puncture[] = {
0, 355, 361, 367, 373, 379, 385, 391, 397, 403, 409, 415,
421, 427, 433, 439, 445, 451, 457, 460, 463, 466, 468, 469,
471, 472,
-1, /* end */
};
const struct osmo_conv_code gsm0503_conv_tch_afs_7_4 = {
.N = 3,
.K = 5,
.len = 154,
.next_output = conv_tch_afs_7_4_next_output,
.next_state = conv_tch_afs_7_4_next_state,
.next_term_output = conv_tch_afs_7_4_next_term_output,
.next_term_state = conv_tch_afs_7_4_next_term_state,
.puncture = conv_tch_afs_7_4_puncture,
};
/* TCH/AFS6.7 */
/* ---------- */
static const uint8_t conv_tch_afs_6_7_next_output[][2] = {
{ 0, 15 }, { 4, 11 }, { 8, 7 }, { 12, 3 },
{ 4, 11 }, { 0, 15 }, { 12, 3 }, { 8, 7 },
{ 0, 15 }, { 4, 11 }, { 8, 7 }, { 12, 3 },
{ 4, 11 }, { 0, 15 }, { 12, 3 }, { 8, 7 },
};
static const uint8_t conv_tch_afs_6_7_next_state[][2] = {
{ 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
{ 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
{ 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
{ 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
};
static int conv_tch_afs_6_7_puncture[] = {
1, 3, 7, 11, 15, 27, 39, 55, 67, 79, 95, 107,
119, 135, 147, 159, 175, 187, 199, 215, 227, 239, 255, 267,
279, 287, 291, 295, 299, 303, 307, 311, 315, 319, 323, 327,
331, 335, 339, 343, 347, 351, 355, 359, 363, 367, 369, 371,
375, 377, 379, 383, 385, 387, 391, 393, 395, 399, 401, 403,
407, 409, 411, 415, 417, 419, 423, 425, 427, 431, 433, 435,
439, 441, 443, 447, 449, 451, 455, 457, 459, 463, 465, 467,
471, 473, 475, 479, 481, 483, 487, 489, 491, 495, 497, 499,
503, 505, 507, 511, 513, 515, 519, 521, 523, 527, 529, 531,
535, 537, 539, 543, 545, 547, 549, 551, 553, 555, 557, 559,
561, 563, 565, 567, 569, 571, 573, 575,
-1, /* end */
};
static const uint8_t conv_tch_afs_6_7_next_term_output[] = {
0, 11, 7, 12, 11, 0, 12, 7, 15, 4, 8, 3, 4, 15, 3, 8,
};
static const uint8_t conv_tch_afs_6_7_next_term_state[] = {
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
};
const struct osmo_conv_code gsm0503_conv_tch_afs_6_7 = {
.N = 4,
.K = 5,
.len = 140,
.next_output = conv_tch_afs_6_7_next_output,
.next_state = conv_tch_afs_6_7_next_state,
.next_term_output = conv_tch_afs_6_7_next_term_output,
.next_term_state = conv_tch_afs_6_7_next_term_state,
.puncture = conv_tch_afs_6_7_puncture,
};
/* TCH/AFS5.9 */
/* ---------- */
static const uint8_t conv_tch_afs_5_9_next_output[][2] = {
{ 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 },
{ 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 },
{ 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 },
{ 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 },
{ 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 },
{ 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 },
{ 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 },
{ 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 },
{ 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 },
{ 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 },
{ 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 },
{ 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 },
{ 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 },
{ 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 },
{ 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 },
{ 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 },
};
static const uint8_t conv_tch_afs_5_9_next_state[][2] = {
{ 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
{ 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
{ 17, 16 }, { 18, 19 }, { 20, 21 }, { 23, 22 },
{ 24, 25 }, { 27, 26 }, { 29, 28 }, { 30, 31 },
{ 32, 33 }, { 35, 34 }, { 37, 36 }, { 38, 39 },
{ 41, 40 }, { 42, 43 }, { 44, 45 }, { 47, 46 },
{ 49, 48 }, { 50, 51 }, { 52, 53 }, { 55, 54 },
{ 56, 57 }, { 59, 58 }, { 61, 60 }, { 62, 63 },
{ 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
{ 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
{ 16, 17 }, { 19, 18 }, { 21, 20 }, { 22, 23 },
{ 25, 24 }, { 26, 27 }, { 28, 29 }, { 31, 30 },
{ 33, 32 }, { 34, 35 }, { 36, 37 }, { 39, 38 },
{ 40, 41 }, { 43, 42 }, { 45, 44 }, { 46, 47 },
{ 48, 49 }, { 51, 50 }, { 53, 52 }, { 54, 55 },
{ 57, 56 }, { 58, 59 }, { 60, 61 }, { 63, 62 },
};
static const uint8_t conv_tch_afs_5_9_next_term_output[] = {
0, 7, 11, 12, 11, 12, 0, 7, 7, 0, 12, 11, 12, 11, 7, 0,
8, 15, 3, 4, 3, 4, 8, 15, 15, 8, 4, 3, 4, 3, 15, 8,
15, 8, 4, 3, 4, 3, 15, 8, 8, 15, 3, 4, 3, 4, 8, 15,
7, 0, 12, 11, 12, 11, 7, 0, 0, 7, 11, 12, 11, 12, 0, 7,
};
static const uint8_t conv_tch_afs_5_9_next_term_state[] = {
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
};
static int conv_tch_afs_5_9_puncture[] = {
0, 1, 3, 5, 7, 11, 15, 31, 47, 63, 79, 95,
111, 127, 143, 159, 175, 191, 207, 223, 239, 255, 271, 287,
303, 319, 327, 331, 335, 343, 347, 351, 359, 363, 367, 375,
379, 383, 391, 395, 399, 407, 411, 415, 423, 427, 431, 439,
443, 447, 455, 459, 463, 467, 471, 475, 479, 483, 487, 491,
495, 499, 503, 507, 509, 511, 512, 513, 515, 516, 517, 519,
-1, /* end */
};
const struct osmo_conv_code gsm0503_conv_tch_afs_5_9 = {
.N = 4,
.K = 7,
.len = 124,
.next_output = conv_tch_afs_5_9_next_output,
.next_state = conv_tch_afs_5_9_next_state,
.next_term_output = conv_tch_afs_5_9_next_term_output,
.next_term_state = conv_tch_afs_5_9_next_term_state,
.puncture = conv_tch_afs_5_9_puncture,
};
/* TCH/AFS5.15 */
/* ----------- */
static const uint8_t conv_tch_afs_5_15_next_output[][2] = {
{ 0, 31 }, { 4, 27 }, { 24, 7 }, { 28, 3 },
{ 4, 27 }, { 0, 31 }, { 28, 3 }, { 24, 7 },
{ 0, 31 }, { 4, 27 }, { 24, 7 }, { 28, 3 },
{ 4, 27 }, { 0, 31 }, { 28, 3 }, { 24, 7 },
};
static const uint8_t conv_tch_afs_5_15_next_state[][2] = {
{ 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
{ 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
{ 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
{ 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
};
static const uint8_t conv_tch_afs_5_15_next_term_output[] = {
0, 27, 7, 28, 27, 0, 28, 7, 31, 4, 24, 3, 4, 31, 3, 24,
};
static const uint8_t conv_tch_afs_5_15_next_term_state[] = {
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
};
static int conv_tch_afs_5_15_puncture[] = {
0, 4, 5, 9, 10, 14, 15, 20, 25, 30, 35, 40,
50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160,
170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280,
290, 300, 310, 315, 320, 325, 330, 334, 335, 340, 344, 345,
350, 354, 355, 360, 364, 365, 370, 374, 375, 380, 384, 385,
390, 394, 395, 400, 404, 405, 410, 414, 415, 420, 424, 425,
430, 434, 435, 440, 444, 445, 450, 454, 455, 460, 464, 465,
470, 474, 475, 480, 484, 485, 490, 494, 495, 500, 504, 505,
510, 514, 515, 520, 524, 525, 529, 530, 534, 535, 539, 540,
544, 545, 549, 550, 554, 555, 559, 560, 564,
-1, /* end */
};
const struct osmo_conv_code gsm0503_conv_tch_afs_5_15 = {
.N = 5,
.K = 5,
.len = 109,
.next_output = conv_tch_afs_5_15_next_output,
.next_state = conv_tch_afs_5_15_next_state,
.next_term_output = conv_tch_afs_5_15_next_term_output,
.next_term_state = conv_tch_afs_5_15_next_term_state,
.puncture = conv_tch_afs_5_15_puncture,
};
/* TCH/AFS4.75 */
/* ----------- */
static const uint8_t conv_tch_afs_4_75_next_output[][2] = {
{ 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 },
{ 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 },
{ 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 },
{ 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 },
{ 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 },
{ 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 },
{ 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 },
{ 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 },
{ 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 },
{ 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 },
{ 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 },
{ 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 },
{ 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 },
{ 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 },
{ 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 },
{ 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 },
};
static const uint8_t conv_tch_afs_4_75_next_state[][2] = {
{ 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
{ 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
{ 17, 16 }, { 18, 19 }, { 20, 21 }, { 23, 22 },
{ 24, 25 }, { 27, 26 }, { 29, 28 }, { 30, 31 },
{ 32, 33 }, { 35, 34 }, { 37, 36 }, { 38, 39 },
{ 41, 40 }, { 42, 43 }, { 44, 45 }, { 47, 46 },
{ 49, 48 }, { 50, 51 }, { 52, 53 }, { 55, 54 },
{ 56, 57 }, { 59, 58 }, { 61, 60 }, { 62, 63 },
{ 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
{ 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
{ 16, 17 }, { 19, 18 }, { 21, 20 }, { 22, 23 },
{ 25, 24 }, { 26, 27 }, { 28, 29 }, { 31, 30 },
{ 33, 32 }, { 34, 35 }, { 36, 37 }, { 39, 38 },
{ 40, 41 }, { 43, 42 }, { 45, 44 }, { 46, 47 },
{ 48, 49 }, { 51, 50 }, { 53, 52 }, { 54, 55 },
{ 57, 56 }, { 58, 59 }, { 60, 61 }, { 63, 62 },
};
static const uint8_t conv_tch_afs_4_75_next_term_output[] = {
0, 7, 27, 28, 27, 28, 0, 7, 7, 0, 28, 27, 28, 27, 7, 0,
24, 31, 3, 4, 3, 4, 24, 31, 31, 24, 4, 3, 4, 3, 31, 24,
31, 24, 4, 3, 4, 3, 31, 24, 24, 31, 3, 4, 3, 4, 24, 31,
7, 0, 28, 27, 28, 27, 7, 0, 0, 7, 27, 28, 27, 28, 0, 7,
};
static const uint8_t conv_tch_afs_4_75_next_term_state[] = {
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
};
static int conv_tch_afs_4_75_puncture[] = {
0, 1, 2, 4, 5, 7, 9, 15, 25, 35, 45, 55,
65, 75, 85, 95, 105, 115, 125, 135, 145, 155, 165, 175,
185, 195, 205, 215, 225, 235, 245, 255, 265, 275, 285, 295,
305, 315, 325, 335, 345, 355, 365, 375, 385, 395, 400, 405,
410, 415, 420, 425, 430, 435, 440, 445, 450, 455, 459, 460,
465, 470, 475, 479, 480, 485, 490, 495, 499, 500, 505, 509,
510, 515, 517, 519, 520, 522, 524, 525, 526, 527, 529, 530,
531, 532, 534,
-1, /* end */
};
const struct osmo_conv_code gsm0503_conv_tch_afs_4_75 = {
.N = 5,
.K = 7,
.len = 101,
.next_output = conv_tch_afs_4_75_next_output,
.next_state = conv_tch_afs_4_75_next_state,
.next_term_output = conv_tch_afs_4_75_next_term_output,
.next_term_state = conv_tch_afs_4_75_next_term_state,
.puncture = conv_tch_afs_4_75_puncture,
};
/* TCH/AHS7.95 */
/* ----------- */
static const uint8_t conv_tch_ahs_7_95_next_output[][2] = {
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
};
static const uint8_t conv_tch_ahs_7_95_next_state[][2] = {
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
{ 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
{ 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
};
static const uint8_t conv_tch_ahs_7_95_next_term_output[] = {
0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1,
};
static const uint8_t conv_tch_ahs_7_95_next_term_state[] = {
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
};
static int conv_tch_ahs_7_95_puncture[] = {
1, 3, 5, 7, 11, 15, 19, 23, 27, 31, 35, 43,
47, 51, 55, 59, 63, 67, 71, 79, 83, 87, 91, 95,
99, 103, 107, 115, 119, 123, 127, 131, 135, 139, 143, 151,
155, 159, 163, 167, 171, 175, 177, 179, 183, 185, 187, 191,
193, 195, 197, 199, 203, 205, 207, 211, 213, 215, 219, 221,
223, 227, 229, 231, 233, 235, 239, 241, 243, 247, 249, 251,
255, 257, 259, 261, 263, 265,
-1, /* end */
};
const struct osmo_conv_code gsm0503_conv_tch_ahs_7_95 = {
.N = 2,
.K = 5,
.len = 129,
.next_output = conv_tch_ahs_7_95_next_output,
.next_state = conv_tch_ahs_7_95_next_state,
.next_term_output = conv_tch_ahs_7_95_next_term_output,
.next_term_state = conv_tch_ahs_7_95_next_term_state,
.puncture = conv_tch_ahs_7_95_puncture,
};
/* TCH/AHS7.4 */
/* ---------- */
static const uint8_t conv_tch_ahs_7_4_next_output[][2] = {
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
};
static const uint8_t conv_tch_ahs_7_4_next_state[][2] = {
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
{ 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
{ 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
};
static const uint8_t conv_tch_ahs_7_4_next_term_output[] = {
0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1,
};
static const uint8_t conv_tch_ahs_7_4_next_term_state[] = {
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
};
static int conv_tch_ahs_7_4_puncture[] = {
1, 3, 7, 11, 19, 23, 27, 35, 39, 43, 51, 55,
59, 67, 71, 75, 83, 87, 91, 99, 103, 107, 115, 119,
123, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171,
175, 179, 183, 187, 191, 195, 199, 203, 207, 211, 215, 219,
221, 223, 227, 229, 231, 235, 237, 239, 243, 245, 247, 251,
253, 255, 257, 259,
-1, /* end */
};
const struct osmo_conv_code gsm0503_conv_tch_ahs_7_4 = {
.N = 2,
.K = 5,
.len = 126,
.next_output = conv_tch_ahs_7_4_next_output,
.next_state = conv_tch_ahs_7_4_next_state,
.next_term_output = conv_tch_ahs_7_4_next_term_output,
.next_term_state = conv_tch_ahs_7_4_next_term_state,
.puncture = conv_tch_ahs_7_4_puncture,
};
/* TCH/AHS6.7 */
/* ---------- */
static const uint8_t conv_tch_ahs_6_7_next_output[][2] = {
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
};
static const uint8_t conv_tch_ahs_6_7_next_state[][2] = {
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
{ 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
{ 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
};
static const uint8_t conv_tch_ahs_6_7_next_term_output[] = {
0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1,
};
static const uint8_t conv_tch_ahs_6_7_next_term_state[] = {
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
};
static int conv_tch_ahs_6_7_puncture[] = {
1, 3, 9, 19, 29, 39, 49, 59, 69, 79, 89, 99,
109, 119, 129, 139, 149, 159, 167, 169, 177, 179, 187, 189,
197, 199, 203, 207, 209, 213, 217, 219, 223, 227, 229, 231,
233, 235, 237, 239,
-1, /* end */
};
const struct osmo_conv_code gsm0503_conv_tch_ahs_6_7 = {
.N = 2,
.K = 5,
.len = 116,
.next_output = conv_tch_ahs_6_7_next_output,
.next_state = conv_tch_ahs_6_7_next_state,
.next_term_output = conv_tch_ahs_6_7_next_term_output,
.next_term_state = conv_tch_ahs_6_7_next_term_state,
.puncture = conv_tch_ahs_6_7_puncture,
};
/* TCH/AHS5.9 */
/* ---------- */
static const uint8_t conv_tch_ahs_5_9_next_output[][2] = {
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
};
static const uint8_t conv_tch_ahs_5_9_next_state[][2] = {
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
{ 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
{ 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
};
static const uint8_t conv_tch_ahs_5_9_next_term_output[] = {
0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1,
};
static const uint8_t conv_tch_ahs_5_9_next_term_state[] = {
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
};
static int conv_tch_ahs_5_9_puncture[] = {
1, 15, 71, 127, 139, 151, 163, 175, 187, 195, 203, 211,
215, 219, 221, 223,
-1, /* end */
};
const struct osmo_conv_code gsm0503_conv_tch_ahs_5_9 = {
.N = 2,
.K = 5,
.len = 108,
.next_output = conv_tch_ahs_5_9_next_output,
.next_state = conv_tch_ahs_5_9_next_state,
.next_term_output = conv_tch_ahs_5_9_next_term_output,
.next_term_state = conv_tch_ahs_5_9_next_term_state,
.puncture = conv_tch_ahs_5_9_puncture,
};
/* TCH/AHS5.15 */
/* ----------- */
static const uint8_t conv_tch_ahs_5_15_next_output[][2] = {
{ 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
{ 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
{ 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
{ 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
};
static const uint8_t conv_tch_ahs_5_15_next_state[][2] = {
{ 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
{ 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
{ 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
{ 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
};
static const uint8_t conv_tch_ahs_5_15_next_term_output[] = {
0, 5, 3, 6, 5, 0, 6, 3, 7, 2, 4, 1, 2, 7, 1, 4,
};
static const uint8_t conv_tch_ahs_5_15_next_term_state[] = {
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
};
static int conv_tch_ahs_5_15_puncture[] = {
0, 1, 3, 4, 6, 9, 12, 15, 18, 21, 27, 33,
39, 45, 51, 54, 57, 63, 69, 75, 81, 87, 90, 93,
99, 105, 111, 117, 123, 126, 129, 135, 141, 147, 153, 159,
162, 165, 168, 171, 174, 177, 180, 183, 186, 189, 192, 195,
198, 201, 204, 207, 210, 213, 216, 219, 222, 225, 228, 231,
234, 237, 240, 243, 244, 246, 249, 252, 255, 256, 258, 261,
264, 267, 268, 270, 273, 276, 279, 280, 282, 285, 288, 289,
291, 294, 295, 297, 298, 300, 301,
-1, /* end */
};
const struct osmo_conv_code gsm0503_conv_tch_ahs_5_15 = {
.N = 3,
.K = 5,
.len = 97,
.next_output = conv_tch_ahs_5_15_next_output,
.next_state = conv_tch_ahs_5_15_next_state,
.next_term_output = conv_tch_ahs_5_15_next_term_output,
.next_term_state = conv_tch_ahs_5_15_next_term_state,
.puncture = conv_tch_ahs_5_15_puncture,
};
/* TCH/AHS4.75 */
/* ----------- */
static const uint8_t conv_tch_ahs_4_75_next_output[][2] = {
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
};
static const uint8_t conv_tch_ahs_4_75_next_state[][2] = {
{ 0, 1 }, { 2, 3 }, { 5, 4 }, { 7, 6 },
{ 9, 8 }, { 11, 10 }, { 12, 13 }, { 14, 15 },
{ 16, 17 }, { 18, 19 }, { 21, 20 }, { 23, 22 },
{ 25, 24 }, { 27, 26 }, { 28, 29 }, { 30, 31 },
{ 33, 32 }, { 35, 34 }, { 36, 37 }, { 38, 39 },
{ 40, 41 }, { 42, 43 }, { 45, 44 }, { 47, 46 },
{ 49, 48 }, { 51, 50 }, { 52, 53 }, { 54, 55 },
{ 56, 57 }, { 58, 59 }, { 61, 60 }, { 63, 62 },
{ 1, 0 }, { 3, 2 }, { 4, 5 }, { 6, 7 },
{ 8, 9 }, { 10, 11 }, { 13, 12 }, { 15, 14 },
{ 17, 16 }, { 19, 18 }, { 20, 21 }, { 22, 23 },
{ 24, 25 }, { 26, 27 }, { 29, 28 }, { 31, 30 },
{ 32, 33 }, { 34, 35 }, { 37, 36 }, { 39, 38 },
{ 41, 40 }, { 43, 42 }, { 44, 45 }, { 46, 47 },
{ 48, 49 }, { 50, 51 }, { 53, 52 }, { 55, 54 },
{ 57, 56 }, { 59, 58 }, { 60, 61 }, { 62, 63 },
};
static const uint8_t conv_tch_ahs_4_75_next_term_output[] = {
0, 3, 5, 6, 5, 6, 0, 3, 3, 0, 6, 5, 6, 5, 3, 0,
4, 7, 1, 2, 1, 2, 4, 7, 7, 4, 2, 1, 2, 1, 7, 4,
7, 4, 2, 1, 2, 1, 7, 4, 4, 7, 1, 2, 1, 2, 4, 7,
3, 0, 6, 5, 6, 5, 3, 0, 0, 3, 5, 6, 5, 6, 0, 3,
};
static const uint8_t conv_tch_ahs_4_75_next_term_state[] = {
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
};
static int conv_tch_ahs_4_75_puncture[] = {
1, 2, 4, 5, 7, 8, 10, 13, 16, 22, 28, 34,
40, 46, 52, 58, 64, 70, 76, 82, 88, 94, 100, 106,
112, 118, 124, 130, 136, 142, 148, 151, 154, 160, 163, 166,
172, 175, 178, 184, 187, 190, 196, 199, 202, 208, 211, 214,
220, 223, 226, 232, 235, 238, 241, 244, 247, 250, 253, 256,
259, 262, 265, 268, 271, 274, 275, 277, 278, 280, 281, 283,
284,
-1, /* end */
};
const struct osmo_conv_code gsm0503_conv_tch_ahs_4_75 = {
.N = 3,
.K = 7,
.len = 89,
.next_output = conv_tch_ahs_4_75_next_output,
.next_state = conv_tch_ahs_4_75_next_state,
.next_term_output = conv_tch_ahs_4_75_next_term_output,
.next_term_state = conv_tch_ahs_4_75_next_term_state,
.puncture = conv_tch_ahs_4_75_puncture,
};

View File

@@ -0,0 +1,26 @@
#ifndef _0503_CONV_H
#define _0503_CONV_H
extern const struct osmo_conv_code gsm0503_conv_xcch;
extern const struct osmo_conv_code gsm0503_conv_cs2;
extern const struct osmo_conv_code gsm0503_conv_cs3;
extern const struct osmo_conv_code gsm0503_conv_rach;
extern const struct osmo_conv_code gsm0503_conv_sch;
extern const struct osmo_conv_code gsm0503_conv_tch_fr;
extern const struct osmo_conv_code gsm0503_conv_tch_hr;
extern const struct osmo_conv_code gsm0503_conv_tch_afs_12_2;
extern const struct osmo_conv_code gsm0503_conv_tch_afs_10_2;
extern const struct osmo_conv_code gsm0503_conv_tch_afs_7_95;
extern const struct osmo_conv_code gsm0503_conv_tch_afs_7_4;
extern const struct osmo_conv_code gsm0503_conv_tch_afs_6_7;
extern const struct osmo_conv_code gsm0503_conv_tch_afs_5_9;
extern const struct osmo_conv_code gsm0503_conv_tch_afs_5_15;
extern const struct osmo_conv_code gsm0503_conv_tch_afs_4_75;
extern const struct osmo_conv_code gsm0503_conv_tch_ahs_7_95;
extern const struct osmo_conv_code gsm0503_conv_tch_ahs_7_4;
extern const struct osmo_conv_code gsm0503_conv_tch_ahs_6_7;
extern const struct osmo_conv_code gsm0503_conv_tch_ahs_5_9;
extern const struct osmo_conv_code gsm0503_conv_tch_ahs_5_15;
extern const struct osmo_conv_code gsm0503_conv_tch_ahs_4_75;
#endif /* _0503_CONV_H */

View File

@@ -0,0 +1,144 @@
#include <stdint.h>
#include <osmocom/core/bits.h>
#include "gsm0503_tables.h"
#include "gsm0503_interleaving.h"
/*
* GSM xCCH interleaving and burst mapping
*
* Interleaving:
*
* Given 456 coded input bits, form 4 blocks of 114 bits:
*
* i(B, j) = c(n, k) k = 0, ..., 455
* n = 0, ..., N, N + 1, ...
* B = B_0 + 4n + (k mod 4)
* j = 2(49k mod 57) + ((k mod 8) div 4)
*
* Mapping on Burst:
*
* e(B, j) = i(B, j)
* e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56
* e(B, 57) = h_l(B)
* e(B, 58) = h_n(B)
*
* Where hl(B) and hn(B) are bits in burst B indicating flags.
*/
void gsm0503_xcch_deinterleave(sbit_t *cB, sbit_t *iB)
{
int j, k, B;
for (k=0; k<456; k++) {
B = k & 3;
j = 2 * ((49 * k) % 57) + ((k & 7) >> 2);
cB[k] = iB[B * 114 + j];
}
}
void gsm0503_xcch_interleave(ubit_t *cB, ubit_t *iB)
{
int j, k, B;
for (k=0; k<456; k++) {
B = k & 3;
j = 2 * ((49 * k) % 57) + ((k & 7) >> 2);
iB[B * 114 + j] = cB[k];
}
}
/*
* GSM TCH FR/EFR/AFS interleaving and burst mapping
*
* Interleaving:
*
* Given 456 coded input bits, form 8 blocks of 114 bits,
* where even bits of the first 4 blocks and odd bits of the last 4 blocks
* are used:
*
* i(B, j) = c(n, k) k = 0, ..., 455
* n = 0, ..., N, N + 1, ...
* B = B_0 + 4n + (k mod 8)
* j = 2(49k mod 57) + ((k mod 8) div 4)
*
* Mapping on Burst:
*
* e(B, j) = i(B, j)
* e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56
* e(B, 57) = h_l(B)
* e(B, 58) = h_n(B)
*
* Where hl(B) and hn(B) are bits in burst B indicating flags.
*/
void gsm0503_tch_fr_deinterleave(sbit_t *cB, sbit_t *iB)
{
int j, k, B;
for (k=0; k<456; k++) {
B = k & 7;
j = 2 * ((49 * k) % 57) + ((k & 7) >> 2);
cB[k] = iB[B * 114 + j];
}
}
void gsm0503_tch_fr_interleave(ubit_t *cB, ubit_t *iB)
{
int j, k, B;
for (k=0; k<456; k++) {
B = k & 7;
j = 2 * ((49 * k) % 57) + ((k & 7) >> 2);
iB[B * 114 + j] = cB[k];
}
}
/*
* GSM TCH HR/AHS interleaving and burst mapping
*
* Interleaving:
*
* Given 288 coded input bits, form 4 blocks of 114 bits,
* where even bits of the first 2 blocks and odd bits of the last 2 blocks
* are used:
*
* i(B, j) = c(n, k) k = 0, ..., 227
* n = 0, ..., N, N + 1, ...
* B = B_0 + 2n + b
* j, b = table[k];
*
* Mapping on Burst:
*
* e(B, j) = i(B, j)
* e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56
* e(B, 57) = h_l(B)
* e(B, 58) = h_n(B)
*
* Where hl(B) and hn(B) are bits in burst B indicating flags.
*/
void gsm0503_tch_hr_deinterleave(sbit_t *cB, sbit_t *iB)
{
int j, k, B;
for (k=0; k<228; k++) {
B = gsm0503_tch_hr_interleaving[k][1];
j = gsm0503_tch_hr_interleaving[k][0];
cB[k] = iB[B * 114 + j];
}
}
void gsm0503_tch_hr_interleave(ubit_t *cB, ubit_t *iB)
{
int j, k, B;
for (k=0; k<228; k++) {
B = gsm0503_tch_hr_interleaving[k][1];
j = gsm0503_tch_hr_interleaving[k][0];
iB[B * 114 + j] = cB[k];
}
}

View File

@@ -0,0 +1,11 @@
#ifndef _0503_INTERLEAVING_H
#define _0503_INTERLEAVING_H
void gsm0503_xcch_deinterleave(sbit_t *cB, sbit_t *iB);
void gsm0503_xcch_interleave(ubit_t *cB, ubit_t *iB);
void gsm0503_tch_fr_deinterleave(sbit_t *cB, sbit_t *iB);
void gsm0503_tch_fr_interleave(ubit_t *cB, ubit_t *iB);
void gsm0503_tch_hr_deinterleave(sbit_t *cB, sbit_t *iB);
void gsm0503_tch_hr_interleave(ubit_t *cB, ubit_t *iB);
#endif /* _0503_INTERLEAVING_H */

View File

@@ -0,0 +1,72 @@
#include <stdint.h>
#include <string.h>
#include <osmocom/core/bits.h>
#include "gsm0503_mapping.h"
void gsm0503_xcch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *hl, sbit_t *hn)
{
memcpy(iB, eB, 57);
memcpy(iB+57, eB+59, 57);
if (hl)
*hl = eB[57];
if (hn)
*hn = eB[58];
}
void gsm0503_xcch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *hl,
const ubit_t *hn)
{
memcpy(eB, iB, 57);
memcpy(eB+59, iB+57, 57);
if (hl)
eB[57] = *hl;
if (hn)
eB[58] = *hn;
}
void gsm0503_tch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd)
{
int i;
/* brainfuck: only copy even or odd bits */
if (iB) {
for (i=odd; i<57; i+=2)
iB[i] = eB[i];
for (i=58-odd; i<114; i+=2)
iB[i] = eB[i+2];
}
if (h) {
if (!odd)
*h = eB[58];
else
*h = eB[57];
}
}
void gsm0503_tch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd)
{
int i;
/* brainfuck: only copy even or odd bits */
if (eB) {
for (i=odd; i<57; i+=2)
eB[i] = iB[i];
for (i=58-odd; i<114; i+=2)
eB[i+2] = iB[i];
}
if (h) {
if (!odd)
eB[58] = *h;
else
eB[57] = *h;
}
}

View File

@@ -0,0 +1,10 @@
#ifndef _0503_MAPPING_H
#define _0503_MAPPING_H
void gsm0503_xcch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *hl, sbit_t *hn);
void gsm0503_xcch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *hl,
const ubit_t *hn);
void gsm0503_tch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd);
void gsm0503_tch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd);
#endif /* _0503_INTERLEAVING_H */

View File

@@ -0,0 +1,103 @@
#include <stdint.h>
#include <osmocom/core/crcgen.h>
#include "gsm0503_parity.h"
/*
* GSM (SACCH) parity (FIRE code)
*
* g(x) = (x^23 + 1)(x^17 + x^3 + 1)
* = x^40 + x^26 + x^23 + x^17 + x^3 + a1
*/
const struct osmo_crc64gen_code gsm0503_fire_crc40 = {
.bits = 40,
.poly = 0x0004820009ULL,
.init = 0x0000000000ULL,
.remainder = 0xffffffffffULL,
};
/*
* GSM PDTCH CS-2, CS-3, CS-4 parity
*
* g(x) = x^16 + x^12 + x^5 + 1
*/
const struct osmo_crc16gen_code gsm0503_cs234_crc16 = {
.bits = 16,
.poly = 0x1021,
.init = 0x0000,
.remainder = 0xffff,
};
/*
* GSM RACH parity
*
* g(x) = x^6 + x^5 + x^3 + x^2 + x^1 + 1
*/
const struct osmo_crc8gen_code gsm0503_rach_crc6 = {
.bits = 6,
.poly = 0x2f,
.init = 0x00,
.remainder = 0x3f,
};
/*
* GSM SCH parity
*
* g(x) = x^10 + x^8 + x^6 + x^5 + x^4 + x^2 + 1
*/
const struct osmo_crc16gen_code gsm0503_sch_crc10 = {
.bits = 10,
.poly = 0x175,
.init = 0x000,
.remainder = 0x3ff,
};
/*
* GSM TCH FR/HR/EFR parity
*
* g(x) = x^3 + x + 1
*/
const struct osmo_crc8gen_code gsm0503_tch_fr_crc3 = {
.bits = 3,
.poly = 0x3,
.init = 0x0,
.remainder = 0x7,
};
/*
* GSM TCH EFR parity
*
* g(x) = x^8 + x^4 + x^3 + x^2 + 1
*/
const struct osmo_crc8gen_code gsm0503_tch_efr_crc8 = {
.bits = 8,
.poly = 0x1d,
.init = 0x00,
.remainder = 0x00,
};
/*
* GSM AMR parity
*
* g(x) = x^6 + x^5 + x^3 + x^2 + x^1 + 1
*/
const struct osmo_crc8gen_code gsm0503_amr_crc6 = {
.bits = 6,
.poly = 0x2f,
.init = 0x00,
.remainder = 0x3f,
};

View File

@@ -0,0 +1,12 @@
#ifndef _0503_PARITY_H
#define _0503_PARITY_H
const struct osmo_crc64gen_code gsm0503_fire_crc40;
const struct osmo_crc16gen_code gsm0503_cs234_crc16;
const struct osmo_crc8gen_code gsm0503_rach_crc6;
const struct osmo_crc16gen_code gsm0503_sch_crc10;
const struct osmo_crc8gen_code gsm0503_tch_fr_crc3;
const struct osmo_crc8gen_code gsm0503_tch_efr_crc8;
const struct osmo_crc8gen_code gsm0503_amr_crc6;
#endif /* _0503_PARITY_H */

View File

@@ -0,0 +1,193 @@
#include <stdint.h>
#include <osmocom/core/bits.h>
#include "gsm0503_tables.h"
const ubit_t gsm0503_pdtch_hl_hn_ubit[4][8] = {
{ 1,1, 1,1, 1,1, 1,1 },
{ 1,1, 0,0, 1,0, 0,0 },
{ 0,0, 1,0, 0,0, 0,1 },
{ 0,0, 0,1, 0,1, 1,0 },
};
const sbit_t gsm0503_pdtch_hl_hn_sbit[4][8] = {
{ -127,-127, -127,-127, -127,-127, -127,-127 },
{ -127,-127, 127, 127, -127, 127, 127, 127 },
{ 127, 127, -127, 127, 127, 127, 127,-127 },
{ 127, 127, 127,-127, 127,-127, -127, 127 },
};
const ubit_t gsm0503_usf2six[8][6] = {
{ 0,0,0, 0,0,0 },
{ 1,0,0, 1,0,1 },
{ 0,1,0, 1,1,0 },
{ 1,1,0, 0,1,1 },
{ 0,0,1, 0,1,1 },
{ 1,0,1, 1,1,0 },
{ 0,1,1, 1,0,1 },
{ 1,1,1, 0,0,0 },
};
const ubit_t gsm0503_usf2twelve_ubit[8][12] = {
{ 0,0,0, 0,0,0, 0,0,0, 0,0,0 },
{ 1,1,0, 1,0,0, 0,0,1, 0,1,1 },
{ 0,0,1, 1,0,1, 1,1,0, 1,1,0 },
{ 1,1,1, 0,0,1, 1,1,1, 1,0,1 },
{ 0,0,0, 0,1,1, 0,1,1, 1,0,1 },
{ 1,1,0, 1,1,1, 0,1,0, 1,1,0 },
{ 0,0,1, 1,1,0, 1,0,1, 0,1,1 },
{ 1,1,1, 0,1,0, 1,0,0, 0,0,0 },
};
const sbit_t gsm0503_usf2twelve_sbit[8][12] = {
{ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127 },
{ -127,-127, 127, -127, 127, 127, 127, 127,-127, 127,-127,-127 },
{ 127, 127,-127, -127, 127,-127, -127,-127, 127, -127,-127, 127 },
{ -127,-127,-127, 127, 127,-127, -127,-127,-127, -127, 127,-127 },
{ 127, 127, 127, 127,-127,-127, 127,-127,-127, -127, 127,-127 },
{ -127,-127, 127, -127,-127,-127, 127,-127, 127, -127,-127, 127 },
{ 127, 127,-127, -127,-127, 127, -127, 127,-127, 127,-127,-127 },
{ -127,-127,-127, 127,-127, 127, -127, 127, 127, 127, 127, 127 },
};
const uint8_t gsm0503_puncture_cs2[588] = {
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
0,0,0,1, 0,0,0,1, 0,0,0,1
};
const uint8_t gsm0503_puncture_cs3[676] = {
0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,0
};
/* this corresponds to the bit-lengths of the individual codec
* parameters as indicated in Table 1.1 of TS 06.10 */
const uint8_t gsm0503_gsm_fr_map[76] = {
6, 6, 5, 5, 4, 4, 3, 3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 2, 2, 6, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3
};
/* this table describes the 65 most importaint bits from EFR coded
* bits as indicated in TS 05.03 (3.1.1.1) */
const uint8_t gsm0503_gsm_efr_protected_bits[65] = {
39, 40, 41, 42, 43, 44, 48, 87, 45, 2,
3, 8, 10, 18, 19, 24, 46, 47,142,143,
144,145,146,147, 92, 93,195,196, 98,137,
148, 94,197,149,150, 95,198, 4, 5, 11,
12, 16, 9, 6, 7, 13, 17, 20, 96,199,
1, 14, 15, 21, 25, 26, 28,151,201,190,
240, 88,138,191,241
};
/* Encoded in-band data for speech frames */
const ubit_t gsm0503_afs_ic_ubit[4][8] = {
{ 0,0,0,0,0,0,0,0 },
{ 0,1,0,1,1,1,0,1 },
{ 1,0,1,1,1,0,1,0 },
{ 1,1,1,0,0,1,1,1 },
};
const sbit_t gsm0503_afs_ic_sbit[4][8] = {
{ 127, 127, 127, 127, 127, 127, 127, 127 },
{ 127,-127, 127,-127,-127,-127, 127,-127 },
{ -127, 127,-127,-127,-127, 127,-127, 127 },
{ -127,-127,-127, 127, 127,-127,-127,-127 },
};
const ubit_t gsm0503_ahs_ic_ubit[4][4] = {
{ 0,0,0,0 },
{ 1,0,0,1 },
{ 1,1,1,0 },
{ 0,1,1,1 },
};
const sbit_t gsm0503_ahs_ic_sbit[4][4] = {
{ 127, 127, 127, 127 },
{ -127, 127, 127,-127 },
{ -127,-127,-127, 127 },
{ 127,-127,-127,-127 },
};
const uint8_t gsm0503_tch_hr_interleaving[228][2] = {
{ 0 ,0 }, { 1 ,2 }, { 78 ,1 }, { 79 ,3 }, { 48 ,0 }, { 49 ,2 },
{ 54 ,1 }, { 55 ,3 }, { 24 ,0 }, { 25 ,2 }, { 30 ,1 }, { 31 ,3 },
{ 72 ,0 }, { 73 ,2 }, { 6 ,1 }, { 7 ,3 }, { 96 ,0 }, { 97 ,2 },
{ 12 ,0 }, { 13 ,2 }, { 102,1 }, { 103,3 }, { 60 ,0 }, { 61 ,2 },
{ 66 ,1 }, { 67 ,3 }, { 90 ,1 }, { 91 ,3 }, { 36 ,0 }, { 37 ,2 },
{ 42 ,1 }, { 43 ,3 }, { 18 ,1 }, { 19 ,3 }, { 84 ,0 }, { 85 ,2 },
{ 108,0 }, { 109,2 }, { 2 ,0 }, { 3 ,2 }, { 80 ,1 }, { 81 ,3 },
{ 50 ,0 }, { 51 ,2 }, { 56 ,1 }, { 57 ,3 }, { 26 ,0 }, { 27 ,2 },
{ 32 ,1 }, { 33 ,3 }, { 74 ,0 }, { 75 ,2 }, { 8 ,1 }, { 9 ,3 },
{ 98 ,0 }, { 99 ,2 }, { 14 ,0 }, { 15 ,2 }, { 104,1 }, { 105,3 },
{ 62 ,0 }, { 63 ,2 }, { 68 ,1 }, { 69 ,3 }, { 92 ,1 }, { 93 ,3 },
{ 38 ,0 }, { 39 ,2 }, { 44 ,1 }, { 45 ,3 }, { 20 ,1 }, { 21 ,3 },
{ 86 ,0 }, { 87 ,2 }, { 110,0 }, { 111,2 }, { 4 ,0 }, { 5 ,2 },
{ 82 ,1 }, { 83 ,3 }, { 52 ,0 }, { 53 ,2 }, { 58 ,1 }, { 59 ,3 },
{ 28 ,0 }, { 29 ,2 }, { 34 ,1 }, { 35 ,3 }, { 76 ,0 }, { 77 ,2 },
{ 10 ,1 }, { 12 ,3 }, { 100,0 }, { 101,2 }, { 16 ,0 }, { 17 ,2 },
{ 106,1 }, { 107,3 }, { 64 ,0 }, { 65 ,2 }, { 70 ,1 }, { 71 ,3 },
{ 94 ,1 }, { 95 ,3 }, { 40 ,0 }, { 41 ,2 }, { 46 ,1 }, { 47 ,3 },
{ 22 ,1 }, { 23 ,3 }, { 88 ,0 }, { 89 ,2 }, { 112,0 }, { 113,2 },
{ 6 ,0 }, { 7 ,2 }, { 84 ,1 }, { 85 ,3 }, { 54 ,0 }, { 55 ,2 },
{ 60 ,1 }, { 61 ,3 }, { 30 ,0 }, { 31 ,2 }, { 36 ,1 }, { 37 ,3 },
{ 78 ,0 }, { 79 ,2 }, { 12 ,1 }, { 13 ,3 }, { 102,0 }, { 103,2 },
{ 18 ,0 }, { 19 ,2 }, { 108,1 }, { 109,3 }, { 66 ,0 }, { 67 ,2 },
{ 72 ,1 }, { 73 ,3 }, { 96 ,1 }, { 97 ,3 }, { 42 ,0 }, { 43 ,2 },
{ 48 ,1 }, { 49 ,3 }, { 24 ,1 }, { 25 ,3 }, { 90 ,0 }, { 91 ,2 },
{ 0 ,1 }, { 1 ,3 }, { 8 ,0 }, { 9 ,2 }, { 86 ,1 }, { 87 ,3 },
{ 56 ,0 }, { 57 ,2 }, { 62 ,1 }, { 63 ,3 }, { 32 ,0 }, { 33 ,2 },
{ 38 ,1 }, { 39 ,3 }, { 80 ,0 }, { 81 ,2 }, { 14 ,1 }, { 15 ,3 },
{ 104,0 }, { 105,2 }, { 20 ,0 }, { 21 ,2 }, { 110,1 }, { 111,3 },
{ 68 ,0 }, { 69 ,2 }, { 74 ,1 }, { 75 ,3 }, { 98 ,1 }, { 99 ,3 },
{ 44 ,0 }, { 45 ,2 }, { 50 ,1 }, { 51 ,3 }, { 26 ,1 }, { 27 ,3 },
{ 92 ,0 }, { 93 ,2 }, { 2 ,1 }, { 3 ,3 }, { 10 ,0 }, { 11 ,2 },
{ 88 ,1 }, { 89 ,3 }, { 58 ,0 }, { 59 ,2 }, { 64 ,1 }, { 65 ,3 },
{ 34 ,0 }, { 35 ,2 }, { 40 ,1 }, { 41 ,3 }, { 82 ,0 }, { 83 ,2 },
{ 16 ,1 }, { 17 ,3 }, { 106,0 }, { 107,2 }, { 22 ,0 }, { 23 ,2 },
{ 112,1 }, { 113,3 }, { 70 ,0 }, { 71 ,2 }, { 76 ,1 }, { 77 ,3 },
{ 100,1 }, { 101,3 }, { 46 ,0 }, { 47 ,2 }, { 52 ,1 }, { 53 ,3 },
{ 28 ,1 }, { 29 ,3 }, { 94 ,0 }, { 95 ,2 }, { 4 ,1 }, { 5 ,3 },
};

View File

@@ -0,0 +1,19 @@
#ifndef _0503_TABLES_H
#define _0503_TABLES_H
extern const ubit_t gsm0503_pdtch_hl_hn_ubit[4][8];
extern const sbit_t gsm0503_pdtch_hl_hn_sbit[4][8];
extern const ubit_t gsm0503_usf2six[8][6];
extern const ubit_t gsm0503_usf2twelve_ubit[8][12];
extern const sbit_t gsm0503_usf2twelve_sbit[8][12];
extern const uint8_t gsm0503_puncture_cs2[588];
extern const uint8_t gsm0503_puncture_cs3[676];
extern const uint8_t gsm0503_gsm_fr_map[76];
extern const uint8_t gsm0503_gsm_efr_protected_bits[65];
extern const ubit_t gsm0503_afs_ic_ubit[4][8];
extern const sbit_t gsm0503_afs_ic_sbit[4][8];
extern const ubit_t gsm0503_ahs_ic_ubit[4][4];
extern const sbit_t gsm0503_ahs_ic_sbit[4][4];
extern const uint8_t gsm0503_tch_hr_interleaving[228][2];
#endif /* _0503_TABLES_H */

716
src/osmo-bts-trx/l1_if.c Normal file
View File

@@ -0,0 +1,716 @@
/*
* layer 1 primitive handling and interface
*
* Copyright (C) 2013 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2015 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
*
* 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 <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/bits.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/oml.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/l1sap.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/amr.h>
#include <osmo-bts/abis.h>
#include "l1_if.h"
#include "trx_if.h"
#include "scheduler.h"
static const uint8_t transceiver_chan_types[_GSM_PCHAN_MAX] = {
[GSM_PCHAN_NONE] = 8,
[GSM_PCHAN_CCCH] = 4,
[GSM_PCHAN_CCCH_SDCCH4] = 5,
[GSM_PCHAN_TCH_F] = 1,
[GSM_PCHAN_TCH_H] = 2,
[GSM_PCHAN_SDCCH8_SACCH8C] = 7,
[GSM_PCHAN_PDCH] = 13,
//[GSM_PCHAN_TCH_F_PDCH] = FIXME,
[GSM_PCHAN_UNKNOWN] = 0,
};
/*
* create/destroy trx l1 instance
*/
struct trx_l1h *l1if_open(struct gsm_bts_trx *trx)
{
struct trx_l1h *l1h;
int rc;
l1h = talloc_zero(tall_bts_ctx, struct trx_l1h);
if (!l1h)
return NULL;
l1h->trx = trx;
trx->role_bts.l1h = l1h;
trx_sched_init(l1h);
rc = trx_if_open(l1h);
if (rc < 0) {
LOGP(DL1C, LOGL_FATAL, "Cannot initialize scheduler\n");
goto err;
}
return l1h;
err:
l1if_close(l1h);
trx->role_bts.l1h = NULL;
return NULL;
}
void l1if_close(struct trx_l1h *l1h)
{
trx_if_close(l1h);
trx_sched_exit(l1h);
talloc_free(l1h);
}
void l1if_reset(struct trx_l1h *l1h)
{
}
static void check_transceiver_availability_trx(struct trx_l1h *l1h, int avail)
{
struct gsm_bts_trx *trx = l1h->trx;
uint8_t tn;
/* HACK, we should change state when we receive first clock from
* transceiver */
if (avail) {
/* signal availability */
oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OK);
oml_mo_tx_sw_act_rep(&trx->mo);
oml_mo_state_chg(&trx->bb_transc.mo, -1, NM_AVSTATE_OK);
oml_mo_tx_sw_act_rep(&trx->bb_transc.mo);
for (tn = 0; tn < 8; tn++)
oml_mo_state_chg(&trx->ts[tn].mo, NM_OPSTATE_DISABLED,
(l1h->config.slotmask & (1 << tn)) ?
NM_AVSTATE_DEPENDENCY :
NM_AVSTATE_NOT_INSTALLED);
} else {
oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED,
NM_AVSTATE_OFF_LINE);
oml_mo_state_chg(&trx->bb_transc.mo, NM_OPSTATE_DISABLED,
NM_AVSTATE_OFF_LINE);
for (tn = 0; tn < 8; tn++)
oml_mo_state_chg(&trx->ts[tn].mo, NM_OPSTATE_DISABLED,
NM_AVSTATE_OFF_LINE);
}
}
int check_transceiver_availability(struct gsm_bts *bts, int avail)
{
struct gsm_bts_trx *trx;
struct trx_l1h *l1h;
llist_for_each_entry(trx, &bts->trx_list, list) {
l1h = trx_l1h_hdl(trx);
check_transceiver_availability_trx(l1h, avail);
}
return 0;
}
/*
* transceiver provisioning
*/
int l1if_provision_transceiver_trx(struct trx_l1h *l1h)
{
uint8_t tn;
if (!transceiver_available)
return -EIO;
if (l1h->config.poweron
&& l1h->config.tsc_valid
&& l1h->config.bsic_valid
&& l1h->config.arfcn_valid) {
/* before power on */
if (l1h->config.arfcn_valid && !l1h->config.arfcn_sent) {
trx_if_cmd_rxtune(l1h, l1h->config.arfcn);
trx_if_cmd_txtune(l1h, l1h->config.arfcn);
l1h->config.arfcn_sent = 1;
}
if (l1h->config.tsc_valid && !l1h->config.tsc_sent) {
trx_if_cmd_settsc(l1h, l1h->config.tsc);
l1h->config.tsc_sent = 1;
}
if (l1h->config.bsic_valid && !l1h->config.bsic_sent) {
trx_if_cmd_setbsic(l1h, l1h->config.bsic);
l1h->config.bsic_sent = 1;
}
if (!l1h->config.poweron_sent) {
trx_if_cmd_poweron(l1h);
l1h->config.poweron_sent = 1;
}
/* after power on */
if (l1h->config.rxgain_valid && !l1h->config.rxgain_sent) {
trx_if_cmd_setrxgain(l1h, l1h->config.rxgain);
l1h->config.rxgain_sent = 1;
}
if (l1h->config.power_valid && !l1h->config.power_sent) {
trx_if_cmd_setpower(l1h, l1h->config.power);
l1h->config.power_sent = 1;
}
if (l1h->config.maxdly_valid && !l1h->config.maxdly_sent) {
trx_if_cmd_setmaxdly(l1h, l1h->config.maxdly);
l1h->config.maxdly_sent = 1;
}
for (tn = 0; tn < 8; tn++) {
if (l1h->config.slottype_valid[tn]
&& !l1h->config.slottype_sent[tn]) {
trx_if_cmd_setslot(l1h, tn,
l1h->config.slottype[tn]);
l1h->config.slottype_sent[tn] = 1;
}
}
return 0;
}
if (!l1h->config.poweron && !l1h->config.poweron_sent) {
trx_if_cmd_poweroff(l1h);
l1h->config.poweron_sent = 1;
l1h->config.rxgain_sent = 0;
l1h->config.power_sent = 0;
l1h->config.maxdly_sent = 0;
for (tn = 0; tn < 8; tn++)
l1h->config.slottype_sent[tn] = 0;
}
return 0;
}
int l1if_provision_transceiver(struct gsm_bts *bts)
{
struct gsm_bts_trx *trx;
struct trx_l1h *l1h;
uint8_t tn;
llist_for_each_entry(trx, &bts->trx_list, list) {
l1h = trx_l1h_hdl(trx);
l1h->config.arfcn_sent = 0;
l1h->config.tsc_sent = 0;
l1h->config.bsic_sent = 0;
l1h->config.poweron_sent = 0;
l1h->config.rxgain_sent = 0;
l1h->config.power_sent = 0;
l1h->config.maxdly_sent = 0;
for (tn = 0; tn < 8; tn++)
l1h->config.slottype_sent[tn] = 0;
l1if_provision_transceiver_trx(l1h);
}
return 0;
}
/*
* activation/configuration/deactivation of transceiver's TRX
*/
/* initialize the layer1 */
static int trx_init(struct gsm_bts_trx *trx)
{
struct trx_l1h *l1h = trx_l1h_hdl(trx);
/* power on transceiver, if not already */
if (!l1h->config.poweron) {
l1h->config.poweron = 1;
l1h->config.poweron_sent = 0;
l1if_provision_transceiver_trx(l1h);
}
if (trx == trx->bts->c0)
lchan_init_lapdm(&trx->ts[0].lchan[4]);
/* Set to Operational State: Enabled */
oml_mo_state_chg(&trx->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
/* Send OPSTART ack */
return oml_mo_opstart_ack(&trx->mo);
}
/* deactivate transceiver */
int bts_model_trx_close(struct gsm_bts_trx *trx)
{
struct trx_l1h *l1h = trx_l1h_hdl(trx);
enum gsm_phys_chan_config pchan = trx->ts[0].pchan;
/* close all logical channels and reset timeslots */
trx_sched_reset(l1h);
/* deactivate lchan for CCCH */
if (pchan == GSM_PCHAN_CCCH || pchan == GSM_PCHAN_CCCH_SDCCH4) {
lchan_set_state(&trx->ts[0].lchan[4], LCHAN_S_INACTIVE);
}
/* power off transceiver, if not already */
if (l1h->config.poweron) {
l1h->config.poweron = 0;
l1h->config.poweron_sent = 0;
l1if_provision_transceiver_trx(l1h);
}
/* Set to Operational State: Disabled */
check_transceiver_availability_trx(l1h, 0);
return 0;
}
/* on RSL failure, deactivate transceiver */
void bts_model_abis_close(struct gsm_bts *bts)
{
struct gsm_bts_trx *trx;
llist_for_each_entry(trx, &bts->trx_list, list)
bts_model_trx_close(trx);
}
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan)
{
/* we always implement the power control loop in osmo-bts software, as
* there is no automatism in the underlying osmo-trx */
return 0;
}
/* set bts attributes */
static uint8_t trx_set_bts(struct gsm_bts *bts, struct tlv_parsed *new_attr)
{
struct gsm_bts_trx *trx;
struct trx_l1h *l1h;
uint8_t bsic = bts->bsic;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
if (TLVP_PRESENT(new_attr, NM_ATT_CONN_FAIL_CRIT)) {
const uint8_t *val = TLVP_VAL(new_attr, NM_ATT_CONN_FAIL_CRIT);
btsb->radio_link_timeout = val[1];
}
llist_for_each_entry(trx, &bts->trx_list, list) {
l1h = trx_l1h_hdl(trx);
if (l1h->config.bsic != bsic || !l1h->config.bsic_valid) {
l1h->config.bsic = bsic;
l1h->config.bsic_valid = 1;
l1h->config.bsic_sent = 0;
l1if_provision_transceiver_trx(l1h);
}
}
check_transceiver_availability(bts, transceiver_available);
return 0;
}
/* set trx attributes */
static uint8_t trx_set_trx(struct gsm_bts_trx *trx)
{
struct trx_l1h *l1h = trx_l1h_hdl(trx);
uint16_t arfcn = trx->arfcn;
if (l1h->config.arfcn != arfcn || !l1h->config.arfcn_valid) {
l1h->config.arfcn = arfcn;
l1h->config.arfcn_valid = 1;
l1h->config.arfcn_sent = 0;
l1if_provision_transceiver_trx(l1h);
}
if (l1h->config.power_oml) {
l1h->config.power = trx->max_power_red;
l1h->config.power_valid = 1;
l1h->config.power_sent = 0;
l1if_provision_transceiver_trx(l1h);
}
return 0;
}
/* set ts attributes */
static uint8_t trx_set_ts(struct gsm_bts_trx_ts *ts)
{
struct trx_l1h *l1h = trx_l1h_hdl(ts->trx);
uint8_t tn = ts->nr;
uint16_t tsc = ts->tsc;
enum gsm_phys_chan_config pchan = ts->pchan;
uint8_t slottype;
int rc;
/* all TSC of all timeslots must be equal, because transceiver only
* supports one TSC per TRX */
if (l1h->config.tsc != tsc || !l1h->config.tsc_valid) {
l1h->config.tsc = tsc;
l1h->config.tsc_valid = 1;
l1h->config.tsc_sent = 0;
l1if_provision_transceiver_trx(l1h);
}
/* set physical channel */
rc = trx_sched_set_pchan(l1h, tn, pchan);
if (rc)
return NM_NACK_RES_NOTAVAIL;
/* activate lchan for CCCH */
if (pchan == GSM_PCHAN_CCCH || pchan == GSM_PCHAN_CCCH_SDCCH4) {
ts->lchan[4].rel_act_kind = LCHAN_REL_ACT_OML;
lchan_set_state(&ts->lchan[4], LCHAN_S_ACTIVE);
}
slottype = transceiver_chan_types[pchan];
if (l1h->config.slottype[tn] != slottype
|| !l1h->config.slottype_valid[tn]) {
l1h->config.slottype[tn] = slottype;
l1h->config.slottype_valid[tn] = 1;
l1h->config.slottype_sent[tn] = 0;
l1if_provision_transceiver_trx(l1h);
}
return 0;
}
/*
* primitive handling
*/
/* enable ciphering */
static int l1if_set_ciphering(struct trx_l1h *l1h, struct gsm_lchan *lchan,
uint8_t chan_nr, int downlink)
{
/* ciphering already enabled in both directions */
if (lchan->ciph_state == LCHAN_CIPH_RXTX_CONF)
return -EINVAL;
if (!downlink) {
/* set uplink */
trx_sched_set_cipher(l1h, chan_nr, 0, lchan->encr.alg_id - 1,
lchan->encr.key, lchan->encr.key_len);
lchan->ciph_state = LCHAN_CIPH_RX_CONF;
} else {
/* set downlink and also set uplink, if not already */
if (lchan->ciph_state != LCHAN_CIPH_RX_CONF) {
trx_sched_set_cipher(l1h, chan_nr, 0,
lchan->encr.alg_id - 1, lchan->encr.key,
lchan->encr.key_len);
}
trx_sched_set_cipher(l1h, chan_nr, 1, lchan->encr.alg_id - 1,
lchan->encr.key, lchan->encr.key_len);
lchan->ciph_state = LCHAN_CIPH_RXTX_CONF;
}
return 0;
}
static int mph_info_chan_confirm(struct trx_l1h *l1h, uint8_t chan_nr,
enum osmo_mph_info_type type, uint8_t cause)
{
struct osmo_phsap_prim l1sap;
memset(&l1sap, 0, sizeof(l1sap));
osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_CONFIRM,
NULL);
l1sap.u.info.type = type;
l1sap.u.info.u.act_cnf.chan_nr = chan_nr;
l1sap.u.info.u.act_cnf.cause = cause;
return l1sap_up(l1h->trx, &l1sap);
}
int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn)
{
struct osmo_phsap_prim l1sap;
memset(&l1sap, 0, sizeof(l1sap));
osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO,
PRIM_OP_INDICATION, NULL);
l1sap.u.info.type = PRIM_INFO_TIME;
l1sap.u.info.u.time_ind.fn = fn;
if (!bts->c0)
return -EINVAL;
return l1sap_up(bts->c0, &l1sap);
}
void l1if_fill_meas_res(struct osmo_phsap_prim *l1sap, uint8_t chan_nr, float ta,
float ber, float rssi)
{
memset(l1sap, 0, sizeof(*l1sap));
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_MPH_INFO,
PRIM_OP_INDICATION, NULL);
l1sap->u.info.type = PRIM_INFO_MEAS;
l1sap->u.info.u.meas_ind.chan_nr = chan_nr;
l1sap->u.info.u.meas_ind.ta_offs_qbits = (int16_t)(ta*4);
l1sap->u.info.u.meas_ind.ber10k = (unsigned int) (ber * 10000);
l1sap->u.info.u.meas_ind.inv_rssi = (uint8_t) (rssi * -1);
}
int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn, uint8_t chan_nr,
int n_errors, int n_bits_total, float rssi, float toa)
{
struct gsm_lchan *lchan = &trx->ts[tn].lchan[l1sap_chan2ss(chan_nr)];
struct osmo_phsap_prim l1sap;
/* 100% BER is n_bits_total is 0 */
float ber = n_bits_total==0 ? 1.0 : (float)n_errors / (float)n_bits_total;
LOGP(DMEAS, LOGL_DEBUG, "RX L1 frame %s fn=%u chan_nr=0x%02x MS pwr=%ddBm rssi=%.1f dBFS "
"ber=%.2f%% (%d/%d bits) L1_ta=%d rqd_ta=%d toa=%.2f\n",
gsm_lchan_name(lchan), fn, chan_nr, ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power),
rssi, ber*100, n_errors, n_bits_total, lchan->meas.l1_info[1], lchan->rqd_ta, toa);
l1if_fill_meas_res(&l1sap, chan_nr, lchan->rqd_ta + toa, ber, rssi);
return l1sap_up(trx, &l1sap);
}
/* primitive from common part */
int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
{
struct trx_l1h *l1h = trx_l1h_hdl(trx);
struct msgb *msg = l1sap->oph.msg;
uint8_t chan_nr;
uint8_t tn, ss;
int rc = 0;
struct gsm_lchan *lchan;
switch (OSMO_PRIM_HDR(&l1sap->oph)) {
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST):
if (!msg)
break;
/* put data into scheduler's queue */
return trx_sched_ph_data_req(l1h, l1sap);
case OSMO_PRIM(PRIM_TCH, PRIM_OP_REQUEST):
if (!msg)
break;
/* put data into scheduler's queue */
return trx_sched_tch_req(l1h, l1sap);
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST):
switch (l1sap->u.info.type) {
case PRIM_INFO_ACT_CIPH:
chan_nr = l1sap->u.info.u.ciph_req.chan_nr;
tn = L1SAP_CHAN2TS(chan_nr);
ss = l1sap_chan2ss(chan_nr);
lchan = &trx->ts[tn].lchan[ss];
if (l1sap->u.info.u.ciph_req.uplink)
l1if_set_ciphering(l1h, lchan, chan_nr, 0);
if (l1sap->u.info.u.ciph_req.downlink)
l1if_set_ciphering(l1h, lchan, chan_nr, 1);
break;
case PRIM_INFO_ACTIVATE:
case PRIM_INFO_DEACTIVATE:
case PRIM_INFO_MODIFY:
chan_nr = l1sap->u.info.u.act_req.chan_nr;
tn = L1SAP_CHAN2TS(chan_nr);
ss = l1sap_chan2ss(chan_nr);
lchan = &trx->ts[tn].lchan[ss];
if (l1sap->u.info.type == PRIM_INFO_ACTIVATE) {
if ((chan_nr & 0x80)) {
LOGP(DL1C, LOGL_ERROR, "Cannot activate"
" chan_nr 0x%02x\n", chan_nr);
break;
}
/* activate dedicated channel */
trx_sched_set_lchan(l1h, chan_nr, 0x00, 1);
/* activate associated channel */
trx_sched_set_lchan(l1h, chan_nr, 0x40, 1);
/* set mode */
trx_sched_set_mode(l1h, chan_nr,
lchan->rsl_cmode, lchan->tch_mode,
lchan->tch.amr_mr.num_modes,
lchan->tch.amr_mr.mode[0].mode,
lchan->tch.amr_mr.mode[1].mode,
lchan->tch.amr_mr.mode[2].mode,
lchan->tch.amr_mr.mode[3].mode,
amr_get_initial_mode(lchan),
(lchan->ho.active == 1));
/* init lapdm */
lchan_init_lapdm(lchan);
/* set lchan active */
lchan_set_state(lchan, LCHAN_S_ACTIVE);
/* set initial ciphering */
l1if_set_ciphering(l1h, lchan, chan_nr, 0);
l1if_set_ciphering(l1h, lchan, chan_nr, 1);
if (lchan->encr.alg_id)
lchan->ciph_state = LCHAN_CIPH_RXTX_CONF;
else
lchan->ciph_state = LCHAN_CIPH_NONE;
/* confirm */
mph_info_chan_confirm(l1h, chan_nr,
PRIM_INFO_ACTIVATE, 0);
break;
}
if (l1sap->u.info.type == PRIM_INFO_MODIFY) {
/* change mode */
trx_sched_set_mode(l1h, chan_nr,
lchan->rsl_cmode, lchan->tch_mode,
lchan->tch.amr_mr.num_modes,
lchan->tch.amr_mr.mode[0].mode,
lchan->tch.amr_mr.mode[1].mode,
lchan->tch.amr_mr.mode[2].mode,
lchan->tch.amr_mr.mode[3].mode,
amr_get_initial_mode(lchan),
0);
break;
}
if ((chan_nr & 0x80)) {
LOGP(DL1C, LOGL_ERROR, "Cannot deactivate "
"chan_nr 0x%02x\n", chan_nr);
break;
}
/* deactivate associated channel */
trx_sched_set_lchan(l1h, chan_nr, 0x40, 0);
if (!l1sap->u.info.u.act_req.sacch_only) {
/* set lchan inactive */
lchan_set_state(lchan, LCHAN_S_NONE);
/* deactivate dedicated channel */
trx_sched_set_lchan(l1h, chan_nr, 0x00, 0);
/* confirm only on dedicated channel */
mph_info_chan_confirm(l1h, chan_nr,
PRIM_INFO_DEACTIVATE, 0);
lchan->ciph_state = 0; /* FIXME: do this in common/\*.c */
}
break;
default:
LOGP(DL1C, LOGL_NOTICE, "unknown MPH-INFO.req %d\n",
l1sap->u.info.type);
rc = -EINVAL;
goto done;
}
break;
default:
LOGP(DL1C, LOGL_NOTICE, "unknown prim %d op %d\n",
l1sap->oph.primitive, l1sap->oph.operation);
rc = -EINVAL;
goto done;
}
done:
if (msg)
msgb_free(msg);
return rc;
}
/*
* oml handling
*/
/* callback from OML */
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
struct tlv_parsed *old_attr, struct tlv_parsed *new_attr,
void *obj)
{
/* FIXME: check if the attributes are valid */
return 0;
}
/* callback from OML */
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
int cause = 0;
switch (foh->msg_type) {
case NM_MT_SET_BTS_ATTR:
cause = trx_set_bts(obj, new_attr);
break;
case NM_MT_SET_RADIO_ATTR:
cause = trx_set_trx(obj);
break;
case NM_MT_SET_CHAN_ATTR:
cause = trx_set_ts(obj);
break;
}
return oml_fom_ack_nack(msg, cause);
}
/* callback from OML */
int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
void *obj)
{
int rc;
switch (mo->obj_class) {
case NM_OC_RADIO_CARRIER:
/* activate transceiver */
rc = trx_init(obj);
break;
case NM_OC_CHANNEL:
/* configure timeslot */
rc = 0; //ts_connect(obj);
/* Set to Operational State: Enabled */
oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
/* Send OPSTART ack */
rc = oml_mo_opstart_ack(mo);
break;
case NM_OC_BTS:
case NM_OC_SITE_MANAGER:
case NM_OC_BASEB_TRANSC:
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
rc = oml_mo_opstart_ack(mo);
break;
default:
rc = oml_mo_opstart_nack(mo, NM_NACK_OBJCLASS_NOTSUPP);
}
return rc;
}
int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
void *obj, uint8_t adm_state)
{
/* blindly accept all state changes */
mo->nm_state.administrative = adm_state;
return oml_mo_statechg_ack(mo);
}
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx)
{
return 0;
}
int bts_model_oml_estab(struct gsm_bts *bts)
{
return 0;
}

179
src/osmo-bts-trx/l1_if.h Normal file
View File

@@ -0,0 +1,179 @@
#ifndef L1_IF_H_TRX
#define L1_IF_H_TRX
/* These types define the different channels on a multiframe.
* Each channel has queues and can be activated individually.
*/
enum trx_chan_type {
TRXC_IDLE = 0,
TRXC_FCCH,
TRXC_SCH,
TRXC_BCCH,
TRXC_RACH,
TRXC_CCCH,
TRXC_TCHF,
TRXC_TCHH_0,
TRXC_TCHH_1,
TRXC_SDCCH4_0,
TRXC_SDCCH4_1,
TRXC_SDCCH4_2,
TRXC_SDCCH4_3,
TRXC_SDCCH8_0,
TRXC_SDCCH8_1,
TRXC_SDCCH8_2,
TRXC_SDCCH8_3,
TRXC_SDCCH8_4,
TRXC_SDCCH8_5,
TRXC_SDCCH8_6,
TRXC_SDCCH8_7,
TRXC_SACCHTF,
TRXC_SACCHTH_0,
TRXC_SACCHTH_1,
TRXC_SACCH4_0,
TRXC_SACCH4_1,
TRXC_SACCH4_2,
TRXC_SACCH4_3,
TRXC_SACCH8_0,
TRXC_SACCH8_1,
TRXC_SACCH8_2,
TRXC_SACCH8_3,
TRXC_SACCH8_4,
TRXC_SACCH8_5,
TRXC_SACCH8_6,
TRXC_SACCH8_7,
TRXC_PDTCH,
TRXC_PTCCH,
_TRX_CHAN_MAX
};
/* States each channel on a multiframe */
struct trx_chan_state {
/* scheduler */
uint8_t active; /* Channel is active */
ubit_t *dl_bursts; /* burst buffer for TX */
sbit_t *ul_bursts; /* burst buffer for RX */
uint32_t ul_first_fn; /* fn of first burst */
uint8_t ul_mask; /* mask of received bursts */
/* RSSI / TOA */
uint8_t rssi_num; /* number of RSSI values */
float rssi_sum; /* sum of RSSI values */
uint8_t toa_num; /* number of TOA values */
float toa_sum; /* sum of TOA values */
/* loss detection */
uint8_t lost; /* (SACCH) loss detection */
/* mode */
uint8_t rsl_cmode, tch_mode; /* mode for TCH channels */
/* AMR */
uint8_t codec[4]; /* 4 possible codecs for amr */
int codecs; /* number of possible codecs */
float ber_sum; /* sum of bit error rates */
int ber_num; /* number of bit error rates */
uint8_t ul_ft; /* current uplink FT index */
uint8_t dl_ft; /* current downlink FT index */
uint8_t ul_cmr; /* current uplink CMR index */
uint8_t dl_cmr; /* current downlink CMR index */
uint8_t amr_loop; /* if AMR loop is enabled */
/* TCH/H */
uint8_t dl_ongoing_facch; /* FACCH/H on downlink */
uint8_t ul_ongoing_facch; /* FACCH/H on uplink */
/* encryption */
int ul_encr_algo; /* A5/x encry algo downlink */
int dl_encr_algo; /* A5/x encry algo uplink */
int ul_encr_key_len;
int dl_encr_key_len;
uint8_t ul_encr_key[8];
uint8_t dl_encr_key[8];
/* measurements */
struct {
uint8_t clock; /* cyclic clock counter */
int8_t rssi[32]; /* last RSSI values */
int rssi_count; /* received RSSI values */
int rssi_valid_count; /* number of stored value */
int rssi_got_burst; /* any burst received so far */
float toa_sum; /* sum of TOA values */
int toa_num; /* number of TOA value */
} meas;
/* handover */
uint8_t ho_rach_detect; /* if rach detection is on */
};
struct trx_config {
uint8_t poweron; /* poweron(1) or poweroff(0) */
int poweron_sent;
int arfcn_valid;
uint16_t arfcn;
int arfcn_sent;
int tsc_valid;
uint8_t tsc;
int tsc_sent;
int bsic_valid;
uint8_t bsic;
int bsic_sent;
int rxgain_valid;
int rxgain;
int rxgain_sent;
int power_valid;
int power;
int power_oml;
int power_sent;
int maxdly_valid;
int maxdly;
int maxdly_sent;
uint8_t slotmask;
int slottype_valid[8];
uint8_t slottype[8];
int slottype_sent[8];
};
struct trx_l1h {
struct llist_head trx_ctrl_list;
struct gsm_bts_trx *trx;
struct osmo_fd trx_ofd_ctrl;
struct osmo_timer_list trx_ctrl_timer;
struct osmo_fd trx_ofd_data;
/* transceiver config */
struct trx_config config;
uint8_t mf_index[8]; /* selected multiframe index */
uint32_t mf_last_fn[8]; /* last received frame */
uint8_t mf_period[8]; /* period of multiframe */
struct trx_sched_frame *mf_frames[8]; /* pointer to frame layout */
/* Channel states for all channels on all timeslots */
struct trx_chan_state chan_states[8][_TRX_CHAN_MAX];
struct llist_head dl_prims[8]; /* Queue primitves for TX */
uint8_t ho_rach_detect[8][8];
};
struct trx_l1h *l1if_open(struct gsm_bts_trx *trx);
void l1if_close(struct trx_l1h *l1h);
void l1if_reset(struct trx_l1h *l1h);
int check_transceiver_availability(struct gsm_bts *bts, int avail);
int l1if_provision_transceiver_trx(struct trx_l1h *l1h);
int l1if_provision_transceiver(struct gsm_bts *bts);
int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn);
void l1if_fill_meas_res(struct osmo_phsap_prim *l1sap, uint8_t chan_nr, float ta,
float ber, float rssi);
int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn, uint8_t chan_nr,
int n_errors, int n_bits_total, float rssi, float toa);
#endif /* L1_IF_H_TRX */

339
src/osmo-bts-trx/loops.c Normal file
View File

@@ -0,0 +1,339 @@
/* Loop control for OsmoBTS-TRX */
/* (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 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 <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/l1sap.h>
#include <osmocom/core/bits.h>
#include "trx_if.h"
#include "l1_if.h"
#include "loops.h"
#define MS_PWR_DBM(lvl) ms_pwr_dbm(gsm_arfcn2band(l1h->config.arfcn), lvl)
/*
* MS Power loop
*/
int trx_ms_power_loop = 0;
int8_t trx_target_rssi = -10;
static int ms_power_diff(struct trx_l1h *l1h, struct gsm_lchan *lchan,
uint8_t chan_nr, struct trx_chan_state *chan_state, int8_t diff)
{
int8_t new_power;
new_power = lchan->ms_power - (diff >> 1);
if (diff == 0)
return 0;
if (new_power < 0)
new_power = 0;
// FIXME: to go above 1W, we need to know classmark of MS
if (l1h->config.arfcn >= 512 && l1h->config.arfcn <= 885) {
if (new_power > 15)
new_power = 15;
} else {
if (new_power > 19)
new_power = 19;
}
/* a higher value means a lower level (and vice versa) */
if (new_power > lchan->ms_power + MS_LOWER_MAX)
new_power = lchan->ms_power + MS_LOWER_MAX;
else if (new_power < lchan->ms_power - MS_RAISE_MAX)
new_power = lchan->ms_power - MS_RAISE_MAX;
if (lchan->ms_power == new_power) {
LOGP(DLOOP, LOGL_INFO, "Keeping MS new_power of trx=%u "
"chan_nr=0x%02x at control level %d (%d dBm)\n",
l1h->trx->nr, chan_nr, new_power,
MS_PWR_DBM(new_power));
return 0;
}
LOGP(DLOOP, LOGL_INFO, "%s MS new_power of trx=%u chan_nr=0x%02x from "
"control level %d (%d dBm) to %d (%d dBm)\n",
(diff > 0) ? "Raising" : "Lowering",
l1h->trx->nr, chan_nr, lchan->ms_power,
MS_PWR_DBM(lchan->ms_power), new_power, MS_PWR_DBM(new_power));
lchan->ms_power = new_power;
return 0;
}
static int ms_power_val(struct trx_chan_state *chan_state, int8_t rssi)
{
/* ignore inserted dummy frames, treat as lost frames */
if (rssi < -127)
return 0;
LOGP(DLOOP, LOGL_DEBUG, "Got RSSI value of %d\n", rssi);
chan_state->meas.rssi_count++;
chan_state->meas.rssi_got_burst = 1;
/* store and process RSSI */
if (chan_state->meas.rssi_valid_count
== ARRAY_SIZE(chan_state->meas.rssi))
return 0;
chan_state->meas.rssi[chan_state->meas.rssi_valid_count++] = rssi;
chan_state->meas.rssi_valid_count++;
return 0;
}
static int ms_power_clock(struct trx_l1h *l1h, struct gsm_lchan *lchan,
uint8_t chan_nr, struct trx_chan_state *chan_state)
{
int rssi;
int i;
/* skip every second clock, to prevent oscillating due to roundtrip
* delay */
if (!(chan_state->meas.clock & 1))
return 0;
LOGP(DLOOP, LOGL_DEBUG, "Got SACCH master clock at RSSI count %d\n",
chan_state->meas.rssi_count);
/* wait for initial burst */
if (!chan_state->meas.rssi_got_burst)
return 0;
/* if no burst was received from MS at clock */
if (chan_state->meas.rssi_count == 0) {
LOGP(DLOOP, LOGL_NOTICE, "LOST SACCH frame of trx=%u "
"chan_nr=0x%02x, so we raise MS power\n",
l1h->trx->nr, chan_nr);
return ms_power_diff(l1h, lchan, chan_nr, chan_state,
MS_RAISE_MAX);
}
/* reset total counter */
chan_state->meas.rssi_count = 0;
/* check the minimum level received after MS acknowledged the ordered
* power level */
if (chan_state->meas.rssi_valid_count == 0)
return 0;
for (rssi = 999, i = 0; i < chan_state->meas.rssi_valid_count; i++) {
if (rssi > chan_state->meas.rssi[i])
rssi = chan_state->meas.rssi[i];
}
/* reset valid counter */
chan_state->meas.rssi_valid_count = 0;
/* change RSSI */
LOGP(DLOOP, LOGL_DEBUG, "Lowest RSSI: %d Target RSSI: %d Current "
"MS power: %d (%d dBm) of trx=%u chan_nr=0x%02x\n", rssi,
trx_target_rssi, lchan->ms_power, MS_PWR_DBM(lchan->ms_power),
l1h->trx->nr, chan_nr);
ms_power_diff(l1h, lchan, chan_nr, chan_state, trx_target_rssi - rssi);
return 0;
}
/*
* Timing Advance loop
*/
int trx_ta_loop = 1;
int ta_val(struct trx_l1h *l1h, struct gsm_lchan *lchan, uint8_t chan_nr,
struct trx_chan_state *chan_state, float toa)
{
/* check if the current L1 header acks to the current ordered TA */
if (lchan->meas.l1_info[1] != lchan->rqd_ta)
return 0;
/* sum measurement */
chan_state->meas.toa_sum += toa;
if (++(chan_state->meas.toa_num) < 16)
return 0;
/* complete set */
toa = chan_state->meas.toa_sum / chan_state->meas.toa_num;
/* check for change of TOA */
if (toa < -0.9F && lchan->rqd_ta > 0) {
LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is too "
"early (%.2f), now lowering TA from %d to %d\n",
l1h->trx->nr, chan_nr, toa, lchan->rqd_ta,
lchan->rqd_ta - 1);
lchan->rqd_ta--;
} else if (toa > 0.9F && lchan->rqd_ta < 63) {
LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is too "
"late (%.2f), now raising TA from %d to %d\n",
l1h->trx->nr, chan_nr, toa, lchan->rqd_ta,
lchan->rqd_ta + 1);
lchan->rqd_ta++;
} else
LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is "
"correct (%.2f), keeping current TA of %d\n",
l1h->trx->nr, chan_nr, toa, lchan->rqd_ta);
chan_state->meas.toa_num = 0;
chan_state->meas.toa_sum = 0;
return 0;
}
int trx_loop_sacch_input(struct trx_l1h *l1h, uint8_t chan_nr,
struct trx_chan_state *chan_state, int8_t rssi, float toa)
{
struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)]
.lchan[l1sap_chan2ss(chan_nr)];
if (trx_ms_power_loop)
ms_power_val(chan_state, rssi);
if (trx_ta_loop)
ta_val(l1h, lchan, chan_nr, chan_state, toa);
return 0;
}
int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr,
struct trx_chan_state *chan_state)
{
struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)]
.lchan[l1sap_chan2ss(chan_nr)];
if (trx_ms_power_loop)
ms_power_clock(l1h, lchan, chan_nr, chan_state);
/* count the number of SACCH clocks */
chan_state->meas.clock++;
return 0;
}
int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
struct trx_chan_state *chan_state, float ber)
{
struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)]
.lchan[l1sap_chan2ss(chan_nr)];
int c_i;
/* check if loop is enabled */
if (!chan_state->amr_loop)
return 0;
/* wait for MS to use the requested codec */
if (chan_state->ul_ft != chan_state->dl_cmr)
return 0;
/* count bit errors */
if (L1SAP_IS_CHAN_TCHH(chan_nr)) {
chan_state->ber_num += 2;
chan_state->ber_sum += (ber + ber);
} else {
chan_state->ber_num++;
chan_state->ber_sum += ber;
}
/* count frames */
if (chan_state->ber_num < 48)
return 0;
/* calculate average (reuse ber variable) */
ber = chan_state->ber_sum / chan_state->ber_num;
/* FIXME: calculate C/I from BER */
c_i = ber * 100;
/* reset bit errors */
chan_state->ber_num = 0;
chan_state->ber_sum = 0;
LOGP(DLOOP, LOGL_DEBUG, "Current bit error rate (BER) %.6f "
"codec id %d of trx=%u chan_nr=0x%02x\n", ber,
chan_state->ul_ft, l1h->trx->nr, chan_nr);
/* degrade */
if (chan_state->dl_cmr > 0) {
/* degrade, if ber is above threshold FIXME: C/I */
if (ber >
lchan->tch.amr_mr.mode[chan_state->dl_cmr-1].threshold_bts) {
LOGP(DLOOP, LOGL_DEBUG, "Degrading due to BER %.6f "
"from codec id %d to %d of trx=%u "
"chan_nr=0x%02x\n", ber, chan_state->dl_cmr,
chan_state->dl_cmr - 1, l1h->trx->nr, chan_nr);
chan_state->dl_cmr--;
}
return 0;
}
/* upgrade */
if (chan_state->dl_cmr < chan_state->codecs - 1) {
/* degrade, if ber is above threshold FIXME: C/I*/
if (ber <
lchan->tch.amr_mr.mode[chan_state->dl_cmr].threshold_bts
- lchan->tch.amr_mr.mode[chan_state->dl_cmr].hysteresis_bts) {
LOGP(DLOOP, LOGL_DEBUG, "Upgrading due to BER %.6f "
"from codec id %d to %d of trx=%u "
"chan_nr=0x%02x\n", ber, chan_state->dl_cmr,
chan_state->dl_cmr + 1, l1h->trx->nr, chan_nr);
chan_state->dl_cmr++;
}
return 0;
}
return 0;
}
int trx_loop_amr_set(struct trx_chan_state *chan_state, int loop)
{
if (chan_state->amr_loop && !loop) {
chan_state->amr_loop = 0;
return 0;
}
if (!chan_state->amr_loop && loop) {
chan_state->amr_loop = 1;
/* reset bit errors */
chan_state->ber_num = 0;
chan_state->ber_sum = 0;
return 0;
}
return 0;
}

31
src/osmo-bts-trx/loops.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef _TRX_LOOPS_H
#define _TRX_LOOPS_H
/*
* calibration of loops
*/
/* how much power levels do we raise/lower as maximum (1 level = 2 dB) */
#define MS_RAISE_MAX 4
#define MS_LOWER_MAX 1
/*
* loops api
*/
extern int trx_ms_power_loop;
extern int8_t trx_target_rssi;
extern int trx_ta_loop;
int trx_loop_sacch_input(struct trx_l1h *l1h, uint8_t chan_nr,
struct trx_chan_state *chan_state, int8_t rssi, float toa);
int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr,
struct trx_chan_state *chan_state);
int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
struct trx_chan_state *chan_state, float ber);
int trx_loop_amr_set(struct trx_chan_state *chan_state, int loop);
#endif /* _TRX_LOOPS_H */

406
src/osmo-bts-trx/main.c Normal file
View File

@@ -0,0 +1,406 @@
/* Main program for OsmoBTS-TRX */
/* (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 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 <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <sched.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/application.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/bits.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/abis.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/vty.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/l1sap.h>
#include "l1_if.h"
#include "trx_if.h"
#include "scheduler.h"
const int pcu_direct = 0;
int quit = 0;
static const char *config_file = "osmo-bts.cfg";
static int daemonize = 0;
static char *gsmtap_ip = 0;
static int rt_prio = -1;
static int trx_num = 1;
char *software_version = "0.0";
uint8_t abis_mac[6] = { 0, 1, 2, 3, 4, 5 };
char *bsc_host = "localhost";
char *bts_id = "1801/0";
// FIXME this is a hack
static void get_mac(void)
{
struct if_nameindex *ifn = if_nameindex();
struct ifreq ifr;
int sock;
int ret;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
return;
memset(&ifr, 0, sizeof(ifr));
if (!ifn)
return;
while (ifn->if_name) {
strncpy(ifr.ifr_name, ifn->if_name, sizeof(ifr.ifr_name)-1);
ret = ioctl(sock, SIOCGIFHWADDR, &ifr);
if (ret == 0 && !!memcmp(ifr.ifr_hwaddr.sa_data,
"\0\0\0\0\0\0", 6)) {
memcpy(abis_mac, ifr.ifr_hwaddr.sa_data, 6);
printf("Using MAC address of %s: "
"'%02x:%02x:%02x:%02x:%02x:%02x'\n",
ifn->if_name,
abis_mac[0], abis_mac[1], abis_mac[2],
abis_mac[3], abis_mac[4], abis_mac[5]);
break;
}
ifn++;
}
// if_freenameindex(ifn);
}
int bts_model_init(struct gsm_bts *bts)
{
void *l1h;
struct gsm_bts_trx *trx;
llist_for_each_entry(trx, &bts->trx_list, list) {
l1h = l1if_open(trx);
if (!l1h) {
LOGP(DL1C, LOGL_FATAL, "Cannot open L1 Interface\n");
goto error;
}
trx->role_bts.l1h = l1h;
trx->nominal_power = 23;
l1if_reset(l1h);
}
bts_model_vty_init(bts);
return 0;
error:
llist_for_each_entry(trx, &bts->trx_list, list) {
l1h = trx->role_bts.l1h;
if (l1h)
l1if_close(l1h);
}
return -EIO;
}
/* dummy, since no direct dsp support */
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx)
{
return 0;
}
static void print_help()
{
printf( "Some useful options:\n"
" -h --help this text\n"
" -d --debug MASK Enable debugging (e.g. -d DRSL:DOML:DLAPDM)\n"
" -D --daemonize For the process into a background daemon\n"
" -c --config-file Specify the filename of the config file\n"
" -s --disable-color Don't use colors in stderr log output\n"
" -T --timestamp Prefix every log line with a timestamp\n"
" -V --version Print version information and exit\n"
" -e --log-level Set a global log-level\n"
" -t --trx-num Set number of TRX (default=%d)\n"
" -i --gsmtap-ip The destination IP used for GSMTAP.\n"
" -r --realtime PRIO Set realtime scheduler with given prio\n"
" -I --local-trx-ip Local IP for transceiver to connect (default=%s)\n"
,trx_num, transceiver_ip);
}
/* FIXME: finally get some option parsing code into libosmocore */
static void handle_options(int argc, char **argv)
{
while (1) {
int option_idx = 0, c;
static const struct option long_options[] = {
/* FIXME: all those are generic Osmocom app options */
{ "help", 0, 0, 'h' },
{ "debug", 1, 0, 'd' },
{ "daemonize", 0, 0, 'D' },
{ "config-file", 1, 0, 'c' },
{ "disable-color", 0, 0, 's' },
{ "timestamp", 0, 0, 'T' },
{ "version", 0, 0, 'V' },
{ "log-level", 1, 0, 'e' },
{ "trx-num", 1, 0, 't' },
{ "gsmtap-ip", 1, 0, 'i' },
{ "realtime", 1, 0, 'r' },
{ "local-trx-ip", 1, 0, 'I' },
{ 0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "hc:d:Dc:sTVe:t:i:r:I:",
long_options, &option_idx);
if (c == -1)
break;
switch (c) {
case 'h':
print_help();
exit(0);
break;
case 's':
log_set_use_color(osmo_stderr_target, 0);
break;
case 'd':
log_parse_category_mask(osmo_stderr_target, optarg);
break;
case 'D':
daemonize = 1;
break;
case 'c':
config_file = strdup(optarg);
break;
case 'T':
log_set_print_timestamp(osmo_stderr_target, 1);
break;
case 'V':
print_version(1);
exit(0);
break;
case 'e':
log_set_log_level(osmo_stderr_target, atoi(optarg));
break;
case 't':
trx_num = atoi(optarg);
if (trx_num < 1)
trx_num = 1;
break;
case 'i':
gsmtap_ip = optarg;
break;
case 'r':
rt_prio = atoi(optarg);
break;
case 'I':
transceiver_ip = strdup(optarg);
break;
default:
break;
}
}
}
static struct gsm_bts *bts;
static void signal_handler(int signal)
{
fprintf(stderr, "signal %u received\n", signal);
switch (signal) {
case SIGINT:
//osmo_signal_dispatch(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL);
if (!quit)
bts_shutdown(bts, "SIGINT");
quit++;
break;
case SIGABRT:
case SIGUSR1:
case SIGUSR2:
talloc_report_full(tall_bts_ctx, stderr);
break;
default:
break;
}
}
static int write_pid_file(char *procname)
{
FILE *outf;
char tmp[PATH_MAX+1];
snprintf(tmp, sizeof(tmp)-1, "/var/run/%s.pid", procname);
tmp[PATH_MAX-1] = '\0';
outf = fopen(tmp, "w");
if (!outf)
return -1;
fprintf(outf, "%d\n", getpid());
fclose(outf);
return 0;
}
int main(int argc, char **argv)
{
struct gsm_bts_role_bts *btsb;
struct gsm_bts_trx *trx;
struct e1inp_line *line;
void *tall_msgb_ctx;
int rc, i;
printf("((*))\n |\n / \\ OsmoBTS\n");
get_mac();
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
tall_msgb_ctx = talloc_named_const(tall_bts_ctx, 1, "msgb");
msgb_set_talloc_ctx(tall_msgb_ctx);
bts_log_init(NULL);
handle_options(argc, argv);
bts = gsm_bts_alloc(tall_bts_ctx);
if (!bts) {
fprintf(stderr, "Failed to create BTS structure\n");
exit(1);
}
for (i = 1; i < trx_num; i++) {
trx = gsm_bts_trx_alloc(bts);
if (!trx) {
fprintf(stderr, "Failed to TRX structure\n");
exit(1);
}
}
vty_init(&bts_vty_info);
e1inp_vty_init();
bts_vty_init(bts, &bts_log_info);
if (bts_init(bts) < 0) {
fprintf(stderr, "unable to to open bts\n");
exit(1);
}
btsb = bts_role_bts(bts);
btsb->support.ciphers = CIPHER_A5(1) | CIPHER_A5(2);
if (gsmtap_ip) {
gsmtap = gsmtap_source_init(gsmtap_ip, GSMTAP_UDP_PORT, 1);
if (!gsmtap) {
fprintf(stderr, "Failed during gsmtap_init()\n");
exit(1);
}
gsmtap_source_add_sink(gsmtap);
}
abis_init(bts);
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
fprintf(stderr, "Failed to parse the config file: '%s'\n",
config_file);
exit(1);
}
if (!settsc_enabled && !setbsic_enabled)
settsc_enabled = setbsic_enabled = 1;
write_pid_file("osmo-bts");
rc = telnet_init(tall_bts_ctx, NULL, 4241);
if (rc < 0) {
fprintf(stderr, "Error initializing telnet\n");
exit(1);
}
if (pcu_sock_init()) {
fprintf(stderr, "PCU L1 socket failed\n");
exit(-1);
}
signal(SIGINT, &signal_handler);
//signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
if (!btsb->bsc_oml_host) {
fprintf(stderr, "Cannot start BTS without knowing BSC OML IP\n");
exit(1);
}
line = abis_open(bts, btsb->bsc_oml_host, "sysmoBTS");
if (!line) {
fprintf(stderr, "unable to connect to BSC\n");
exit(1);
}
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
perror("Error during daemonize");
exit(1);
}
}
if (rt_prio != -1) {
struct sched_param schedp;
/* high priority scheduling required for handling bursts */
memset(&schedp, 0, sizeof(schedp));
schedp.sched_priority = rt_prio;
rc = sched_setscheduler(0, SCHED_RR, &schedp);
if (rc) {
fprintf(stderr, "Error setting SCHED_RR with prio %d\n",
rt_prio);
}
}
while (quit < 2) {
log_reset_context();
osmo_select_main(0);
}
#if 0
telnet_exit();
talloc_report_full(tall_bts_ctx, stderr);
#endif
return 0;
}

3044
src/osmo-bts-trx/scheduler.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
#ifndef TRX_SCHEDULER_H
#define TRX_SCHEDULER_H
extern uint32_t trx_clock_advance;
extern uint32_t trx_rts_advance;
extern uint32_t transceiver_last_fn;
int trx_sched_init(struct trx_l1h *l1h);
void trx_sched_exit(struct trx_l1h *l1h);
int trx_sched_ph_data_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap);
int trx_sched_tch_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap);
int trx_sched_clock(uint32_t fn);
int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
sbit_t *bits, int8_t rssi, float toa);
/* set multiframe scheduler to given pchan */
int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn,
enum gsm_phys_chan_config pchan);
/* setting all logical channels given attributes to active/inactive */
int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id,
int active);
/* setting all logical channels given attributes to active/inactive */
int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode,
uint8_t tch_mode, int codecs, uint8_t codec0, uint8_t codec1,
uint8_t codec2, uint8_t codec3, uint8_t initial_codec,
uint8_t handover);
/* setting cipher on logical channels */
int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink,
int algo, uint8_t *key, int key_len);
/* close all logical channels and reset timeslots */
void trx_sched_reset(struct trx_l1h *l1h);
#endif /* TRX_SCHEDULER_H */

560
src/osmo-bts-trx/trx_if.c Normal file
View File

@@ -0,0 +1,560 @@
/*
* OpenBTS TRX interface handling
*
* Copyright (C) 2013 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 <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/bits.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/bts.h>
#include "l1_if.h"
#include "trx_if.h"
#include "scheduler.h"
/* enable to print RSSI level graph */
//#define TOA_RSSI_DEBUG
int transceiver_available = 0;
const char *transceiver_ip = "127.0.0.1";
int settsc_enabled = 0;
int setbsic_enabled = 0;
/*
* socket
*/
static uint16_t base_port_local = 5800;
/* open socket */
static int trx_udp_open(void *priv, struct osmo_fd *ofd, uint16_t port,
int (*cb)(struct osmo_fd *fd, unsigned int what))
{
struct sockaddr_storage sas;
struct sockaddr *sa = (struct sockaddr *)&sas;
socklen_t sa_len;
int rc;
/* Init */
ofd->fd = -1;
ofd->cb = cb;
ofd->data = priv;
/* Listen / Binds */
rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, 0, transceiver_ip,
port, OSMO_SOCK_F_BIND);
if (rc < 0)
return rc;
/* Connect */
sa_len = sizeof(sas);
rc = getsockname(ofd->fd, sa, &sa_len);
if (rc)
return rc;
if (sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
sin->sin_port = htons(ntohs(sin->sin_port) - 100);
} else if (sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
sin6->sin6_port = htons(ntohs(sin6->sin6_port) - 100);
} else {
return -EINVAL;
}
rc = connect(ofd->fd, sa, sa_len);
if (rc)
return rc;
return 0;
}
/* close socket */
static void trx_udp_close(struct osmo_fd *ofd)
{
if (ofd->fd > 0) {
osmo_fd_unregister(ofd);
close(ofd->fd);
ofd->fd = -1;
}
}
/*
* clock
*/
static struct osmo_fd trx_ofd_clk;
/* get clock from clock socket */
static int trx_clk_read_cb(struct osmo_fd *ofd, unsigned int what)
{
char buf[1500];
int len;
uint32_t fn;
len = recv(ofd->fd, buf, sizeof(buf) - 1, 0);
if (len <= 0)
return len;
buf[len] = '\0';
if (!!strncmp(buf, "IND CLOCK ", 10)) {
LOGP(DTRX, LOGL_NOTICE, "Unknown message on clock port: %s\n",
buf);
return 0;
}
sscanf(buf, "IND CLOCK %u", &fn);
LOGP(DTRX, LOGL_INFO, "Clock indication: fn=%u\n", fn);
if (fn >= 2715648) {
fn %= 2715648;
LOGP(DTRX, LOGL_ERROR, "Indicated clock's FN is not wrapping "
"correctly, correcting to fn=%u\n", fn);
}
trx_sched_clock(fn);
return 0;
}
/*
* ctrl
*/
static void trx_ctrl_timer_cb(void *data);
/* send first ctrl message and start timer */
static void trx_ctrl_send(struct trx_l1h *l1h)
{
struct trx_ctrl_msg *tcm;
/* get first command */
if (llist_empty(&l1h->trx_ctrl_list))
return;
tcm = llist_entry(l1h->trx_ctrl_list.next, struct trx_ctrl_msg, list);
LOGP(DTRX, LOGL_DEBUG, "Sending control '%s' to trx=%u\n", tcm->cmd,
l1h->trx->nr);
/* send command */
send(l1h->trx_ofd_ctrl.fd, tcm->cmd, strlen(tcm->cmd)+1, 0);
/* start timer */
l1h->trx_ctrl_timer.cb = trx_ctrl_timer_cb;
l1h->trx_ctrl_timer.data = l1h;
osmo_timer_schedule(&l1h->trx_ctrl_timer, 2, 0);
}
/* send first ctrl message and start timer */
static void trx_ctrl_timer_cb(void *data)
{
struct trx_l1h *l1h = data;
LOGP(DTRX, LOGL_NOTICE, "No response from transceiver for trx=%d\n",
l1h->trx->nr);
trx_ctrl_send(l1h);
}
/* add a new ctrl command */
static int trx_ctrl_cmd(struct trx_l1h *l1h, int critical, const char *cmd,
const char *fmt, ...)
{
struct trx_ctrl_msg *tcm;
va_list ap;
int l, pending = 0;
if (!transceiver_available && !!strcmp(cmd, "POWEROFF")) {
LOGP(DTRX, LOGL_ERROR, "CTRL ignored: No clock from "
"transceiver, please fix!\n");
return -EIO;
}
if (!llist_empty(&l1h->trx_ctrl_list))
pending = 1;
/* create message */
tcm = talloc_zero(tall_bts_ctx, struct trx_ctrl_msg);
if (!tcm)
return -ENOMEM;
if (fmt && fmt[0]) {
l = snprintf(tcm->cmd, sizeof(tcm->cmd)-1, "CMD %s ", cmd);
va_start(ap, fmt);
vsnprintf(tcm->cmd + l, sizeof(tcm->cmd) - l - 1, fmt, ap);
va_end(ap);
} else
snprintf(tcm->cmd, sizeof(tcm->cmd)-1, "CMD %s", cmd);
tcm->cmd_len = strlen(cmd);
tcm->critical = critical;
llist_add_tail(&tcm->list, &l1h->trx_ctrl_list);
LOGP(DTRX, LOGL_INFO, "Adding new control '%s'\n", tcm->cmd);
/* send message, if no pending message */
if (!pending)
trx_ctrl_send(l1h);
return 0;
}
int trx_if_cmd_poweroff(struct trx_l1h *l1h)
{
if (l1h->trx->nr == 0)
return trx_ctrl_cmd(l1h, 1, "POWEROFF", "");
else
return 0;
}
int trx_if_cmd_poweron(struct trx_l1h *l1h)
{
if (l1h->trx->nr == 0)
return trx_ctrl_cmd(l1h, 1, "POWERON", "");
else
return 0;
}
int trx_if_cmd_settsc(struct trx_l1h *l1h, uint8_t tsc)
{
if (!settsc_enabled)
return 0;
/* if TSC is enabled only, the positive response is mandatory */
return trx_ctrl_cmd(l1h, (setbsic_enabled) ? 0 : 1, "SETTSC", "%d",
tsc);
}
int trx_if_cmd_setbsic(struct trx_l1h *l1h, uint8_t bsic)
{
if (!setbsic_enabled)
return 0;
/* if BSIC is enabled only, the positive response is mandatory */
return trx_ctrl_cmd(l1h, (settsc_enabled) ? 0 : 1, "SETBSIC", "%d",
bsic);
}
int trx_if_cmd_setrxgain(struct trx_l1h *l1h, int db)
{
return trx_ctrl_cmd(l1h, 0, "SETRXGAIN", "%d", db);
}
int trx_if_cmd_setpower(struct trx_l1h *l1h, int db)
{
return trx_ctrl_cmd(l1h, 0, "SETPOWER", "%d", db);
}
int trx_if_cmd_setmaxdly(struct trx_l1h *l1h, int dly)
{
return trx_ctrl_cmd(l1h, 0, "SETMAXDLY", "%d", dly);
}
int trx_if_cmd_setslot(struct trx_l1h *l1h, uint8_t tn, uint8_t type)
{
return trx_ctrl_cmd(l1h, 1, "SETSLOT", "%d %d", tn, type);
}
int trx_if_cmd_rxtune(struct trx_l1h *l1h, uint16_t arfcn)
{
uint16_t freq10;
freq10 = gsm_arfcn2freq10(arfcn, 1); /* RX = uplink */
if (freq10 == 0xffff) {
LOGP(DTRX, LOGL_ERROR, "Arfcn %d not defined.\n", arfcn);
return -ENOTSUP;
}
return trx_ctrl_cmd(l1h, 1, "RXTUNE", "%d", freq10 * 100);
}
int trx_if_cmd_txtune(struct trx_l1h *l1h, uint16_t arfcn)
{
uint16_t freq10;
freq10 = gsm_arfcn2freq10(arfcn, 0); /* TX = downlink */
if (freq10 == 0xffff) {
LOGP(DTRX, LOGL_ERROR, "Arfcn %d not defined.\n", arfcn);
return -ENOTSUP;
}
return trx_ctrl_cmd(l1h, 1, "TXTUNE", "%d", freq10 * 100);
}
int trx_if_cmd_handover(struct trx_l1h *l1h, uint8_t tn, uint8_t ss)
{
return trx_ctrl_cmd(l1h, 1, "HANDOVER", "%d %d", tn, ss);
}
int trx_if_cmd_nohandover(struct trx_l1h *l1h, uint8_t tn, uint8_t ss)
{
return trx_ctrl_cmd(l1h, 1, "NOHANDOVER", "%d %d", tn, ss);
}
/* get response from ctrl socket */
static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
{
struct trx_l1h *l1h = ofd->data;
char buf[1500];
int len, resp;
len = recv(ofd->fd, buf, sizeof(buf) - 1, 0);
if (len <= 0)
return len;
buf[len] = '\0';
if (!strncmp(buf, "RSP ", 4)) {
struct trx_ctrl_msg *tcm;
char *p;
int rsp_len = 0;
/* calculate the length of response item */
p = strchr(buf + 4, ' ');
if (p)
rsp_len = p - buf - 4;
else
rsp_len = strlen(buf) - 4;
LOGP(DTRX, LOGL_INFO, "Response message: '%s'\n", buf);
/* abort timer and send next message, if any */
if (osmo_timer_pending(&l1h->trx_ctrl_timer))
osmo_timer_del(&l1h->trx_ctrl_timer);
/* get command for response message */
if (llist_empty(&l1h->trx_ctrl_list)) {
LOGP(DTRX, LOGL_NOTICE, "Response message without "
"command\n");
return -EINVAL;
}
tcm = llist_entry(l1h->trx_ctrl_list.next, struct trx_ctrl_msg,
list);
/* check if respose matches command */
if (rsp_len != tcm->cmd_len) {
notmatch:
LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_NOTICE,
"Response message '%s' does not match command "
"message '%s'\n", buf, tcm->cmd);
goto rsp_error;
}
if (!!strncmp(buf + 4, tcm->cmd + 4, rsp_len))
goto notmatch;
/* check for response code */
sscanf(p + 1, "%d", &resp);
if (resp) {
LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_NOTICE,
"transceiver (trx=%d) rejected TRX command "
"with response: '%s'\n", l1h->trx->nr, buf);
rsp_error:
if (tcm->critical) {
bts_shutdown(l1h->trx->bts, "SIGINT");
/* keep tcm list, so process is stopped */
return -EIO;
}
}
/* remove command from list */
llist_del(&tcm->list);
talloc_free(tcm);
trx_ctrl_send(l1h);
} else
LOGP(DTRX, LOGL_NOTICE, "Unknown message on ctrl port: %s\n",
buf);
return 0;
}
/*
* data
*/
static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what)
{
struct trx_l1h *l1h = ofd->data;
uint8_t buf[256];
int len;
uint8_t tn;
int8_t rssi;
float toa = 0.0;
uint32_t fn;
sbit_t bits[148];
int i;
len = recv(ofd->fd, buf, sizeof(buf), 0);
if (len <= 0)
return len;
if (len != 158) {
LOGP(DTRX, LOGL_NOTICE, "Got data message with invalid lenght "
"'%d'\n", len);
return -EINVAL;
}
tn = buf[0];
fn = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4];
rssi = -(int8_t)buf[5];
toa = ((int16_t)(buf[6] << 8) | buf[7]) / 256.0F;
/* copy and convert bits {254..0} to sbits {-127..127} */
for (i = 0; i < 148; i++) {
if (buf[8 + i] == 255)
bits[i] = -127;
else
bits[i] = 127 - buf[8 + i];
}
if (tn >= 8) {
LOGP(DTRX, LOGL_ERROR, "Illegal TS %d\n", tn);
return -EINVAL;
}
if (fn >= 2715648) {
LOGP(DTRX, LOGL_ERROR, "Illegal FN %u\n", fn);
return -EINVAL;
}
LOGP(DTRX, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%.2f\n",
tn, fn, rssi, toa);
#ifdef TOA_RSSI_DEBUG
char deb[128];
sprintf(deb, "| 0 "
" | rssi=%4d toa=%4.2f fn=%u", rssi, toa, fn);
deb[1 + (128 + rssi) / 4] = '*';
fprintf(stderr, "%s\n", deb);
#endif
trx_sched_ul_burst(l1h, tn, fn, bits, rssi, toa);
return 0;
}
int trx_if_data(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t pwr,
const ubit_t *bits)
{
uint8_t buf[256];
LOGP(DTRX, LOGL_DEBUG, "TX burst tn=%u fn=%u pwr=%u\n", tn, fn, pwr);
buf[0] = tn;
buf[1] = (fn >> 24) & 0xff;
buf[2] = (fn >> 16) & 0xff;
buf[3] = (fn >> 8) & 0xff;
buf[4] = (fn >> 0) & 0xff;
buf[5] = pwr;
/* copy ubits {0,1} */
memcpy(buf + 6, bits, 148);
/* we must be sure that we have clock, and we have sent all control
* data */
if (transceiver_available && llist_empty(&l1h->trx_ctrl_list)) {
send(l1h->trx_ofd_data.fd, buf, 154, 0);
} else
LOGP(DTRX, LOGL_DEBUG, "Ignoring TX data, transceiver "
"offline.\n");
return 0;
}
/*
* open/close
*/
int trx_if_open(struct trx_l1h *l1h)
{
int rc;
LOGP(DTRX, LOGL_NOTICE, "Open transceiver for trx=%u\n", l1h->trx->nr);
/* initialize ctrl queue */
INIT_LLIST_HEAD(&l1h->trx_ctrl_list);
/* open sockets */
if (l1h->trx->nr == 0) {
rc = trx_udp_open(NULL, &trx_ofd_clk, base_port_local,
trx_clk_read_cb);
if (rc < 0)
return rc;
LOGP(DTRX, LOGL_NOTICE, "Waiting for transceiver send clock\n");
}
rc = trx_udp_open(l1h, &l1h->trx_ofd_ctrl,
base_port_local + (l1h->trx->nr << 1) + 1, trx_ctrl_read_cb);
if (rc < 0)
goto err;
rc = trx_udp_open(l1h, &l1h->trx_ofd_data,
base_port_local + (l1h->trx->nr << 1) + 2, trx_data_read_cb);
if (rc < 0)
goto err;
/* enable all slots */
l1h->config.slotmask = 0xff;
if (l1h->trx->nr == 0)
trx_if_cmd_poweroff(l1h);
return 0;
err:
trx_if_close(l1h);
return rc;
}
/* flush pending control messages */
void trx_if_flush(struct trx_l1h *l1h)
{
struct trx_ctrl_msg *tcm;
/* free ctrl message list */
while (!llist_empty(&l1h->trx_ctrl_list)) {
tcm = llist_entry(l1h->trx_ctrl_list.next, struct trx_ctrl_msg,
list);
llist_del(&tcm->list);
talloc_free(tcm);
}
}
void trx_if_close(struct trx_l1h *l1h)
{
LOGP(DTRX, LOGL_NOTICE, "Close transceiver for trx=%u\n", l1h->trx->nr);
trx_if_flush(l1h);
/* close sockets */
if (l1h->trx->nr == 0)
trx_udp_close(&trx_ofd_clk);
trx_udp_close(&l1h->trx_ofd_ctrl);
trx_udp_close(&l1h->trx_ofd_data);
}

35
src/osmo-bts-trx/trx_if.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef TRX_IF_H
#define TRX_IF_H
extern int transceiver_available;
extern const char *transceiver_ip;
extern int settsc_enabled;
extern int setbsic_enabled;
struct trx_ctrl_msg {
struct llist_head list;
char cmd[128];
int cmd_len;
int critical;
};
int trx_if_cmd_poweroff(struct trx_l1h *l1h);
int trx_if_cmd_poweron(struct trx_l1h *l1h);
int trx_if_cmd_settsc(struct trx_l1h *l1h, uint8_t tsc);
int trx_if_cmd_setbsic(struct trx_l1h *l1h, uint8_t bsic);
int trx_if_cmd_setrxgain(struct trx_l1h *l1h, int db);
int trx_if_cmd_setpower(struct trx_l1h *l1h, int db);
int trx_if_cmd_setmaxdly(struct trx_l1h *l1h, int dly);
int trx_if_cmd_setslot(struct trx_l1h *l1h, uint8_t tn, uint8_t type);
int trx_if_cmd_rxtune(struct trx_l1h *l1h, uint16_t arfcn);
int trx_if_cmd_txtune(struct trx_l1h *l1h, uint16_t arfcn);
int trx_if_cmd_handover(struct trx_l1h *l1h, uint8_t tn, uint8_t ss);
int trx_if_cmd_nohandover(struct trx_l1h *l1h, uint8_t tn, uint8_t ss);
int trx_if_data(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t pwr,
const ubit_t *bits);
int trx_if_open(struct trx_l1h *l1h);
void trx_if_flush(struct trx_l1h *l1h);
void trx_if_close(struct trx_l1h *l1h);
#endif /* TRX_IF_H */

421
src/osmo-bts-trx/trx_vty.c Normal file
View File

@@ -0,0 +1,421 @@
/* VTY interface for sysmoBTS */
/* (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 <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/bits.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/misc.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/vty.h>
#include "l1_if.h"
#include "scheduler.h"
#include "trx_if.h"
#include "loops.h"
static struct gsm_bts *vty_bts;
DEFUN(show_transceiver, show_transceiver_cmd, "show transceiver",
SHOW_STR "Display information about transceivers\n")
{
struct gsm_bts *bts = vty_bts;
struct gsm_bts_trx *trx;
struct trx_l1h *l1h;
uint8_t tn;
if (!transceiver_available) {
vty_out(vty, "transceiver is not connected%s", VTY_NEWLINE);
} else {
vty_out(vty, "transceiver is connected, current fn=%u%s",
transceiver_last_fn, VTY_NEWLINE);
}
llist_for_each_entry(trx, &bts->trx_list, list) {
l1h = trx_l1h_hdl(trx);
vty_out(vty, "TRX %d%s", trx->nr, VTY_NEWLINE);
vty_out(vty, " %s%s",
(l1h->config.poweron) ? "poweron":"poweroff",
VTY_NEWLINE);
if (l1h->config.arfcn_valid)
vty_out(vty, " arfcn : %d%s%s",
(l1h->config.arfcn & ~ARFCN_PCS),
(l1h->config.arfcn & ARFCN_PCS) ? " (PCS)" : "",
VTY_NEWLINE);
else
vty_out(vty, " arfcn : undefined%s", VTY_NEWLINE);
if (l1h->config.tsc_valid)
vty_out(vty, " tsc : %d%s", l1h->config.tsc,
VTY_NEWLINE);
else
vty_out(vty, " tsc : undefined%s", VTY_NEWLINE);
if (l1h->config.bsic_valid)
vty_out(vty, " bsic : %d%s", l1h->config.bsic,
VTY_NEWLINE);
else
vty_out(vty, " bisc : undefined%s", VTY_NEWLINE);
if (l1h->config.rxgain_valid)
vty_out(vty, " rxgain : %d%s", l1h->config.rxgain,
VTY_NEWLINE);
else
vty_out(vty, " rxgain : undefined%s", VTY_NEWLINE);
if (l1h->config.power_valid)
vty_out(vty, " power : %d%s", l1h->config.power,
VTY_NEWLINE);
else
vty_out(vty, " power : undefined%s", VTY_NEWLINE);
if (l1h->config.maxdly_valid)
vty_out(vty, " maxdly : %d%s", l1h->config.maxdly,
VTY_NEWLINE);
else
vty_out(vty, " maxdly : undefined%s", VTY_NEWLINE);
for (tn = 0; tn < 8; tn++) {
if (!((1 << tn) & l1h->config.slotmask))
vty_out(vty, " slot #%d: unsupported%s", tn,
VTY_NEWLINE);
else if (l1h->config.slottype_valid[tn])
vty_out(vty, " slot #%d: type %d%s", tn,
l1h->config.slottype[tn],
VTY_NEWLINE);
else
vty_out(vty, " slot #%d: undefined%s", tn,
VTY_NEWLINE);
}
}
return CMD_SUCCESS;
}
DEFUN(cfg_bts_fn_advance, cfg_bts_fn_advance_cmd,
"fn-advance <0-30>",
"Set the number of frames to be transmitted to transceiver in advance "
"of current FN\n"
"Advance in frames\n")
{
trx_clock_advance = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_bts_rts_advance, cfg_bts_rts_advance_cmd,
"rts-advance <0-30>",
"Set the number of frames to be requested (PCU) in advance of current "
"FN. Do not change this, unless you have a good reason!\n"
"Advance in frames\n")
{
trx_rts_advance = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_bts_ms_power_loop, cfg_bts_ms_power_loop_cmd,
"ms-power-loop <-127-127>",
"Enable MS power control loop\nTarget RSSI value (transceiver specific, "
"should be 6dB or more above noise floor)\n")
{
trx_ms_power_loop = 1;
trx_target_rssi = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_bts_no_ms_power_loop, cfg_bts_no_ms_power_loop_cmd,
"no ms-power-loop",
NO_STR "Disable MS power control loop\n")
{
trx_ms_power_loop = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_bts_timing_advance_loop, cfg_bts_timing_advance_loop_cmd,
"timing-advance-loop",
"Enable timing advance control loop\n")
{
trx_ta_loop = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_bts_no_timing_advance_loop, cfg_bts_no_timing_advance_loop_cmd,
"no timing-advance-loop",
NO_STR "Disable timing advance control loop\n")
{
trx_ta_loop = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_bts_settsc, cfg_bts_settsc_cmd,
"settsc",
"Use SETTSC to configure transceiver\n")
{
settsc_enabled = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_bts_setbsic, cfg_bts_setbsic_cmd,
"setbsic",
"Use SETBSIC to configure transceiver\n")
{
setbsic_enabled = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_bts_no_settsc, cfg_bts_no_settsc_cmd,
"no settsc",
NO_STR "Disable SETTSC to configure transceiver\n")
{
settsc_enabled = 0;
if (!setbsic_enabled) {
vty_out(vty, "%% Auto enabling SETBSIC.%s", VTY_NEWLINE);
setbsic_enabled = 1;
}
return CMD_SUCCESS;
}
DEFUN(cfg_bts_no_setbsic, cfg_bts_no_setbsic_cmd,
"no setbsic",
NO_STR "Disable SETBSIC to configure transceiver\n")
{
setbsic_enabled = 0;
if (!settsc_enabled) {
vty_out(vty, "%% Auto enabling SETTSC.%s", VTY_NEWLINE);
settsc_enabled = 1;
}
return CMD_SUCCESS;
}
DEFUN(cfg_trx_rxgain, cfg_trx_rxgain_cmd,
"rxgain <0-50>",
"Set the receiver gain in dB\n"
"Gain in dB\n")
{
struct gsm_bts_trx *trx = vty->index;
struct trx_l1h *l1h = trx_l1h_hdl(trx);
l1h->config.rxgain = atoi(argv[0]);
l1h->config.rxgain_valid = 1;
l1h->config.rxgain_sent = 0;
l1if_provision_transceiver_trx(l1h);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_power, cfg_trx_power_cmd,
"power <0-50>",
"Set the transmitter power dampening\n"
"Power dampening in dB\n")
{
struct gsm_bts_trx *trx = vty->index;
struct trx_l1h *l1h = trx_l1h_hdl(trx);
l1h->config.power = atoi(argv[0]);
l1h->config.power_oml = 0;
l1h->config.power_valid = 1;
l1h->config.power_sent = 0;
l1if_provision_transceiver_trx(l1h);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_poweroml_, cfg_trx_power_oml_cmd,
"power oml",
"Set the transmitter power dampening\n"
"Given by NM_ATT_RF_MAXPOWR_R (max power reduction) via OML\n")
{
struct gsm_bts_trx *trx = vty->index;
struct trx_l1h *l1h = trx_l1h_hdl(trx);
l1h->config.power = trx->max_power_red;
l1h->config.power_oml = 1;
l1h->config.power_valid = 1;
l1h->config.power_sent = 0;
l1if_provision_transceiver_trx(l1h);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_maxdly, cfg_trx_maxdly_cmd,
"maxdly <0-31>",
"Set the maximum delay of GSM symbols\n"
"GSM symbols (approx. 1.1km per symbol)\n")
{
struct gsm_bts_trx *trx = vty->index;
struct trx_l1h *l1h = trx_l1h_hdl(trx);
l1h->config.maxdly = atoi(argv[0]);
l1h->config.maxdly_valid = 1;
l1h->config.maxdly_sent = 0;
l1if_provision_transceiver_trx(l1h);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_slotmask, cfg_trx_slotmask_cmd,
"slotmask (1|0) (1|0) (1|0) (1|0) (1|0) (1|0) (1|0) (1|0)",
"Set the supported slots\n"
"TS0 supported\nTS0 unsupported\nTS1 supported\nTS1 unsupported\n"
"TS2 supported\nTS2 unsupported\nTS3 supported\nTS3 unsupported\n"
"TS4 supported\nTS4 unsupported\nTS5 supported\nTS5 unsupported\n"
"TS6 supported\nTS6 unsupported\nTS7 supported\nTS7 unsupported\n")
{
struct gsm_bts_trx *trx = vty->index;
struct trx_l1h *l1h = trx_l1h_hdl(trx);
uint8_t tn;
l1h->config.slotmask = 0;
for (tn = 0; tn < 8; tn++)
if (argv[tn][0] == '1')
l1h->config.slotmask |= (1 << tn);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_no_rxgain, cfg_trx_no_rxgain_cmd,
"no rxgain <0-50>",
NO_STR "Unset the receiver gain in dB\n"
"Gain in dB\n")
{
struct gsm_bts_trx *trx = vty->index;
struct trx_l1h *l1h = trx_l1h_hdl(trx);
l1h->config.rxgain_valid = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_trx_no_power, cfg_trx_no_power_cmd,
"no power <0-50>",
NO_STR "Unset the transmitter power dampening\n"
"Power dampening in dB\n")
{
struct gsm_bts_trx *trx = vty->index;
struct trx_l1h *l1h = trx_l1h_hdl(trx);
l1h->config.power_valid = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_trx_no_maxdly, cfg_trx_no_maxdly_cmd,
"no maxdly <0-31>",
NO_STR "Unset the maximum delay of GSM symbols\n"
"GSM symbols (approx. 1.1km per symbol)\n")
{
struct gsm_bts_trx *trx = vty->index;
struct trx_l1h *l1h = trx_l1h_hdl(trx);
l1h->config.maxdly_valid = 0;
return CMD_SUCCESS;
}
void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts)
{
vty_out(vty, " fn-advance %d%s", trx_clock_advance, VTY_NEWLINE);
vty_out(vty, " rts-advance %d%s", trx_rts_advance, VTY_NEWLINE);
if (trx_ms_power_loop)
vty_out(vty, " ms-power-loop %d%s", trx_target_rssi,
VTY_NEWLINE);
else
vty_out(vty, " no ms-power-loop%s", VTY_NEWLINE);
vty_out(vty, " %stiming-advance-loop%s", (trx_ta_loop) ? "":"no ",
VTY_NEWLINE);
if (settsc_enabled)
vty_out(vty, " settsc%s", VTY_NEWLINE);
if (setbsic_enabled)
vty_out(vty, " setbsic%s", VTY_NEWLINE);
}
void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
{
struct trx_l1h *l1h = trx_l1h_hdl(trx);
if (l1h->config.rxgain_valid)
vty_out(vty, " rxgain %d%s", l1h->config.rxgain, VTY_NEWLINE);
if (l1h->config.power_valid) {
if (l1h->config.power_oml)
vty_out(vty, " power oml%s", VTY_NEWLINE);
else
vty_out(vty, " power %d%s", l1h->config.power,
VTY_NEWLINE);
}
if (l1h->config.maxdly_valid)
vty_out(vty, " maxdly %d%s", l1h->config.maxdly, VTY_NEWLINE);
if (l1h->config.slotmask != 0xff)
vty_out(vty, " slotmask %d %d %d %d %d %d %d %d%s",
l1h->config.slotmask & 1,
(l1h->config.slotmask >> 1) & 1,
(l1h->config.slotmask >> 2) & 1,
(l1h->config.slotmask >> 3) & 1,
(l1h->config.slotmask >> 4) & 1,
(l1h->config.slotmask >> 5) & 1,
(l1h->config.slotmask >> 6) & 1,
l1h->config.slotmask >> 7,
VTY_NEWLINE);
}
int bts_model_vty_init(struct gsm_bts *bts)
{
vty_bts = bts;
install_element_ve(&show_transceiver_cmd);
install_element(BTS_NODE, &cfg_bts_fn_advance_cmd);
install_element(BTS_NODE, &cfg_bts_rts_advance_cmd);
install_element(BTS_NODE, &cfg_bts_ms_power_loop_cmd);
install_element(BTS_NODE, &cfg_bts_no_ms_power_loop_cmd);
install_element(BTS_NODE, &cfg_bts_timing_advance_loop_cmd);
install_element(BTS_NODE, &cfg_bts_no_timing_advance_loop_cmd);
install_element(BTS_NODE, &cfg_bts_settsc_cmd);
install_element(BTS_NODE, &cfg_bts_setbsic_cmd);
install_element(BTS_NODE, &cfg_bts_no_settsc_cmd);
install_element(BTS_NODE, &cfg_bts_no_setbsic_cmd);
install_element(TRX_NODE, &cfg_trx_rxgain_cmd);
install_element(TRX_NODE, &cfg_trx_power_cmd);
install_element(TRX_NODE, &cfg_trx_power_oml_cmd);
install_element(TRX_NODE, &cfg_trx_maxdly_cmd);
install_element(TRX_NODE, &cfg_trx_slotmask_cmd);
install_element(TRX_NODE, &cfg_trx_no_rxgain_cmd);
install_element(TRX_NODE, &cfg_trx_no_power_cmd);
install_element(TRX_NODE, &cfg_trx_no_maxdly_cmd);
return 0;
}

View File

@@ -1,4 +1,12 @@
SUBDIRS = paging cipher sysmobts agch misc
SUBDIRS = paging cipher agch misc bursts handover
if ENABLE_SYSMOBTS
SUBDIRS += sysmobts
endif
if ENABLE_SYSMOBTS
SUBDIRS += sysmobts
endif
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac

14
tests/bursts/Makefile.am Normal file
View File

@@ -0,0 +1,14 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS)
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS)
noinst_PROGRAMS = bursts_test
EXTRA_DIST = bursts_test.ok
bursts_test_SOURCES = bursts_test.c \
$(top_builddir)/src/osmo-bts-trx/gsm0503_coding.c \
$(top_builddir)/src/osmo-bts-trx/gsm0503_conv.c \
$(top_builddir)/src/osmo-bts-trx/gsm0503_interleaving.c \
$(top_builddir)/src/osmo-bts-trx/gsm0503_mapping.c \
$(top_builddir)/src/osmo-bts-trx/gsm0503_tables.c \
$(top_builddir)/src/osmo-bts-trx/gsm0503_parity.c
bursts_test_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)

495
tests/bursts/bursts_test.c Normal file
View File

@@ -0,0 +1,495 @@
/* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/utils.h>
#include "../../src/osmo-bts-trx/gsm0503_coding.h"
#include <osmo-bts/logging.h>
#define ASSERT_TRUE(rc) \
if (!(rc)) { \
printf("Assert failed in %s:%d.\n", \
__FILE__, __LINE__); \
abort(); \
}
/* set condition to 1, to show debugging */
#define printd if (0) printf
static int ubits2sbits(ubit_t *ubits, sbit_t *sbits, int count)
{
int i;
for (i = 0; i < count; i++) {
if (*ubits == 0x23) {
ubits++;
sbits++;
continue;
}
if ((*ubits++) & 1)
*sbits++ = -127;
else
*sbits++ = 127;
}
return count;
}
static void test_xcch(uint8_t *l2)
{
uint8_t result[23];
ubit_t bursts_u[116 * 4];
sbit_t bursts_s[116 * 4];
int n_errors, n_bits_total;
printd("Encoding: %s\n", osmo_hexdump(l2, 23));
/* encode */
xcch_encode(bursts_u, l2);
printd("U-Bits:\n");
printd("%s %02x %02x ", osmo_hexdump(bursts_u, 57),
bursts_u[57], bursts_u[58]);
printd("%s\n", osmo_hexdump(bursts_u + 59, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 116, 57),
bursts_u[57 + 116], bursts_u[58 + 116]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 116, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 232, 57),
bursts_u[57 + 232], bursts_u[58 + 232]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 232, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 348, 57),
bursts_u[57 + 348], bursts_u[58 + 348]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 348, 57));
ubits2sbits(bursts_u, bursts_s, 116 * 4);
printd("S-Bits:\n");
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s, 57),
(uint8_t)bursts_s[57], (uint8_t)bursts_s[58]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 116, 57),
(uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 232, 57),
(uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 348, 57),
(uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57));
/* destroy */
memset(bursts_s, 0, 30);
memset(bursts_s + 116, 0, 30);
/* decode */
xcch_decode(result, bursts_s, &n_errors, &n_bits_total);
ASSERT_TRUE(n_bits_total == 456);
printd("Decoded: %s\n", osmo_hexdump(result, 23));
printf("xcch_decode: n_errors=%d n_bits_total=%d ber=%.2f\n",
n_errors, n_bits_total, (float)n_errors/n_bits_total);
ASSERT_TRUE(!memcmp(l2, result, 23));
printd("\n");
}
static void test_rach(uint8_t bsic, uint8_t ra)
{
uint8_t result;
ubit_t bursts_u[36];
sbit_t bursts_s[36];
printd("Encoding: %02x\n", ra);
/* encode */
rach_encode(bursts_u, &ra, bsic);
printd("U-Bits:\n");
printd("%s\n", osmo_hexdump(bursts_u, 36));
ubits2sbits(bursts_u, bursts_s, 36);
printd("S-Bits:\n");
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s, 36));
/* destroy */
memset(bursts_s + 6, 0, 8);
/* decode */
rach_decode(&result, bursts_s, bsic);
printd("Decoded: %02x\n", result);
ASSERT_TRUE(ra == result);
printd("\n");
}
static void test_sch(uint8_t *info)
{
uint8_t result[4];
ubit_t bursts_u[78];
sbit_t bursts_s[78];
/* zero bits 25 and above */
info[3] &= 1;
result[3] = 0;
printd("Encoding: %s\n", osmo_hexdump(info, 4));
/* encode */
sch_encode(bursts_u, info);
printd("U-Bits:\n");
printd("%s\n", osmo_hexdump(bursts_u, 78));
ubits2sbits(bursts_u, bursts_s, 78);
printd("S-Bits:\n");
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s, 78));
/* destroy */
memset(bursts_s + 6, 0, 10);
/* decode */
sch_decode(result, bursts_s);
printd("Decoded: %s\n", osmo_hexdump(result, 4));
ASSERT_TRUE(!memcmp(info, result, 4));
printd("\n");
}
static void test_fr(uint8_t *speech, int len)
{
uint8_t result[33];
ubit_t bursts_u[116 * 8];
sbit_t bursts_s[116 * 8];
int n_errors, n_bits_total;
int rc;
memset(bursts_u, 0x23, sizeof(bursts_u));
memset(bursts_s, 0, sizeof(bursts_s));
printd("Encoding: %s\n", osmo_hexdump(speech, len));
/* encode */
tch_fr_encode(bursts_u, speech, len, 1);
printd("U-Bits:\n");
printd("%s %02x %02x ", osmo_hexdump(bursts_u, 57),
bursts_u[57], bursts_u[58]);
printd("%s\n", osmo_hexdump(bursts_u + 59, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 116, 57),
bursts_u[57 + 116], bursts_u[58 + 116]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 116, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 232, 57),
bursts_u[57 + 232], bursts_u[58 + 232]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 232, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 348, 57),
bursts_u[57 + 348], bursts_u[58 + 348]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 348, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 464, 57),
bursts_u[57 + 464], bursts_u[58 + 464]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 464, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 580, 57),
bursts_u[57 + 580], bursts_u[58 + 580]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 580, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 696, 57),
bursts_u[57 + 696], bursts_u[58 + 696]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 696, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 812, 57),
bursts_u[57 + 812], bursts_u[58 + 812]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 812, 57));
ubits2sbits(bursts_u, bursts_s, 116 * 8);
printd("S-Bits:\n");
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s, 57),
(uint8_t)bursts_s[57], (uint8_t)bursts_s[58]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 116, 57),
(uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 232, 57),
(uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 348, 57),
(uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 464, 57),
(uint8_t)bursts_s[57 + 464], (uint8_t)bursts_s[58 + 464]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 464, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 580, 57),
(uint8_t)bursts_s[57 + 580], (uint8_t)bursts_s[58 + 580]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 580, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 696, 57),
(uint8_t)bursts_s[57 + 696], (uint8_t)bursts_s[58 + 696]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 696, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 812, 57),
(uint8_t)bursts_s[57 + 812], (uint8_t)bursts_s[58 + 812]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 812, 57));
/* destroy */
memset(bursts_s + 6, 0, 20);
/* decode */
rc = tch_fr_decode(result, bursts_s, 1, len == 31, &n_errors, &n_bits_total);
ASSERT_TRUE(rc == len);
printd("Decoded: %s\n", osmo_hexdump(result, len));
printf("tch_fr_decode: n_errors=%d n_bits_total=%d ber=%.2f\n",
n_errors, n_bits_total, (float)n_errors/n_bits_total);
ASSERT_TRUE(!memcmp(speech, result, len));
printd("\n");
}
static void test_hr(uint8_t *speech, int len)
{
uint8_t result[23];
ubit_t bursts_u[116 * 6];
sbit_t bursts_s[116 * 6];
int n_errors, n_bits_total;
int rc;
memset(bursts_u, 0x23, sizeof(bursts_u));
memset(bursts_s, 0, sizeof(bursts_s));
printd("Encoding: %s\n", osmo_hexdump(speech, len));
/* encode */
tch_hr_encode(bursts_u, speech, len);
printd("U-Bits:\n");
printd("%s %02x %02x ", osmo_hexdump(bursts_u, 57),
bursts_u[57], bursts_u[58]);
printd("%s\n", osmo_hexdump(bursts_u + 59, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 116, 57),
bursts_u[57 + 116], bursts_u[58 + 116]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 116, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 232, 57),
bursts_u[57 + 232], bursts_u[58 + 232]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 232, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 348, 57),
bursts_u[57 + 348], bursts_u[58 + 348]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 348, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 464, 57),
bursts_u[57 + 464], bursts_u[58 + 464]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 464, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 580, 57),
bursts_u[57 + 580], bursts_u[58 + 580]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 580, 57));
ubits2sbits(bursts_u, bursts_s, 116 * 6);
printd("S-Bits:\n");
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s, 57),
(uint8_t)bursts_s[57], (uint8_t)bursts_s[58]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 116, 57),
(uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 232, 57),
(uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 348, 57),
(uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 464, 57),
(uint8_t)bursts_s[57 + 464], (uint8_t)bursts_s[58 + 464]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 464, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 580, 57),
(uint8_t)bursts_s[57 + 580], (uint8_t)bursts_s[58 + 580]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 580, 57));
/* destroy */
memset(bursts_s + 6, 0, 20);
/* decode */
rc = tch_hr_decode(result, bursts_s, 0, &n_errors, &n_bits_total);
ASSERT_TRUE(rc == len);
printd("Decoded: %s\n", osmo_hexdump(result, len));
printf("tch_hr_decode: n_errors=%d n_bits_total=%d ber=%.2f\n",
n_errors, n_bits_total, (float)n_errors/n_bits_total);
ASSERT_TRUE(!memcmp(speech, result, len));
printd("\n");
}
static void test_pdtch(uint8_t *l2, int len)
{
uint8_t result[len];
ubit_t bursts_u[116 * 4];
sbit_t bursts_s[116 * 4];
int n_errors, n_bits_total;
int rc;
/* zero the not coded tail bits */
switch (len) {
case 34:
case 54:
l2[len - 1] &= 0x7f;
result[len - 1] &= 0x7f;
break;
case 40:
l2[len - 1] &= 0x07;
result[len - 1] &= 0x07;
break;
}
printd("Encoding: %s\n", osmo_hexdump(l2, len));
/* encode */
pdtch_encode(bursts_u, l2, len);
printd("U-Bits:\n");
printd("%s %02x %02x ", osmo_hexdump(bursts_u, 57),
bursts_u[57], bursts_u[58]);
printd("%s\n", osmo_hexdump(bursts_u + 59, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 116, 57),
bursts_u[57 + 116], bursts_u[58 + 116]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 116, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 232, 57),
bursts_u[57 + 232], bursts_u[58 + 232]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 232, 57));
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 348, 57),
bursts_u[57 + 348], bursts_u[58 + 348]);
printd("%s\n", osmo_hexdump(bursts_u + 59 + 348, 57));
ubits2sbits(bursts_u, bursts_s, 116 * 4);
printd("S-Bits:\n");
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s, 57),
(uint8_t)bursts_s[57], (uint8_t)bursts_s[58]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 116, 57),
(uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 232, 57),
(uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57));
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 348, 57),
(uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348]);
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57));
/* decode */
rc = pdtch_decode(result, bursts_s, NULL, &n_errors, &n_bits_total);
ASSERT_TRUE(rc == len);
printd("Decoded: %s\n", osmo_hexdump(result, len));
printf("pdtch_decode: n_errors=%d n_bits_total=%d ber=%.2f\n",
n_errors, n_bits_total, (float)n_errors/n_bits_total);
ASSERT_TRUE(!memcmp(l2, result, len));
printd("\n");
}
uint8_t test_l2[][23] = {
/* dummy frame */
{ 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
/* random frame */
{ 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab,
0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92,
0xfa, 0x27, 0x5d, 0x71, 0x7a, 0x59, 0xa8 },
/* jolly frame */
{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 },
};
uint8_t test_macblock[][54] = {
/* random frame */
{ 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab,
0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92,
0xfa, 0x27, 0x5d, 0x71, 0x7a, 0x59, 0xa8, 0x42,
0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab,
0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab,
0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92,
0xfa, 0x27, 0x5d, 0x71, 0x7a, 0xa8 },
/* jolly frame */
{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 },
};
uint8_t test_speech_fr[33];
uint8_t test_speech_efr[31];
uint8_t test_speech_hr[15];
int main(int argc, char **argv)
{
int i;
bts_log_init(NULL);
for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++)
test_xcch(test_l2[i]);
for (i = 0; i < 256; i++) {
test_rach(0x3f, i);
test_rach(0x00, i);
test_rach(0x1a, i);
}
for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++)
test_sch(test_l2[i]);
for (i = 0; i < sizeof(test_speech_fr); i++)
test_speech_fr[i] = i;
test_speech_fr[0] = 0xd0;
test_fr(test_speech_fr, sizeof(test_speech_fr));
for (i = 0; i < sizeof(test_speech_efr); i++)
test_speech_efr[i] = i;
test_speech_efr[0] = 0xc0;
test_fr(test_speech_efr, sizeof(test_speech_efr));
for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++)
test_fr(test_l2[i], sizeof(test_l2[0]));
for (i = 0; i < sizeof(test_speech_hr); i++)
test_speech_hr[i] = i*17;
test_speech_hr[0] = 0x00;
test_hr(test_speech_hr, sizeof(test_speech_hr));
for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++)
test_hr(test_l2[i], sizeof(test_l2[0]));
for (i = 0; i < sizeof(test_macblock) / sizeof(test_macblock[0]); i++) {
test_pdtch(test_macblock[i], 23);
test_pdtch(test_macblock[i], 34);
test_pdtch(test_macblock[i], 40);
test_pdtch(test_macblock[i], 54);
}
printf("Success\n");
return 0;
}

View File

@@ -0,0 +1,21 @@
xcch_decode: n_errors=60 n_bits_total=456 ber=0.13
xcch_decode: n_errors=60 n_bits_total=456 ber=0.13
xcch_decode: n_errors=60 n_bits_total=456 ber=0.13
tch_fr_decode: n_errors=8 n_bits_total=378 ber=0.02
tch_fr_decode: n_errors=8 n_bits_total=378 ber=0.02
tch_fr_decode: n_errors=10 n_bits_total=456 ber=0.02
tch_fr_decode: n_errors=10 n_bits_total=456 ber=0.02
tch_fr_decode: n_errors=10 n_bits_total=456 ber=0.02
tch_hr_decode: n_errors=11 n_bits_total=211 ber=0.05
tch_hr_decode: n_errors=10 n_bits_total=456 ber=0.02
tch_hr_decode: n_errors=10 n_bits_total=456 ber=0.02
tch_hr_decode: n_errors=10 n_bits_total=456 ber=0.02
pdtch_decode: n_errors=0 n_bits_total=456 ber=0.00
pdtch_decode: n_errors=132 n_bits_total=588 ber=0.22
pdtch_decode: n_errors=220 n_bits_total=676 ber=0.33
pdtch_decode: n_errors=0 n_bits_total=444 ber=0.00
pdtch_decode: n_errors=0 n_bits_total=456 ber=0.00
pdtch_decode: n_errors=132 n_bits_total=588 ber=0.22
pdtch_decode: n_errors=220 n_bits_total=676 ber=0.33
pdtch_decode: n_errors=0 n_bits_total=444 ber=0.00
Success

View File

@@ -0,0 +1,8 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS)$(LIBOSMOTRAU_CFLAGS)
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS)
noinst_PROGRAMS = handover_test
EXTRA_DIST = handover_test.ok
handover_test_SOURCES = handover_test.c
handover_test_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)

View File

@@ -0,0 +1,278 @@
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <sched.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/application.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/backtrace.h>
#include <osmocom/abis/abis.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/abis.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/vty.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/l1sap.h>
#include <osmo-bts/handover.h>
uint8_t phys_info[] = { 0x03, 0x03, 0x0d, 0x06, 0x2d, 0x00, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b };
static struct gsm_bts *bts;
struct gsm_bts_trx *trx;
int quit = 0;
uint8_t abis_mac[6] = { 0, 1, 2, 3, 4, 5 };
const int pcu_direct = 0;
int modify_count = 0;
static void expect_phys_info(struct lapdm_entity *le)
{
struct osmo_phsap_prim pp;
int rc;
rc = lapdm_phsap_dequeue_prim(le, &pp);
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(sizeof(phys_info) == pp.oph.msg->len);
OSMO_ASSERT(!memcmp(phys_info, pp.oph.msg->data, pp.oph.msg->len));
msgb_free(pp.oph.msg);
}
int main(int argc, char **argv)
{
struct gsm_bts_role_bts *btsb;
void *tall_bts_ctx;
void *tall_msgb_ctx;
struct e1inp_line *line;
struct gsm_lchan *lchan;
struct osmo_phsap_prim nl1sap;
struct msgb *msg;
struct abis_rsl_dchan_hdr *rslh;
int i;
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
tall_msgb_ctx = talloc_named_const(tall_bts_ctx, 1, "msgb");
msgb_set_talloc_ctx(tall_msgb_ctx);
bts_log_init(NULL);
osmo_stderr_target->categories[DHO].loglevel = LOGL_DEBUG;
bts = gsm_bts_alloc(tall_bts_ctx);
if (!bts) {
fprintf(stderr, "Failed to create BTS structure\n");
exit(1);
}
trx = gsm_bts_trx_alloc(bts);
if (!trx) {
fprintf(stderr, "Failed to TRX structure\n");
exit(1);
}
if (bts_init(bts) < 0) {
fprintf(stderr, "unable to to open bts\n");
exit(1);
}
btsb = bts_role_bts(bts);
libosmo_abis_init(NULL);
line = e1inp_line_create(0, "ipa");
OSMO_ASSERT(line);
e1inp_ts_config_sign(&line->ts[E1INP_SIGN_RSL-1], line);
trx->rsl_link = e1inp_sign_link_create(&line->ts[E1INP_SIGN_RSL-1], E1INP_SIGN_RSL, NULL, 0, 0);
OSMO_ASSERT(trx->rsl_link);
trx->rsl_link->trx = trx;
fprintf(stderr, "test 1: without timeout\n");
/* create two lchans for handover */
lchan = &trx->ts[1].lchan[0];
l1sap_chan_act(lchan->ts->trx, 0x09, NULL);
lchan = &trx->ts[2].lchan[0];
lchan->ho.active = HANDOVER_ENABLED;
lchan->ho.ref = 23;
l1sap_chan_act(lchan->ts->trx, 0x0a, NULL);
OSMO_ASSERT(msgb_dequeue(&trx->rsl_link->tx_list));
OSMO_ASSERT(msgb_dequeue(&trx->rsl_link->tx_list));
OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list));
/* send access burst with wrong ref */
memset(&nl1sap, 0, sizeof(nl1sap));
osmo_prim_init(&nl1sap.oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_INDICATION, NULL);
nl1sap.u.rach_ind.chan_nr = 0x0a;
nl1sap.u.rach_ind.ra = 42;
l1sap_up(trx, &nl1sap);
/* expect no action */
OSMO_ASSERT(modify_count == 0);
OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list));
/* send access burst with correct ref */
nl1sap.u.rach_ind.ra = 23;
l1sap_up(trx, &nl1sap);
OSMO_ASSERT(modify_count == 1);
/* expect PHYS INFO */
expect_phys_info(&trx->ts[2].lchan[0].lapdm_ch.lapdm_dcch);
/* expect exactly one HO.DET */
OSMO_ASSERT(msg = msgb_dequeue(&trx->rsl_link->tx_list));
rslh = msgb_l2(msg);
OSMO_ASSERT(rslh->c.msg_type == RSL_MT_HANDO_DET);
OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list));
/* expect T3105 running */
OSMO_ASSERT(osmo_timer_pending(&trx->ts[2].lchan[0].ho.t3105))
/* indicate frame */
handover_frame(&trx->ts[2].lchan[0]);
/* expect T3105 not running */
OSMO_ASSERT(!osmo_timer_pending(&trx->ts[2].lchan[0].ho.t3105))
fprintf(stderr, "test 2: with timeout\n");
/* enable handover again */
lchan = &trx->ts[2].lchan[0];
lchan->ho.active = HANDOVER_ENABLED;
lchan->ho.ref = 23;
modify_count = 0;
/* send access burst with correct ref */
nl1sap.u.rach_ind.ra = 23;
l1sap_up(trx, &nl1sap);
OSMO_ASSERT(modify_count == 1);
/* expect PHYS INFO */
expect_phys_info(&trx->ts[2].lchan[0].lapdm_ch.lapdm_dcch);
/* expect exactly one HO.DET */
OSMO_ASSERT(msg = msgb_dequeue(&trx->rsl_link->tx_list));
rslh = msgb_l2(msg);
OSMO_ASSERT(rslh->c.msg_type == RSL_MT_HANDO_DET);
OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list));
for (i = 0; i < btsb->ny1 - 1; i++) {
/* expect T3105 running */
OSMO_ASSERT(osmo_timer_pending(&trx->ts[2].lchan[0].ho.t3105))
/* timeout T3105 */
gettimeofday(&trx->ts[2].lchan[0].ho.t3105.timeout, NULL);
osmo_select_main(0);
/* expect PHYS INFO */
expect_phys_info(&trx->ts[2].lchan[0].lapdm_ch.lapdm_dcch);
}
/* timeout T3105 */
gettimeofday(&trx->ts[2].lchan[0].ho.t3105.timeout, NULL);
osmo_select_main(0);
/* expect T3105 not running */
OSMO_ASSERT(!osmo_timer_pending(&trx->ts[2].lchan[0].ho.t3105))
/* expect exactly one CONN.FAIL */
OSMO_ASSERT(msg = msgb_dequeue(&trx->rsl_link->tx_list));
rslh = msgb_l2(msg);
OSMO_ASSERT(rslh->c.msg_type == RSL_MT_CONN_FAIL);
OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list));
#if 0
while (!quit) {
log_reset_context();
osmo_select_main(0);
}
#endif
printf("Success\n");
return 0;
}
void bts_model_abis_close(struct gsm_bts *bts)
{
}
int bts_model_oml_estab(struct gsm_bts *bts)
{
return 0;
}
int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
{
int rc = 0;
uint8_t chan_nr;
uint8_t tn, ss;
struct gsm_lchan *lchan;
struct msgb *msg = l1sap->oph.msg;
struct osmo_phsap_prim nl1sap;
switch (OSMO_PRIM_HDR(&l1sap->oph)) {
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST):
switch (l1sap->u.info.type) {
case PRIM_INFO_ACTIVATE:
chan_nr = l1sap->u.info.u.act_req.chan_nr;
tn = L1SAP_CHAN2TS(chan_nr);
ss = l1sap_chan2ss(chan_nr);
lchan = &trx->ts[tn].lchan[ss];
lchan_init_lapdm(lchan);
lchan_set_state(lchan, LCHAN_S_ACTIVE);
memset(&nl1sap, 0, sizeof(nl1sap));
osmo_prim_init(&nl1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_CONFIRM, NULL);
nl1sap.u.info.type = PRIM_INFO_ACTIVATE;
nl1sap.u.info.u.act_cnf.chan_nr = chan_nr;
return l1sap_up(trx, &nl1sap);
case PRIM_INFO_MODIFY:
modify_count++;
break;
default:
LOGP(DL1C, LOGL_NOTICE, "unknown MPH-INFO.req %d\n",
l1sap->u.info.type);
rc = -EINVAL;
goto done;
}
break;
default:
LOGP(DL1C, LOGL_NOTICE, "unknown prim %d op %d\n",
l1sap->oph.primitive, l1sap->oph.operation);
rc = -EINVAL;
goto done;
}
done:
if (msg)
msgb_free(msg);
return rc;
}
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type, struct tlv_parsed *old_attr, struct tlv_parsed *new_attr, void *obj) { return 0; }
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg, struct tlv_parsed *new_attr, int obj_kind, void *obj) { return 0; }
int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo, void *obj) { return 0; }
int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo, void *obj, uint8_t adm_state) { return 0; }
int bts_model_init(struct gsm_bts *bts) { return 0; }
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx) { return 0; }
int bts_model_trx_close(struct gsm_bts_trx *trx) { return 0; }
void trx_get_hlayer1(void) {}
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan) { return 0; }

View File

@@ -0,0 +1 @@
Success

View File

@@ -78,7 +78,7 @@ static void test_msg_utils_ipa(void)
memcpy(msg->l1h, ipa_rsl_connect, sizeof(ipa_rsl_connect));
msg->l1h[2] = 0x23;
rc = msg_verify_ipa_structure(msg);
OSMO_ASSERT(rc == -1);
OSMO_ASSERT(rc == 0);
msgb_free(msg);
}

View File

@@ -40,6 +40,8 @@ int bts_model_rsl_chan_mod(struct gsm_lchan *lchan)
{ return 0; }
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
unsigned int rtp_pl_len) {}
int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
{ return 0; }
int l1if_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
@@ -53,3 +55,10 @@ int bts_model_oml_estab(struct gsm_bts *bts)
int l1if_set_txpower(struct femtol1_hdl *fl1h, float tx_power)
{ return 0; }
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan)
{ return 0; }
void bts_model_abis_close(struct gsm_bts *bts)
{ }

View File

@@ -18,6 +18,8 @@
*/
#include <osmo-bts/bts.h>
#include <osmo-bts/l1sap.h>
#include <osmo-bts/power_control.h>
#include "femtobts.h"
#include "l1_if.h"
@@ -169,14 +171,77 @@ static void test_sysmobts_cipher(void)
/* Handle message sent before ciphering was received */
memcpy(&unit.u8Buffer[0], too_early_classmark, ARRAY_SIZE(too_early_classmark));
unit.u8Size = ARRAY_SIZE(too_early_classmark);
bts_check_for_first_ciphrd(&fl1h, &unit, &lchan);
rc = bts_check_for_first_ciphrd(&lchan, unit.u8Buffer, unit.u8Size);
OSMO_ASSERT(rc == 0);
OSMO_ASSERT(lchan.ciph_state == LCHAN_CIPH_RX_CONF);
/* Now send the first ciphered message */
memcpy(&unit.u8Buffer[0], first_ciphered_cipher_cmpl, ARRAY_SIZE(first_ciphered_cipher_cmpl));
unit.u8Size = ARRAY_SIZE(first_ciphered_cipher_cmpl);
bts_check_for_first_ciphrd(&fl1h, &unit, &lchan);
OSMO_ASSERT(lchan.ciph_state == LCHAN_CIPH_TXRX_REQ);
rc = bts_check_for_first_ciphrd(&lchan, unit.u8Buffer, unit.u8Size);
OSMO_ASSERT(rc == 1);
/* we cannot test for lchan.ciph_state == * LCHAN_CIPH_RX_CONF_TX_REQ, as
* this happens asynchronously on the other side of the l1sap queue */
}
static void test_sysmobts_loop(void)
{
struct gsm_bts bts;
struct gsm_bts_role_bts btsb;
struct gsm_bts_trx trx;
struct gsm_bts_trx_ts ts;
struct gsm_lchan *lchan;
int ret;
memset(&bts, 0, sizeof(bts));
memset(&btsb, 0, sizeof(btsb));
memset(&trx, 0, sizeof(trx));
memset(&ts, 0, sizeof(ts));
lchan = &ts.lchan[0];
lchan->ts = &ts;
ts.trx = &trx;
trx.bts = &bts;
bts.role = &btsb;
bts.band = GSM_BAND_1800;
trx.ms_power_control = 1;
btsb.ul_power_target = -75;
printf("Testing sysmobts power control\n");
/* Simply clamping */
lchan->state = LCHAN_S_NONE;
lchan->ms_power_ctrl.current = ms_pwr_ctl_lvl(GSM_BAND_1800, 0);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -60);
OSMO_ASSERT(ret == 0);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
/*
* Now 15 dB too little and we should power it up. Could be a
* power level of 7 or 8 for 15 dBm
*/
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -90);
OSMO_ASSERT(ret == 1);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 7);
/* It should be clamped to level 0 and 30 dBm */
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -100);
OSMO_ASSERT(ret == 1);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 0);
/* Fix it and jump down */
lchan->ms_power_ctrl.fixed = 1;
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -60);
OSMO_ASSERT(ret == 0);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 0);
/* And leave it again */
lchan->ms_power_ctrl.fixed = 0;
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -40);
OSMO_ASSERT(ret == 1);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
}
int main(int argc, char **argv)
@@ -184,6 +249,7 @@ int main(int argc, char **argv)
printf("Testing sysmobts routines\n");
test_sysmobts_auto_band();
test_sysmobts_cipher();
test_sysmobts_loop();
return 0;
}
@@ -199,3 +265,5 @@ int bts_model_init(struct gsm_bts *bts)
{ return 0; }
int bts_model_oml_estab(struct gsm_bts *bts)
{ return 0; }
void bts_model_abis_close(struct gsm_bts *bts)
{ }

View File

@@ -17,3 +17,4 @@ Checking PCS to PCS
PCS to PCS band(1) arfcn(512) want(3) got(3)
PCS to PCS band(8) arfcn(128) want(0) got(0)
PCS to PCS band(2) arfcn(438) want(-1) got(-1)
Testing sysmobts power control

View File

@@ -24,3 +24,15 @@ AT_KEYWORDS([misc])
cat $abs_srcdir/misc/misc_test.ok > expout
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/misc/misc_test], [], [expout], [ignore])
AT_CLEANUP
AT_SETUP([bursts])
AT_KEYWORDS([bursts])
cat $abs_srcdir/bursts/bursts_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/bursts/bursts_test], [], [expout], [ignore])
AT_CLEANUP
AT_SETUP([handover])
AT_KEYWORDS([handover])
cat $abs_srcdir/handover/handover_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/handover/handover_test], [], [expout], [ignore])
AT_CLEANUP