Compare commits

...

542 Commits

Author SHA1 Message Date
Harald Welte
c328371b54 cbch: add debug statements 2014-12-30 00:24:52 +01:00
Harald Welte
ffcdd0da7e 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:24:45 +01:00
Harald Welte
265d0635f2 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:24:40 +01:00
Harald Welte
03483cf0f2 etws_p1: Add missing 'Group Call Information' to P1 Rest Octets 2014-12-27 23:43:09 +01:00
Holger Hans Peter Freyther
7f12860f08 wip... encoding and stuff 2014-12-27 23:05:51 +01:00
Holger Hans Peter Freyther
96efd189cc wip... 2014-12-27 19:33:23 +01:00
Holger Hans Peter Freyther
1f27fda194 wip... handle 2014-12-27 19:05:09 +01:00
Holger Hans Peter Freyther
5a906c7b72 sysmobts: WIP: allow to use the serial number as unit id 2014-12-26 16:27:36 +01:00
Holger Hans Peter Freyther
50dc96507c sysmobts: Include the serial number in the find response
Read the serial number once and format it as a string. In
case no serial number is present -1 will be returned.

Manually tested with a slightly modified version. serial_nr
was the expected one.
2014-12-26 01:46:24 +01:00
Holger Hans Peter Freyther
c265bef48c sysmobts: Add slave on/off action for the sysmoBTS2050
Add new power actions for the sysmoBTS2050. This allows to
switch off the secondary/slave when the system temperature
is too high and back on when the normal level is reached.

Do not allow to switch off the master (so remove the enum
value), do not check if the slave is switching itself off.
2014-12-16 20:22:28 +01:00
Holger Hans Peter Freyther
02a2afa962 sysmobts: Comment out the varpoware options that are not implemented 2014-12-16 20:22:28 +01:00
Holger Hans Peter Freyther
ffc193443c sysmobts: Add "normal" actions to execute
Instead of keeping state to remember what was done and needs
to be undone this patch introduces actions that will be executed
when the system is back to normal.

By design the system is considered to be in the normal state
and these actions will be only executed after the system is
coming back to the normal state.

One advantage of this scheme is that an operator can decide
that an overheated systems hould be off duty and requires manual
interaction to be allowed back in service.

The change has only been smoke tested

Fixes: SYS#833
2014-12-16 20:22:28 +01:00
Holger Hans Peter Freyther
8968b48643 sysmobts: Remove unused global variables
We do not need to have these variables anymore. Just remove them.
2014-12-16 15:12:56 +01:00
Holger Hans Peter Freyther
641a934931 sysmobts: Read the clock calibration from another place
Read the clock calibration from the place that will be read by
the BTS process. Use the standard eeprom code for doing that.
The code assumes that this and the other eeprom code don't
write/invlidate the others reason. If that assumption would not
be true calls to eeprom_free_resources should be added.
2014-12-16 15:12:55 +01:00
Holger Hans Peter Freyther
69897d7eed sysmobts: Don't list non integer parameters in the help
The command can only read integer parameters. Don't offer
buffers as this will lead to error 22.
2014-12-16 15:09:22 +01:00
Holger Hans Peter Freyther
0d09e75f9c eeprom: Fix brown paper bag introduced a long while ago
91d204e2db while adding checks
to resolve coverity issues. We simply had no one writing to
the eeprom so this was unnoticed for a long time.
2014-12-16 15:09:22 +01:00
Holger Hans Peter Freyther
42cc96e2c1 sysmobts: Add an option to stop the systemd sysmobts.service
For systems without direct access to the PA the best option
is to simply switch off the bts service. This will stop the
transmission which will take load from the DSP/FPGA/RF circuit
and indirectly from the PA as well.

We should introduce "pa-on and bts-on" that can be executed
as "normal" action.
2014-12-12 14:53:23 +01:00
Holger Hans Peter Freyther
8381a6a483 sysmobts: Actions can be executed in all levels
Somebody could decide to switch off the PA in the warning level
already. Support this mode of operation. This means we could have
a config that:

* Enables the PA in the normal level
* Disables it in the critical level

With kdbus or better IPC we could even have the PA and other
parts be represented as service that talk to a bts manager and
then simply execute start/stop requests. This would make the
entire TODO entry irrelevant as state would be managed by
systemd and one can see the time the service was executed.
2014-12-12 14:40:25 +01:00
Holger Hans Peter Freyther
4d4dc26742 bts: Move BTS and children into the enabled state after opstart
With "show bts 0" all objects were still listed as dependency.
Once the BTS has been started.. move all the other objects into
the enabled state. Our OpenBSC OML code doesn't care but people
using the VTY to inspect state will be more happy.

One day... we will create proper statemachines inside the BTS
and the BSC instead of changing the state in the BTS impl.

Fixes: ONW#1330
2014-11-10 15:16:16 +01:00
Holger Hans Peter Freyther
48eb374a96 bts: Start with the site manager being enabled and available
We would never transition the sitemanager to anything. Our SW
does not support SW activation's so we are always ready.

Related: ONW#1330
2014-11-10 15:12:38 +01:00
Holger Hans Peter Freyther
2fa6ef2687 bts: Mark NSVC1 as offline. We do not expose a second NSVC
OsmoBSC> show bts 0
  ...
  GPRS NSVC1: Oper 'Disabled', Admin 'unknown 0x0', Avail 'Off line'
2014-11-10 13:18:59 +01:00
Holger Hans Peter Freyther
6d8bcbd192 bts: Fix typo in OML comment 2014-11-10 13:01:33 +01:00
Holger Hans Peter Freyther
b89a5fa55d bts: In case the line isn't created do not exit with code 1
The service file will prevent a re-launch of the sysmobts.service
in case the main process exits with '1'. In case the ethernet is
not available yet the routine would fail and the sysmobts process
will not be restarted.

unable to connect/bind socket: Network is unreachable
<000f> input/ipaccess.c:885 cannot open OML BTS link: Network is unreachable
unable to connect to BSC
root@sysmobts-v2:~# echo $?
2

Fixes: SYS#736
2014-11-10 12:53:22 +01:00
Holger Hans Peter Freyther
9f0002b52b ctrl: Fix compiler warning
bts_ctrl_lookup.c: In function 'bts_controlif_setup':
bts_ctrl_lookup.c:97:2: warning: implicit declaration of function 'bts_ctrl_cmds_install' [-Wimplicit-function-declaration]
  rc = bts_ctrl_cmds_install(bts);
  ^
2014-11-10 12:04:48 +01:00
Holger Hans Peter Freyther
1cce69364d abis: Fix compiler warning and remove const from syntax
libosmo-abis doesn't make it easy to have these parameters
as const.. just declare it non-const in the api. We pass
a static string but we know it will not be modified.
2014-11-10 12:02:45 +01:00
Holger Hans Peter Freyther
8332e29c9f tch: Avoid compiler warnings when using the direct RTP mode
tch.c: In function 'l1_to_rtppayload_amr':
tch.c:247:29: warning: unused variable 'amr_mrc' [-Wunused-variable]
  struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr;
                             ^
tch.c: In function 'rtppayload_to_l1_amr':
tch.c:335:10: warning: unused variable 'amr_if2_core_len' [-Wunused-variable]
  uint8_t amr_if2_core_len = payload_len - 2;
2014-11-10 11:59:53 +01:00
Holger Hans Peter Freyther
be63f03254 sysmobts: Call the routine send and receive instead of get 2014-11-10 11:58:21 +01:00
Andreas Eversberg
9f22fcfd36 Correctly fill system information messages from BSC
SI 5*/6 require L2 header of 0x03,0x03. All SI might be less than 23
octets, so they need to be filled with 0x2b.
2014-08-27 23:45:47 +02:00
Harald Welte
11b14fd662 tx_power: Check actual TRX output power against intended value
We use the completion call-back from L1 to compare the instructed
TRX board output power with the actual value as reported  back from
L1.

Right now we only print an error message in case the values disagree.

In the future we might want to either use that value as part of our
calculation or send an OML alarm report to the BSC.
2014-08-25 19:01:24 +02:00
Harald Welte
9e873335ec Revert "add nominal transmit power for upcoming sysmoBTS 1020 and 1100"
This reverts commit d0866fe477, as it
was a bit premature.  We need to address this more properly.
2014-08-25 18:06:29 +02:00
Holger Hans Peter Freyther
04585dd90a tx: Generate a working configuration file on "write"
We do not have the vty test script to do roundtrip testing. There
is no max-initinal-pout, then there was a typo inside 'initial' and
for the relative step size the unit is dB/mdB. Fix both of that.
2014-08-25 16:06:56 +02:00
Holger Hans Peter Freyther
645cba8532 tx: Fix another typo i found today morning 2014-08-25 16:01:29 +02:00
Holger Hans Peter Freyther
adddb65f46 sysmobts: Remove left-over from non-generic power control 2014-08-25 16:01:23 +02:00
Holger Hans Peter Freyther
fbf04438b7 ctrl: Use msgb_free to free message 2014-08-25 16:01:23 +02:00
Harald Welte
e0e9b30f5c tx_power.c: fix potential array out-of-bounds access 2014-08-25 10:05:55 +02:00
Harald Welte
68b9b376cf make use of libosmocore 'gsm_meas_rep_unidir'
Back in March 2013, some structures and defines related to decoded
measurement reports have been moved from openbsc to libosmocore
(libosmocore e128f4663104ed64e33e362cff2566f36d65e658) so that they can
be used also from osmo-bts.  This finally follows up on that.

You need openbsc 7ff4f0e0fc692bfab829da50edb104e58b271e7e or later.
2014-08-25 09:22:00 +02:00
Harald Welte
d0866fe477 add nominal transmit power for upcoming sysmoBTS 1020 and 1100 2014-08-25 08:48:37 +02:00
Harald Welte
913044ecc5 tx_power.c: Fix typos in comments 2014-08-25 08:46:48 +02:00
Harald Welte
3dd6ebe0b8 add information on unit (milli-dB) to control interface doc 2014-08-25 08:42:08 +02:00
Harald Welte
1fb66c8e6a disable clock control interface on HW_SYSMOBTS_V1 2014-08-24 18:16:45 +02:00
Harald Welte
ab09e27d72 add control_if.h to Makefile.am 2014-08-24 17:42:47 +02:00
Harald Welte
c0a3030277 add a small document describing the use of the control interface 2014-08-24 17:19:59 +02:00
Harald Welte
be18984959 ctrl: Add sysmobts control interface
This sysmobts specific control interface allows for clock calibration
from an external program by means of the "trx.0.clock-info" and
"trx.0.clock-correction" values.
2014-08-24 17:07:02 +02:00
Harald Welte
9d0fd073e9 l1_if: pass private 'void *data' from call to callback
When enqueueing a command towards the L1, we can now pass along
a private data pointer, which then gets passed to the call-back
upon completion.
2014-08-24 17:05:23 +02:00
Harald Welte
8e4cc1cbb8 fix build problem introduced with control interface 2014-08-24 17:04:54 +02:00
Harald Welte
993575bcd8 use libocmocore #defines for VTY port numbers 2014-08-24 16:59:07 +02:00
Harald Welte
0ff0f2d00f ctrl_if: Move control interface to port 4238
... which is now defined in libosmocore
2014-08-24 16:59:05 +02:00
Harald Welte
d9a2aa8d99 add control interface to common BTS (for thermal attenuation)
Using this control interface, an external program can request
attentuation of the transmitter for thermal management reasons. The
external application doesn't have to know anthing about the actual
transmit power, but it can just configure a certian value of milli-dB
(1/10000 bel) and update (increase/decrease) that value depending on
the thermal environment.
2014-08-24 16:42:02 +02:00
Harald Welte
e43feaf231 New generic transmit power handling
In order to support transmit power reduction by thermal management
as well as the variety of new internal / external PA configurations
of BTSs, we need a slightly more complex system.

Also, as at high power a single dB can be quite a big difference,
we are now doing all computations in milli-dB(m), i.e. 1/10000 bel.

Ramping is now used both for up and down ramping, as that is useful in
cases where you want to gracefully shut down a cell by shrinking its
radius, gradually handing over subscribers to neighboring cells.

Furthermore, this code is becoming part of the 'common' codebase, as it
is not really specific to how sysmobts is working.

The user can specify a single aggregate value for external system
gain/attenuation.  Let's say you have 1dB loss of antenna cable, so you
can put that as 'user-gain -1' into the config, which means that a
'transmit power of 20dBm' will be compensatet for that and the TRX is
instructed to output 21dBm to compensate the cable loss.  Similarly,
external PAs can be described by a positive user-gain.

One of the next steps will be to communicate those values and the
nominal power capability of the specific BTS to the BSC, so the BSC will
automatically show correct signal levels in the VTY and log files.

The code includes provisions for future extensions regarding
* an external and an internal PA with calibration tables
* a thermal attenuation setting to be controlled by the site manager
2014-08-24 10:46:21 +02:00
Harald Welte
fcca2e8218 remove copy of gsm_bts_num()
... which is now available from gsm_data_shared.[ch] of openbsc
2014-08-24 10:46:21 +02:00
Holger Hans Peter Freyther
15f899f89e sysmobts: Use the uc connection on both slave and master
We can use this on both slave and master. But only have the
master switch on the PA.
2014-08-22 00:06:01 +02:00
Holger Hans Peter Freyther
3a54b7aa30 Merge branch 'sysmocom/features/sysmobts-mgr-temp'
Implement the first round of temperature control and actions. Only
the PA can be switched off, it will never be switched on again, in
case the microcontroller doesn't respond we will do nothing as well.
These todos need to be addressed in the near future.
2014-08-21 23:55:37 +02:00
Holger Hans Peter Freyther
1f8053e366 sysmobts: Enable the PA on start and disable it as first action
The PA will be unconditionally turned. This makes it possible
that in case of a crash, the PA will be turned on and then we
will do the temperature measurement and turn it off again. There
are no known crashes with the sysmobts-mgr right now so the risk
seems to be okay. In case we can't switch off the PA we have no
way to escalate it right now. We have not seen a dead uc either
so the risk is okay as well.

We can't switch the PA back on once we reach the normal level
as the BTS might transmit with full power and we would need more
current than the power supply/rails can carry. So leave the
system off right now.

What is missing is to use the OML router to actually inform
the BSC that something bad has happened at the BTS.
2014-08-21 23:55:27 +02:00
Holger Hans Peter Freyther
e02d7796c3 sysmobts: Show the current temperature controls state int he VTY 2014-08-21 23:55:27 +02:00
Holger Hans Peter Freyther
714ccb9992 sysmobts: Provide information about the state transitions 2014-08-21 23:55:27 +02:00
Holger Hans Peter Freyther
b0674e9636 sysmobts: Implement a small state machine for temp control
Check the temperature and move between "NORMAL", "WARNING"
and "CRITICAL" state. We will only return from CRITICAL to
WARNING when the temperature has significantly changed, and
when being in state "WARNING" we enter an intermediate state
to allow an easy hysteris.
2014-08-21 23:55:27 +02:00
Holger Hans Peter Freyther
d036cce744 sysmobts: Remove the sbt2050 timer and move defines back
We haven't done anything with the result of the micro controller
query and querying every six hours for the temperature of the
system will not help us. We need to query the temperatures more
frequently but avoid writing to the eeprom too frequently so we
will start another timer for that.
2014-08-21 23:55:27 +02:00
Holger Hans Peter Freyther
c42bf5fdf5 sysmobts: Simplify some includes/dependencies 2014-08-21 23:55:27 +02:00
Holger Hans Peter Freyther
ca71d07e44 sysmobts: Begin to add various limits and actions
The idea is that for different parts of the system we can define
thresholds for warning and critical (severe) temperate thresholds.
And once any of these temperatures is reached we will execute an
action. When crossing from NORMAL to WARNING or WARNING to SEVERE
we will need to apply some hysteris before switching back to the
lower level. E.g. when being SEVERE mode, at least wait until we
are below the warning level again. Besides being able to switch
off things we could start reducing the transmit power of the system
until the system is cold enough again.

No action is implemented so far, everything is varpoware!
2014-08-21 23:55:27 +02:00
Holger Hans Peter Freyther
c7ee5acba9 sysmobts: Fix the temperature log message alignment 2014-08-21 23:55:27 +02:00
Holger Hans Peter Freyther
9698570d47 sysmobts: Move ipaccess-find counterpart to a dedicated source file 2014-08-21 23:55:27 +02:00
Holger Hans Peter Freyther
7be58a173a sysmobts: Fix the build when no 2050 uc header file was found
Fix the build (provide empty stubs) when the header file is not
present.
2014-08-21 18:03:25 +02:00
Holger Hans Peter Freyther
631945a367 Merge branch 'sysmocom/features/sysmobts-mgr'
Add some VTY code to show the temperature on all devices and to
query the external micro controller for voltage/current and the
temperature in the "show manager" command. It should probably be
a "show system" command though.
2014-08-21 16:25:22 +02:00
Holger Hans Peter Freyther
5e1363071f sysmobts: Fix the power request result
We want to know which componets are enabled and the voltage and
current used by the components.
2014-08-21 16:25:07 +02:00
Holger Hans Peter Freyther
4059e29a29 sysmobts: Read the temperature sensors on the device
Read the sensors that are always present and the ones that
are only present on the sysmoBTS 2050.
2014-08-21 16:25:07 +02:00
Holger Hans Peter Freyther
46c085d794 sysmobts: Add is_sbts2050_master 2014-08-21 16:25:07 +02:00
Holger Hans Peter Freyther
b1ceb40363 sysmobts: Read the model number and trx once from the device
Use it for the ipaccess-find response and for the sysmobts
classification code. This can be used by the vty in a second.
2014-08-21 16:25:07 +02:00
Holger Hans Peter Freyther
3ecb2bb604 sysmobts: Initialize fd with an invalid fd
Initialize the ucinfo with an invalid fd to prevent writing
on fd=0 by accident.
2014-08-21 16:25:07 +02:00
Holger Hans Peter Freyther
ffe1d2e1e0 Merge commit 'sysmocom/features/sysmobts-mgr-vty'
Some re-factorings. Still a very long way to go. It should
work with haralds re-based but that wasn't verified due my
toolchain not having the most recent libosmocore. The service
file and screenrc change has not been verified either.
2014-08-21 15:50:40 +02:00
Holger Hans Peter Freyther
575f633483 sysmobts: Use another logp region as it is mostly related to remp 2014-08-21 15:49:06 +02:00
Holger Hans Peter Freyther
54a8b313b4 sysmobts: There is only one uc make it a singleton
Move the init and polling into the sysmoBTS related part. In the
future we should have _one_ temperature control.
2014-08-21 15:49:06 +02:00
Holger Hans Peter Freyther
c84ca8c82f sysmobts: Clean-up the parsing routines 2014-08-21 15:49:06 +02:00
Holger Hans Peter Freyther
035187b44e sysmobts: Move the sysmoBTS 2050 controller handling
Move the code to a separate file to keep things nicely apart
of each other.
2014-08-21 15:49:06 +02:00
Holger Hans Peter Freyther
013df51ca8 sysmobts: Add VTY support to the sysmobts-mgr
Add VTY support to the manager. This way we can interactively
inspect the state of the system and trigger events.
2014-08-21 15:49:06 +02:00
Harald Welte
07198750b2 adopt to recent libosmocore ipa rename 2014-08-20 23:30:28 +02:00
Harald Welte
ac76388c77 TLVP_PRES_LEN is now in libosmocore, avoid redefining it 2014-08-18 20:48:56 +02:00
Harald Welte
bc82b0189a replace oml_{osmo,ipa}_magic[] with abis_nm_{osmo,ipa}_magic
the latter is now in libosmogsm.
2014-08-18 20:48:56 +02:00
Harald Welte
fcd5c367d1 Migrate to osmo_get_macaddr() in recent libosmocore
get_mac_addr() is generally useful and shouldn't be hidden in
the osmo-bts/abis.c file
2014-08-18 20:48:56 +02:00
Holger Hans Peter Freyther
88d60a1f86 sysmobts: Add a unit test that checks of the behavior
We need to build a lot more code to be able to test these two
new routines. I didn't want to move the code to a utils file
as the check is called from a hot path. Add accessors to the
inlined variant to be used by the unit test.

While writing the unit tests I noticed that a re-transmission
of the ciphering command would lead to an attempt to enable
ciphering again. I am not sure that this MphConfig is idempotent.
2014-08-09 09:42:56 +02:00
Holger Hans Peter Freyther
2cc37035d7 sysmobts: Deal with ciphering when we have a transport clash
The network is configured with early classmark sending. This means
that the phone might send a "classmark change" message at the same
time we send a ciphering mode command. When we received the CM
message we assumed we have just received the first ciphered message
and enabled ciphering for tx as well.

When we snoop the Ciphering Mode Command extract the N(S) variable
and when we receive an I frame from the MS see if it handled our
message by comparing the MS N(R) to BTS N(S) + 1.
2014-08-09 09:42:56 +02:00
Holger Hans Peter Freyther
6cf49380cd Merge branch 'sysmocom/features/oml-router' 2014-07-31 17:49:08 +02:00
Holger Hans Peter Freyther
caca1899ce sysmobts: Make sure that the omlrouter is in a FHS path
I wondered if I should use the 'abstract namespace' feature
of Linux but just put the router into /var/run/ to make it
work out of the box. Change the signature to provide a sane
error message.
2014-07-31 17:43:08 +02:00
Holger Hans Peter Freyther
fcdfb690ca sysmobts: Add testcase for ETSI/12.21 message 2014-07-31 16:59:06 +02:00
Holger Hans Peter Freyther
dbc2731887 sysmobts: Extend the testcase for a Osmo message as well 2014-07-31 16:58:48 +02:00
Holger Hans Peter Freyther
0655cac6f1 sysmobts: Verify the structure of IPA and OML messages
Extend the router to verify that the message received is
properly encoded. The code can deal with the basic structure
of ETSI OML and vendor specific messages for ip.access and
the osmocom project.
2014-07-31 16:58:26 +02:00
Holger Hans Peter Freyther
9e1dbf532e sysmobts: Remove debug left over from enabling the RTP mode 2014-07-31 16:57:39 +02:00
Holger Hans Peter Freyther
b05d72d21b sysmobts: Begin with an OML router that will be used by the manager
Begin with the basics of a OML Router. This is currently only
capable of accepting a connection and read messages but it will
evolve into a router in multiple stages. The first usage will
be by the sysmobts-mgr. An OML Error Indication will be sent by
the sysmobts-mgr and it will be forwarded to the BSC. In the
second step we will set a relative power reduction from the
sysmobts-mgr.

In the long-term this code will be used to communicate with a
second TRX.
2014-07-31 16:57:39 +02:00
Holger Hans Peter Freyther
b3d1779b96 tests: Move the "pcu_direct" symbol into the stubs to be shared 2014-07-31 14:55:03 +02:00
Holger Hans Peter Freyther
bc8de14671 oml: Make it possible to include the file directly
Fixes:
../../include/osmo-bts/oml.h:8:42: warning: ‘struct gsm_bts’ declared inside parameter list [enabled by default]
 int down_oml(struct gsm_bts *bts, struct msgb *msg);
                                          ^
../../include/osmo-bts/oml.h:8:42: warning: its scope is only this definition or declaration, which is probably not what you want [enabled by default]
../../include/osmo-bts/oml.h:12:52: warning: ‘struct gsm_abis_mo’ declared inside parameter list [enabled by default]
 int oml_mo_send_msg(struct gsm_abis_mo *mo, struct msgb *msg, uint8_t msg_type);
                                                    ^
../../include/osmo-bts/oml.h:13:31: warning: ‘struct gsm_abis_mo’ declared inside parameter list [enabled by default]
 int oml_mo_opstart_ack(struct gsm_abis_mo *mo);
                               ^
../../include/osmo-bts/oml.h:14:32: warning: ‘struct gsm_abis_mo’ declared inside parameter list [enabled by default]
 int oml_mo_opstart_nack(struct gsm_abis_mo *mo, uint8_t nack_cause);
                                ^
../../include/osmo-bts/oml.h:15:32: warning: ‘struct gsm_abis_mo’ declared inside parameter list [enabled by default]
 int oml_mo_statechg_ack(struct gsm_abis_mo *mo);
                                ^
../../include/osmo-bts/oml.h:16:33: warning: ‘struct gsm_abis_mo’ declared inside parameter list [enabled by default]
 int oml_mo_statechg_nack(struct gsm_abis_mo *mo, uint8_t nack_cause);
                                 ^
../../include/osmo-bts/oml.h:19:29: warning: ‘struct gsm_abis_mo’ declared inside parameter list [enabled by default]
 int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state);
                             ^
../../include/osmo-bts/oml.h:22:31: warning: ‘struct gsm_abis_mo’ declared inside parameter list [enabled by default]
 void oml_mo_state_init(struct gsm_abis_mo *mo, int op_state, int avail_state);
                               ^
../../include/osmo-bts/oml.h:26:10: warning: ‘struct gsm_abis_mo’ declared inside parameter list [enabled by default]
          int success);
          ^
../../include/osmo-bts/oml.h:29:33: warning: ‘struct gsm_abis_mo’ declared inside parameter list [enabled by default]
 int oml_tx_state_changed(struct gsm_abis_mo *mo);
                                 ^
../../include/osmo-bts/oml.h:31:33: warning: ‘struct gsm_abis_mo’ declared inside parameter list [enabled by default]
 int oml_mo_tx_sw_act_rep(struct gsm_abis_mo *mo);
                                 ^
../../include/osmo-bts/oml.h:36:4: warning: ‘struct gsm_abis_mo’ declared inside parameter list [enabled by default]
    uint8_t cause);
2014-07-31 14:54:51 +02:00
Holger Hans Peter Freyther
a19912db34 sysmobts: Enable the direct RTP mode for firmware >= 3.11
We need to patch the CMR due wanting to support systems that still
have Audiocodes hardware in their chain. I have manually tested
and could listen to my own voice on:

TCH/H	&	AMR 5.9		& PTSN			& BSC
TCH/F	&	FR1		& Other subscriber	& NITB
TCH/F	&	EFR		& Other subscriber	& NITB
TCH/H	&	HR1		& Other subscriber	& NITB
TCH/H	&	AMR 5.9		& Other subscriber	& NITB

The tests were done using the Nokia E71, a Blackberry curve and
for the PTSN a HTC 8S were used.
2014-07-31 14:39:32 +02:00
Holger Hans Peter Freyther
eececf5fa9 sysmobts: Make it possible to slowly ramp up the output power
For systems with a bigger PA enabling the full output power at
once might draw more current than a power supply can provide. This
code will step up the output power in smaller steps to avoid this
situation.
2014-07-30 18:22:12 +02:00
Holger Hans Peter Freyther
bc24955e91 sysmobts: Provide VTY routines to do clock calibrations
The sysmoBTS2050 does not have a OCXO and we should not rely
on the GPS module to always have a fix. Instead use the TCXO
by default and from time to time (and we know we have a fix
calibrate the TCXO). This can be done by:

  trx 0 rf-clock-info reset
  wait...
  trx 0 rf-clock-info correct
  write

The output is currently only written to the log as the VTY
connection might go away during the operation. The reset will
set the approriate reference clock and the correct will attempt
to determine and apply the correction. The write terminal will
make sure that next on start a known good value will be used.
2014-07-25 14:45:52 +02:00
Holger Hans Peter Freyther
bac0ff7f6d sysmobts: Free the message on older firmware releases
Seen while implementing a new functionality in the code.
2014-07-25 13:23:00 +02:00
Holger Hans Peter Freyther
94a63851b7 sysmobts: Include the model and master/slave in the unitid
Make it more easy to find the right BTS model and know what is
the master/slave.
2014-07-25 10:48:14 +02:00
Holger Hans Peter Freyther
3674645e20 amr: Avoid toggling the CMR from none and a set one
For LCR and other systems without out-of-band information we need
to indicate the CMR. Not every air message will include the mode
and we sent a stream that had the CMR set and not-set. This lead
to the AudioCodes MGW only playing every second frame.

Remember the last used mode and initialize it to _NONE when we
receive the multirate config. In case of a real error we will
still use AMR_CMR_NONE.

The initial patch is from Harald. I have added the initialization
and moving of the defines to amr.h.

Manually verified by enabling AMR5.9 and looking at two RTP
packages in sequence. In both cases the CMR was 2. I have looked
at "amr.nb.cmr != 2" in wireshark and only found the MGCP dummy
packet.
2014-07-25 09:22:29 +02:00
Holger Hans Peter Freyther
a2b806c375 sysmobts: Fix typo in the comment 2014-06-22 16:23:59 +02:00
Holger Hans Peter Freyther
a7f9b58e44 sysmobts: Fix the initialization of the BTS manager code
The code should only run for the sysmoBTS 2050 and TRX 0.
If the device is not marked as 2050 the code would attempt
to open /dev/ttyS0 and block forever.
2014-05-30 15:29:01 +02:00
Holger Hans Peter Freyther
0e2b624418 sysmobts: Revert all sysmobts-mgr related changes
Harald is right and that the code is generally not ready
for inclusion. I fell victim of trying to finish it while
the code is not ready at all. It is better to re-introduce
the patches in a smaller and more tested way.

The right way would have been a branch were ready things
are split-off the main/wip commit until everything is ready.

Revert "sysmobts: Have a common prefix for the enum"
This reverts commit 44980347f3.

Revert "utils: Used the enum manuf_type_id in the parameter of add_manufacturer_id_label"
This reverts commit 7d36e5ed46.

Revert "utils: Classify the OML message using the return type"
This reverts commit afee0b7929.

Revert "sysmobts: Do not access out of bound string"
This reverts commit f5f41e8051.

Revert "sysmobts: Separate IPA and OML check into two methods"
This reverts commit 13a224063d.

Revert "screenrc: osmobts-mgr now needs a config file"
This reverts commit 0a1699ff8a.

Revert "make sure osmobts-mgr.cfg file is included in tarballs"
This reverts commit 14c60b425f.

Revert "sysmobts-mgr: Add VTY support for configuring it"
This reverts commit c5fedd24c9.

Revert "sysmobts: Add beginnings of an OML router and create Failure Messages in the sysmobts-manager"
This reverts commit c6ab90b270.
2014-05-25 13:48:33 +02:00
Holger Hans Peter Freyther
7996134d2a common: Ignore "si.valid" outside of _MAX_SYSINFO_TYPE
Limit the range from 0 to (_MAX_SYSINFO_TYPE - 1) instead of
0 to 31. This way we will never access the lchan->si.buf[] out
of bounds. This is only a theoretical issue though as the code
filling the lchan->si.buf for the SACCH will not have valid
>= _MAX_SYSINFO_TYPE. Add a small regression test to check we
still schedule all SIs.

Fixes: CID 1040765
2014-05-22 21:17:49 +02:00
Holger Hans Peter Freyther
ac26607fe4 common: Remove unused gsm_time parameter from lchan_sacch_get 2014-05-22 20:42:33 +02:00
Holger Hans Peter Freyther
9d8aeab0b6 sysmobts: Avoid memleak when multiple -c arguments are passed
Rely on optarg pointing to an address that will be valid for
the run of the entire application.

Fixes: CID 1206578
2014-05-22 19:50:39 +02:00
Holger Hans Peter Freyther
44980347f3 sysmobts: Have a common prefix for the enum
Make the manuf_type_id enum have a common prefix for the
symbols.
2014-05-20 09:47:02 +02:00
Álvaro Neira Ayuso
7d36e5ed46 utils: Used the enum manuf_type_id in the parameter of add_manufacturer_id_label
Signed-off-by: Alvaro Neira Ayuso <anayuso@sysmocom.de>
2014-05-20 09:47:02 +02:00
Álvaro Neira Ayuso
afee0b7929 utils: Classify the OML message using the return type
Classify the OML message and return the manufacturer type
or an error. Currently ETSI, ip.access and Osmocom are known.

Signed-off-by: Alvaro Neira Ayuso <anayuso@sysmocom.de>
2014-05-20 09:45:36 +02:00
Álvaro Neira Ayuso
f5f41e8051 sysmobts: Do not access out of bound string
One can either use "strlen(str) + 1" but not add one to the
result of the sizeof.
2014-05-20 09:43:38 +02:00
Álvaro Neira Ayuso
13a224063d sysmobts: Separate IPA and OML check into two methods
I have split the function check_oml_msg, in two functions. One for
checking if the ipa header is well-formed and in check_oml_msg,

Signed-off-by: Alvaro Neira Ayuso <anayuso@sysmocom.de>
2014-05-20 09:37:30 +02:00
Harald Welte
0a1699ff8a screenrc: osmobts-mgr now needs a config file 2014-05-19 13:01:20 +02:00
Harald Welte
14c60b425f make sure osmobts-mgr.cfg file is included in tarballs 2014-05-19 12:58:31 +02:00
Harald Welte
b4280963c0 Revert "sysmobts: Add support for changing the transmit power in sbts2050"
This reverts commit c64d425738.

There are unfrtunately still too many problems with this patch to be
merged at this point.
2014-05-19 11:22:38 +02:00
Harald Welte
76c309e9f7 sysmoBTS TCH: Set CMR in AMR RTP frames
Enable the previously commented-out logic to set the CMR (Codec Mode
Request) in AMR RTP frames based on the CMI (CMR Index) of the AMR
speech frame on the Um interface.

This is of course anyway the right thing to do, but also required for an
AMR receiver which doesn't have out-of-band information on the codec
mode, and which also doesn't determine the AMR codec mode based on the
size of the AMR frame (such as lcr as of current master).

We also move the entire CMR generation into the #ifdef section
which is only compiled-in if we do _not_ use the RTP mode of L1.
2014-05-18 22:31:45 +02:00
Álvaro Neira Ayuso
c64d425738 sysmobts: Add support for changing the transmit power in sbts2050
Make the sysmobts-mgr send a manufacturer O&M message with the power
reduction we want the sysmobts to apply. The sysmobts will handle
this message and set the new tx output power. An ACK/NACK will be
send as a response to the power reduction.

Signed-off-by: Alvaro Neira Ayuso <anayuso@sysmocom.de>
2014-05-18 09:42:49 +02:00
Álvaro Neira Ayuso
c5fedd24c9 sysmobts-mgr: Add VTY support for configuring it
This patch allows to configure the warning temperature threshold,
the severe temperature threshold of the board and the PA and the
actions like the relative value power that we want to reduce the
transmit power to and the part that we want to switch off or not.

Signed-off-by: Alvaro Neira Ayuso <anayuso@sysmocom.de>
2014-05-18 09:42:08 +02:00
Álvaro Neira Ayuso
c6ab90b270 sysmobts: Add beginnings of an OML router and create Failure Messages in the sysmobts-manager
Make the sysmobts listen for OML messages on a Unix Domain Socket.
Messages passing a sanity check will be forwarded to the BSC.

In case the sysmobts-mgr detects a temperature above or below
temperature threshold an OML failure message will be sent
to the BTS.

[moved confinfo into the #ifdef BUILD_SBTS2050]

Signed-off-by: Alvaro Neira Ayuso <anayuso@sysmocom.de>
2014-05-18 09:41:29 +02:00
Harald Welte
73d9d3af6c sysmobts: Support DSP API >= 3.8.1 (u8MaxCellSize) 2014-05-17 10:04:44 +02:00
Holger Hans Peter Freyther
d75c648871 abis: Separate initialization from connect for Abis
Initialize the libosmo-abis VTY nodes more early so we can parse
the config file that was created by "write". Introduce abis_init
to initialize the libosmo-abis and modify abis_open to re-use an
existing line. Update the comments. This has only been tried with
the sysmobts-remote on x86. A TCP connection is opened toward the
configured BSC.

Fixes: SYS#285
2014-05-15 14:18:26 +02:00
Álvaro Neira Ayuso
8b5b993d29 sysmobts: Fix compiler warning about missing declaration
Include utils.h to have a declaration of sysmobts_get_nominal_power,

l1_if.c: In function 'l1if_activate_rf':
l1_if.c:1144:6: warning: implicit declaration of function
'sysmobts_get_nominal_power' [-Wimplicit-function-declaration]

Signed-off-by: Alvaro Neira Ayuso <anayuso@sysmocom.de>
2014-05-15 10:21:43 +02:00
Álvaro Neira Ayuso
9ed6b95c90 osmo-bts-sysmo/utils.c: Added a function for calculate the power transmitter
Signed-off-by: Alvaro Neira Ayuso <anayuso@sysmocom.de>
2014-04-29 12:08:28 +02:00
Holger Hans Peter Freyther
3e5b6db2d2 handover: Call the right function and avoid recursion
Fix a brown paper bag bug and call the right method. The above was
an infinite recursion. The stack didn't overflow as the compiler
optimized the tail-recursion and coverity didn't complain either.

The issue was introduced in the last minutes before the merge
when I renamed "reset_handover" to "handover_reset" to follow
the object_verb approach throughout the handover.c code. While
doing that I sadly replaced reset_handover with handover_frame
and not handover_reset.
2014-04-19 19:22:22 +02:00
Holger Hans Peter Freyther
d59bbd16a7 sysmobts: Add log message in case the channel activation fails 2014-04-07 20:33:04 +02:00
Holger Hans Peter Freyther
f547bee878 rsl: Use unique values for the call to rsl_tx_chan_act_nack
This way we can easily find the place in the code that is responsible
for the NACK.
2014-04-07 20:30:12 +02:00
Jacob Erlbeck
57a4d121c4 oml: Pass all valid state change requests to the model
Currently ADM state change request that tries to set the
administrative state to the current value are immediately ACK'ed.
Beside the caching problem, this could lead the protocol
inconsistencies if two such requests are sent one after the other and
the second arrives before the procedure of the first has finished.

This patch removes the shortcut in oml_rx_chg_adm_state() which
immediately called oml_mo_statechg_ack(mo).

Ticket: OW#1132
Sponsored-by: On-Waves ehf
2014-04-03 12:46:03 +02:00
Holger Hans Peter Freyther
0859795878 sysmobts: Fix build for the v1 of the sysmobts 2014-03-29 19:27:54 +01:00
Holger Hans Peter Freyther
14ff925555 agch/pcu: Fix crash for AGCH commands queued by the PCU
The dequeue code assumed that msg->l3h is a valid pointer but in
the case of the PCU socket it was a null pointer. This lead to
memcpy copying a lot more than 23 bytes which ultimately lead to
a crash. The issue was introduced in the git commits
37c332e5bf and the commit
d290ee029a.

use msg->l3h = msgb_put(msg, len) to make sure that there is a
valid L3 pointer for the message.

 (gdb) bt
 #0  0x419d6384 in memcpy () from /tmp/ow/lib/libc.so.6
 #1  0x0001894c in bts_ccch_copy_msg (bts=0x62248, out_buf=0x62248 "p\025\003", gt=0x1,
    is_ag_res=100684) at bts.c:572
 #2  0x0000c958 in handle_ph_readytosend_ind (rts_ind=<optimized out>, fl1=0x62e78)
     at l1_if.c:515
 #3  l1if_handle_ind (fl1=0x62e78, msg=0x8bb08) at l1_if.c:920
 #4  0x000147e8 in read_dispatch_one (queue=<optimized out>, msg=0x8bb08, fl1h=<optimized out>)
    at l1_transp_hw.c:190
 #5  l1if_fd_cb (ofd=0x62f04, what=<optimized out>) at l1_transp_hw.c:224
 #6  0x41b9d028 in osmo_select_main (polling=<optimized out>) at select.c:158
 #7  0x0000b204 in main (argc=<optimized out>, argv=<optimized out>) at main.c:384

(gdb) p *msg
$12 = {list = {next = 0x100100, prev = 0x200200}, {dst = 0x0, trx = 0x0}, lchan = 0x0,
  l1h = 0x0, l2h = 0x0, l3h = 0x0, l4h = 0x0, cb = {0, 0, 0, 0, 0}, data_len = 23, len = 23,
  head = 0x8572c "-\006?\020\r\340*q\224#", tail = 0x85743 "",
  data = 0x8572c "-\006?\020\r\340*q\224#", _data = 0x8572c "-\006?\020\r\340*q\224#"}
2014-03-27 09:19:24 +01:00
Holger Hans Peter Freyther
64a4327c34 sysmobts: Apply the potentially new max_power_red on the TRX
In case the max power reduction has been changed through OML,
let us call the l1if_set_txpower routine to update the nominal
power. This has been manually verified with both BTS #1 and #57.

./bsc_control.py -d localhost -p 4249 -s bts.0.trx.0.max-power-reduction 0

The above command and GNUradio have been used to determine if
the power level has changed at all.

Fixes: SYS#268
2014-03-26 18:05:41 +01:00
Holger Hans Peter Freyther
a276b98236 oml: Indicate the kind of object passed as the void*
These routines do not pass the gsm_abis_mo and parsing the FOM
header of the msg does not seem to be a good idea either. Pass
in the OML object so that the model code can determine what the
void pointer is.
2014-03-26 18:01:55 +01:00
Holger Hans Peter Freyther
71d98050f3 sysmobts: Honor power reduction on older sysmoBTSv2 hardware
Older hardware didn't have the external attentuator that was used
to control the wanted output power. So starting from the git commit
3c8ff3c70b older hardware was always
transmitting with 23 dBm regardless of the power reduction.

Remember the hardware revision returned by the SystemInformation
primitive, postpone the call to l1if_activate_rf until we know
the board revision.

Manually verified on BTS #1 and #57. On BTS#1 the external
attenuator has not been configured and on BTS#57 it was.
2014-03-26 17:49:06 +01:00
Holger Hans Peter Freyther
83dc54fb95 systemd: Provide the pcu direct mode 2014-03-21 19:28:13 +01:00
Holger Hans Peter Freyther
ae2473c2ca systemd: Do not restart with a broken config file or such
Only restart in case of a crash or the exit(42) when the OML/RSL
link is going down.
2014-03-21 18:10:12 +01:00
Holger Hans Peter Freyther
fb067905d5 sysmobts: Add a magic number to the hLayer2 to differentiate it
The DSP/FPGA appears to report bogus PhDataInd with hlayer2 == 0.
Currently this would match the TRX==0,TS==0 and SS=0 and then we
report bad measurement reports. Add a magic number to the lower
eight bit of the hLayer2 to differentiate valid numbers.

Addresses:
<0004> measurement.c:97 (bts=0,trx=0,ts=0,ss=0) measurement during state: NONE
<0004> measurement.c:102 (bts=0,trx=0,ts=0,ss=0) no space for uplink measurement
2014-03-16 14:23:37 +01:00
Holger Hans Peter Freyther
3e317ea4f0 sysmobts: Change the order to follow the RX handling code 2014-03-16 14:21:41 +01:00
Holger Hans Peter Freyther
b45c8a6b6c sysmobts: Improve the log message and print the hLayer2 we don't know 2014-03-16 14:21:41 +01:00
Álvaro Neira Ayuso
2dca8f37e5 misc/sysmobts_misc: function for switching off/on and requesting status power
I have extended the principal function that we use for requesting
information to the microcontroller for switching off/on the board
and the PA. And I have extended it for requesting the power status
information of the board and the PA.

Signed-off-by: Alvaro Neira Ayuso <anayuso@sysmocom.de>
2014-03-16 14:21:41 +01:00
Álvaro Neira Ayuso
4b614a0246 misc/sysmobts_mgr: Added new header created in the configure
Signed-off-by: Alvaro Neira Ayuso <anayuso@sysmocom.de>
2014-03-12 21:38:18 +01:00
Holger Hans Peter Freyther
bd26784362 misc: Ignore some of the new configure and test files 2014-03-12 16:54:19 +01:00
Holger Hans Peter Freyther
4e3aa93681 misc/sysmobts: Another small change to help in finding the header
We could have a dedicated configure/cflag for the header files
but for now search in the standard directories.
2014-03-12 16:51:27 +01:00
Holger Hans Peter Freyther
06387d89e8 misc: Fix the build breakage now that we have btsconfig.h
Include the btsconfig.h for the PACKAGE_VERSION variable.
2014-03-12 16:40:07 +01:00
Álvaro Neira Ayuso
e030dfd443 misc/sysmobts_misc.c: Read temperature from microcontroller
Add function for requesting the temperature information to the
microcontroller. I have added a function that we can extend
for requesting more information but in this case we only need to
know the temperature.
I have added to a microcontroller temperature handling function
in the manager for monitoring this information.

Signed-off-by: Alvaro Neira Ayuso <anayuso@sysmocom.de>
2014-03-12 16:27:38 +01:00
Jacob Erlbeck
21104720f7 agch: Remove obsolete comment
Use of configuration variables has already been implemented here, so
the TODO comment is removed.

Sponsored-by: On-Waves ehf
2014-03-10 14:04:29 +01:00
Holger Hans Peter Freyther
b26b8fc776 sysmobts: Do a RF mute at initialization when the RC is locked
Currently a locked cell is actively broadcasting when it is being
bootstrapped after the lock.

This patch adds an initial update of the RF mute state when the TRX
is initialized.

Ticket: OW#1131
Sponsored-by: On-Waves ehf
2014-03-10 14:00:21 +01:00
Holger Hans Peter Freyther
9c4a524444 handover,sysmobts: Handle handover in the sysmobts code
When the lchan was activated for handover configure it to wait
for a RACH burst. On release make sure to always release the
RACH SAPI (in case it has been allocated). On the first frame
inform handover.c about it and forward the received access burst
to the handover layer.

Using an E71 it was possible to make a handover for SDCCH and
TCH/F from a nanoBTS but also from itself to itself. The vty
commands of OpenBSC and the silent-call have been used for that.
I didn't verify audio handling so far.
2014-03-10 13:38:07 +01:00
Holger Hans Peter Freyther
bb76d816da handover,sysmobts: Handle idle needed for preparation of rach handling 2014-03-10 13:38:07 +01:00
Holger Hans Peter Freyther
cfce4d65f2 handover,sysmobts: Refactor the parsing/handling of the access delay 2014-03-10 13:38:07 +01:00
Andreas Eversberg
00b4e064ff handover: Add generic handling for handover
The BTS layer needs to inform the handover code when an access
burst has been received. In turn the handover layer will ask the
bts to modify the channel, it will schedule the physical information
inform the BSC with the HANDOVER DETECTION and waits for the BTS
layer to inform it about the first received frame to stop a timer.
2014-03-10 13:38:07 +01:00
Andreas Eversberg
8ade45e795 handover: Set basic values for handover, remember the activation reason
Introduce the handover.h/handover.c and initialize handover parameters
in OML and remember the activation through RSL.
2014-03-10 13:38:07 +01:00
Andreas Eversberg
3058854535 handover: Implement generating HANDOVER DETECTION in rsl_tx_hando_det 2014-03-10 13:38:07 +01:00
Andreas Eversberg
a37e239961 handover: Introduce debug area for handover related items 2014-03-10 13:38:07 +01:00
Holger Hans Peter Freyther
d863f9cbea Merge branch 'jerlbeck/agch-queue' 2014-03-10 13:33:26 +01:00
Jacob Erlbeck
4fcda92d7b agch: Merge IMM.ASS.REJ if possible when enqueueing
This patch implements merging of IMMEDIATE ASSIGN REJECT messages as
suggested in GSM 08.58, 5.7. When a new IMM.ASS.REJ is to be appended
to the AGCH queue and the last message in that queue is of the same
type, the individual entries (up to 4 per message) of both messages
are extracted, combined and stored back. If there are less than 5
entries, all entries fit into the old message and the new one is
discarded. Otherwise, the old message will contain 4 entries and the
remaining ones are stored into the new one which is then appended to
the queue.

Ticket: SYS#224
Sponsored-by: On-Waves ehf
2014-03-10 13:07:35 +01:00
Jacob Erlbeck
0148d4e7d5 agch: Add VTY queue management configuration
This patch adds the following VTY commands to tune AGCH queue
handling:

  agch-queue-management default
  agch-queue-management threshold THRES low LOW high HIGH

Examples:
  agch-queue-management default
    Resets queue management to default parameters.
  agch-queue-management threshold 0 low 25 high 75
    Start dropping at 25%, drop all messages above 75% queue length
    (relative to max queue length corresponding to T3126). Between
    low and high, drop with a probability interpolated linearly
    between 0 (low) and 1 (high).
  agch-queue-management threshold 50 low 0 high 0
    Start dropping at 50% and continue until all IMM.ASS.REJ have
    been removed from the front (output) of the queue

Sponsored-by: On-Waves ehf
2014-03-10 09:40:20 +01:00
Jacob Erlbeck
fae0149260 agch: Manage AGCH queue length
Currently, the AGCH queue length is not limited. This can lead to
large delays and network malfunction if there are many IMM.ASS.REJ
messages.

This patch adds two features:
- Don't accept msgs from the RSL layer when the queue is way too
  full (safety measure, mainly if bts_ccch_copy_msg() is not being
  called by the L1 layer, currently hard coded to 1000 messages)
- Selectively drop IMM.ASS.REJ from the queue output depending on the
  queue length

Ticket: SYS#224
Sponsored-by: On-Waves ehf
2014-03-10 09:40:04 +01:00
Jacob Erlbeck
7503540959 agch/pch: Use PCH for AGCH msgs
This patch extends paging_gen_msg() by adding an output parameter
is_empty that is true, if only a paging message with dummy entries
has been placed into buffer. This feature is then used by
bts_ccch_copy_msg() to insert an AGCH message if is_empty is true.

Ticket: SYS#224
Sponsored-by: On-Waves ehf
2014-03-10 09:22:24 +01:00
Jacob Erlbeck
2d725e77f7 agch/test: Add test for AGCH queue handling
The first test checks the AGCH may queue length computation.

The second test fills the queue by calling bts_agch_enqueue() with a
mix of IMM.ASS and IMM.ASS.REJ. Then it drains the queue by calling
bts_ccch_copy_msg(). After each of both steps, statistics are printed
out.

Sponsored-by: On-Waves ehf
2014-03-10 08:57:38 +01:00
Jacob Erlbeck
35a8144e41 agch: Add simple counters
Counters are added for the following events (use VTY show to query):

  - Dropped IMMEDIATE ASSIGN REJECT messages
  - Merged IMMEDIATE ASSIGN REJECT messages
  - Rejected AGCH messages
  - Use of PCH (non-reserved) for AGCH messages
  - Use of AGCH (reserved) for AGCH messages

Sponsored-by: On-Waves ehf
2014-03-10 08:55:02 +01:00
Jacob Erlbeck
4fbe06b3f2 agch: Recalculate length limit of AGCH queue
This patch adds a function bts_update_agch_max_queue_length()
to compute a limit of the AGCH queue. This is based on the idea,
that the AGCH queue has a limited drain rate and that CHANNEL
REQUESTs must be answered within a certain time frame, given
by the minimum value of T3126 (see GSM 04.08). When the AGCH queue
reaches that limit, the last message would be delivered in time if
there were no other delays involved (which is not the case).

The calculation is based on the ratio of the number RACH slots and
CCCH blocks per time:
  Lmax = (T + 2*S) / R_RACH * R_CCCH
where
  T3126_min = (T + 2*S) / R_RACH
  R_RACH is the RACH slot rate (e.g. RACHs per multiframe)
  R_CCCH is the CCCH block rate (same time base like R_RACH)

The value depends control_channel_desc.ccch_conf and
rach_control.tx_integer (both from SYSINFO_TYPE_3) and should
therefore by called at least each time after one of these is changed.
For this reason, a signal callback is registered under
SS_GLOBAL/S_NEW_SYSINFO which invokes bts_update_agch_max_queue_length().

Sponsored-by: On-Waves ehf
Based-On: "bts: Calculate length of agch queue" by Ivan Kluchnikov
  <kluchnikovi@gmail.com>
2014-03-10 08:54:54 +01:00
Álvaro Neira Ayuso
20f5422803 src/misc/sysmobts_misc: Fixed wrong TEMP_PATH
Before, this patch the program tried to read the info
of the temperature sensor from a wrong location.

Signed-off-by: Alvaro Neira Ayuso <anayuso@sysmocom.de>
2014-03-05 15:03:05 +01:00
Holger Hans Peter Freyther
e5bda88c9d sysmobts: Do not generate RF Conn failure for CCCH and PDCH
This could lead to a BSC attempting to release the BCCH or a
PDCH. In the case of the BCCH this lead to a funny crash.

Log:
<0000> rsl.c:605 (bts=0,trx=0,ts=0,ss=4) Sending Connection Failure: cause = 0x01
<0000> rsl.c:1720 (bts=0,trx=0,ts=0,ss=0) Rx RSL DEACTIVATE_SACCH

Backtrace:
 Program received signal SIGSEGV, Segmentation fault.
msgb_dequeue (queue=0x4007d2d8) at msgb.c:102
102        llist_del(lh);
(gdb) bt
 #0  msgb_dequeue (queue=0x4007d2d8) at msgb.c:102
 #1  0x4002ed28 in lapd_dl_flush_tx (dl=0x4007d220) at lapd_core.c:173
 #2  0x40030cb4 in lapd_dl_reset (dl=0x4007d220) at lapd_core.c:307
 #3  0x40030d00 in lapd_dl_exit (dl=0x4007d220) at lapd_core.c:321
 #4  0x40033d80 in lapdm_entity_exit (le=<optimized out>) at lapdm.c:169
 #5  0x40033d9c in lapdm_channel_exit (lc=0x4007d214) at lapdm.c:180
 #6  0x0001a334 in rsl_tx_rf_rel_ack (lchan=0x4007d180) at rsl.c:505
 #7  0x0000e908 in lchan_deactivate_sapis (lchan=0x4007d180) at oml.c:1427
 #8  sapi_queue_exeute (lchan=0x4007d180) at oml.c:547
 #9  0x0000ead0 in sapi_queue_send (lchan=<optimized out>) at oml.c:571
 #10 queue_sapi_command (lchan=<optimized out>, cmd=<optimized out>)
    at oml.c:609
 #11 queue_sapi_command (lchan=0x4007d180, cmd=<optimized out>) at oml.c:601
 #12 0x0000faf0 in enqueue_rel_marker (lchan=0x4007d180) at oml.c:1440
 #13 lchan_deactivate (lchan=0x4007d180) at oml.c:1447
 #14 0x0001004c in bts_model_rsl_chan_rel (lchan=<optimized out>) at oml.c:1647
 #15 0x0001b948 in rsl_rx_rf_chan_rel (lchan=0x4007d180) at rsl.c:844
 #16 rsl_rx_dchan (msg=0x75a88, trx=0x4007b038) at rsl.c:1727
 #17 down_rsl (trx=0x4007b038, msg=0x75a88) at rsl.c:1853
 #18 0x000154d4 in sign_link_cb (msg=<optimized out>) at abis.c:132
 #19 0x400701c0 in ?? () from /usr/lib/libosmoabis.so.2
 #20 0x400701c0 in ?? () from /usr/lib/libosmoabis.so.2
 Backtrace stopped: previous frame identical to this frame (corrupt stack?)

Fixes: OW#1133
2014-03-03 16:28:53 +01:00
Holger Hans Peter Freyther
5030972316 pcu: Avoid crash when closing the PCU socket
When closing the PCU socket all channels will be closed. In that case
the LAPDm structures might not have been allocated. Mark the channel
as LCHAN_REL_ACT_PCU to avoid going through the RSL code for sending
the message. This avoids a crash when "gprs none" is selected but one
still configures a PDCH and then connects/disconnects the pcu.

 #0  llist_del (entry=0x0) at ../include/osmocom/core/linuxlist.h:119
 #1  msgb_dequeue (queue=0x400bbc58) at msgb.c:102
 #2  0x40110d28 in lapd_dl_flush_tx (dl=0x400bbba0) at lapd_core.c:173
 #3  0x40112cb4 in lapd_dl_reset (dl=0x400bbba0) at lapd_core.c:307
 #4  0x40112d00 in lapd_dl_exit (dl=0x400bbba0) at lapd_core.c:321
 #5  0x40115d80 in lapdm_entity_exit (le=<optimized out>) at lapdm.c:169
 #6  0x40115d9c in lapdm_channel_exit (lc=0x400bbb94) at lapdm.c:180
 #7  0x0001a334 in rsl_tx_rf_rel_ack (lchan=0x400bbb00) at rsl.c:505
 #8  0x0000e908 in lchan_deactivate_sapis (lchan=0x400bbb00) at oml.c:1427
 #9  sapi_queue_exeute (lchan=0x400bbb00) at oml.c:547
 #10 0x0000ead0 in sapi_queue_send (lchan=<optimized out>) at oml.c:571
 #11 queue_sapi_command (lchan=<optimized out>, cmd=<optimized out>) at oml.c:609
 #12 queue_sapi_command (lchan=0x400bbb00, cmd=<optimized out>) at oml.c:601
 #13 0x0000faf0 in enqueue_rel_marker (lchan=0x400bbb00) at oml.c:1440
 #14 lchan_deactivate (lchan=0x400bbb00) at oml.c:1447
 #15 0x0001004c in bts_model_rsl_chan_rel (lchan=<optimized out>) at oml.c:1647
 #16 0x0001de30 in pcu_sock_close (state=0x62788) at pcu_sock.c:654
 #17 0x0001e150 in pcu_sock_read (bfd=0x627a8) at pcu_sock.c:698
 #18 pcu_sock_cb (bfd=0x627a8, flags=1) at pcu_sock.c:755
2014-02-24 13:51:30 +01:00
Holger Hans Peter Freyther
fcd4026e7b Revert "bts/vty: Use new vty_install_default() function, adjust prompts"
This reverts commit bbfd21a36c.
2014-02-24 13:03:33 +01:00
Jacob Erlbeck
f550a6a574 agch: Log error if BS_AG_BLKS_RES is != 1 in SI3
Currently, the DSP is always configured with u8NbrOfAgch = 1 before
SYSINFO type 3 is received. Thus using a different value for
BS_AG_BLKS_RES may lead to inconsistencies and MS failing to receive
paging messages properly.

This patch adds a warning and error logging and should be reverted
when initialisation is done in proper order.

Sponsored-by: On-Waves ehf
2014-02-24 12:55:38 +01:00
Jacob Erlbeck
d290ee029a agch/pch: Put CCCH message generation into common
This patch adds a common function bts_ccch_copy_msg() that provides
and schedules AGCH and PCH messages. It is basically a frontend to
paging_gen_msg() and bts_agch_dequeue() and contains refactored code
from l1_if.c.

Sponsored-by: On-Waves ehf
2014-02-22 08:45:56 +01:00
Jacob Erlbeck
d242ec2ed9 agch: Keep track of AGCH queue length
This patch adds and updates btsb->agch_queue_length to keep track of
the queue length.

Sponsored-by: On-Waves ehf
2014-02-22 08:44:29 +01:00
Jacob Erlbeck
37c332e5bf agch/rsl: Fix msgb handling for IMMEDIATE ASSIGN
Currently, the msg->data pointer is just set to the IMMEDIATE ASSIGN
message and the len is adjusted accordingly. Unfortunately, this
leaves l2h (pointing to the RSL header) and l3h (pointing to the
FULL_IMM_ASS_INFO IE) in an undefined state (outside of [data, tail]).
The code in bts.c accesses the message via msg->data.

This patch sets l3h and l2h correctly.  msgb_l3() will point to the
start of the IMM ASS message and should be used instead of msg->data.

Sponsored-by: On-Waves ehf
2014-02-22 08:41:02 +01:00
Jacob Erlbeck
bbfd21a36c bts/vty: Use new vty_install_default() function, adjust prompts
This patch removes the local 'end' and 'exit' implementations (which
aren't used anyway) and uses the generic ones provided by libosmocore
instead, which are enabled automatically when vty_install_default()
is used.

The prompt strings are modified to match those in
libosmocore/openbsc.

Ticket: OW#952
Sponsored-by: On-Waves ehf
2014-02-22 08:40:48 +01:00
Holger Hans Peter Freyther
4160e3d0f8 sysmobts: Remove debug left over from the SACCH fixes we made 2014-02-20 10:48:35 +01:00
Holger Hans Peter Freyther
2755f6e5b4 rsl: Do not allow IPA CRCX on non traffic channels
Use 0x52 as error cause as the nanoBTS is doing the same under the
situation. This has been spotted while testing handover using the
VTY command for handover.
2014-02-19 18:09:35 +01:00
Jacob Erlbeck
572ed461b6 rsl/si: Fix resetting bits in bts->si_valid
Use  'var &= ~(1 << x)' to reset bits instead of 'var &= (1 << x)'.

Sponsored-by: On-Waves ehf
2014-02-19 18:07:43 +01:00
Holger Hans Peter Freyther
1375a4b153 systemd: Disable colors in the stdout log
For journald we should not output escape sequences as it is
confusing the output.
2014-02-04 13:31:04 +01:00
Holger Hans Peter Freyther
f91799eecf l1fwd-proxy: Bind to the bts VTY port to block another process
Today we had the problem that multiple l1fwd-proxy instances ran
at the same time and not everything working all the time (some
packets were sent to a different host).

Another approach is to use flock on the message queues. This appears
to work fine as well.
2014-01-28 16:36:34 +01:00
Holger Hans Peter Freyther
023739fc94 sysmobts: Add the requested TA to the field ACCH header
Write the requested timing advance into the header. We are currently
using the u8AutoTA mode so the value will be overwritten by the DSP
before the bursts are sent to the MS.
2014-01-27 14:46:58 +01:00
Holger Hans Peter Freyther
1817447c24 sysmobts: Correct sending of LAPDm frames on the SACCH
When a frame is sent on the SACCH the LAPDm code will already
prepend two bytes for the tx_power and the ta. In the BTS we want
to use the values that were set by the BSC though. They currently
come from the RSL CHAN ACT but could also be set through the
RSL MS power control command. Update the comment as the space is
not empty.

At the 30C3 we had a NITB crash because of a RLL timeout on a
TCH/F. Jacob analyzed the problem and tracked it down to the
mismatch of LAPDm and the l1 interface to the DSP.
2014-01-27 14:46:52 +01:00
Harald Welte
2e93a8683c bts_model_rsl_chan_act(): Handle tp==NULL case gracefully
The PCU may call this function without a valid 'tp' (tlv parsed)
pointer, we need to make sure we take this into consideration...
2014-01-23 17:09:10 +01:00
Harald Welte
bc7d6fbdbb sysmoBTS OML: Don't permit TSC != BSIC
The sysmoBTS L1 has the TSC as a global value, we cannot have individual
per-timestamp or even per-lchan TSC, as the GSM specification would
suggest (and other BTSs support).

Rather than fail silently, write an error message to the log and
return NM_NACK_PARAM_RANGE or RSL_ERR_SERV_OPT_UNIMPL back to the BSC.
2014-01-21 23:39:00 +01:00
Harald Welte
bc48e26fc9 common/rsl.c: Allow bts_model_rsl_chan_act() to return negative cause
If the channel couldn't be activated, the function can simply return
a negated RSL_ERR_* constant which will then be propagated towards
the BSC in an CHAN_ACT_NACK RSL message.
2014-01-21 23:38:59 +01:00
Harald Welte
8196de46ad common/oml.c: Send OML NACK if bts_model_check_oml() returns negative
This way, bts_model_check_oml() can return a negated NM_NACK_* constant
which will then be sent as cause value in the corresponding
SET_ATTR_NACK back to the BSC.
2014-01-21 23:38:59 +01:00
Harald Welte
3c8ff3c70b sysmobts: Specify TRX nominal maximum tx power as fMaxTxPower
In the MPH ACTIVATE-RF.req, we need to specify the nominal maximum
transmit power, i.e. >= what we later request during MPH-INIT.req.

This field was first introduced in API version 2.2, but we never used
it so far.  It may help fixing a bug related to excessive power
consumption of the sysmoBTS 2050.
2014-01-21 23:38:47 +01:00
Holger Hans Peter Freyther
15bc64c6cd sysmobts: Honor the LDFLAGS when building the sysmobts-calib utility
Address warning during system builds.

Addresses:
  WARNING: QA Issue: No GNU_HASH in the elf binary...
2014-01-21 18:07:28 +01:00
Holger Hans Peter Freyther
06c098e2cb sysmobts: Launch the sysmobts-mgr in the screen and add service file
Launch the sysmobts-mgr as well. It will monitor the temperature
but it will not update the eeprom or act on any of the data. On
top of that it will respond to ipaccess-find messages making it
more easy to find the device.
2014-01-21 17:08:35 +01:00
Holger Hans Peter Freyther
d76211dc3b sysmobts-mgr: Check the return value of the sendto
Fixes: Coverity CID 1157379
2014-01-17 17:59:12 +01:00
Holger Hans Peter Freyther
5899b2d346 sysmobts-mgr: Respond to ipaccess-find broadcast messages
Bind to port 3006 and listen to incoming IPA requests. Currently
we unconditionally respond with the MAC and IP Address of the unit.
To determine the IP Address the kernel is asked for thesource
address of the route for the destination. In contrast to a nanoBTS
we will reply to the port the initial request came from.
2014-01-17 09:32:05 +01:00
Holger Hans Peter Freyther
1881e46cb9 sysmobts-mgr: Parse the daemonize option
Parse the daemonize option and daemonize after the full
set-up of the code.
2014-01-17 09:30:19 +01:00
Holger Hans Peter Freyther
810fbff380 sysmobts-mgr: Parse logging related commands, re-order init function 2014-01-17 09:30:03 +01:00
Holger Hans Peter Freyther
19224b4b9b sysmobts-mgr: Make it possible to not write to the EEPROM
For testing/trial it is better to not write to the EEPROM
but it is still good to see how the logic is working.
2014-01-16 13:49:50 +01:00
Holger Hans Peter Freyther
3e110ae141 misc: Allow to cross-execute the testsuite using qemu
When cross-compiling osmo-bts/osmo-pcu one can not easily
execute the testsuite. By adding the OSMO_QEMU variable in
front of the normal execution we can execute the tests. This
should work for native and cross builds.

$ OSMO_QEMU="qemu-arm -L /opt/poky/1.1.2/sysroots/armv5te-poky-linux-gnueabi/" make check
2014-01-15 11:59:28 +01:00
Holger Hans Peter Freyther
a4ffc44eac sysmobts: Specify the parameters that can be read from the EEPROM
When using the utility it is not clear which parameters can be
read or written. Make that more obvious.
2014-01-03 12:07:15 +01:00
Holger Hans Peter Freyther
c28a5b0b25 sysmobts: Specify the constant as a float and not a double 2013-12-29 10:14:20 +01:00
Holger Hans Peter Freyther
623d97a0d3 tch: Do not log every single RTP packet and air frame
The sysmoBTS takes quite a bit of CPU time for the vfprintf that
is used by osmo_hexdump. Do not dump every single frame to improve
the performance a bit.
2013-12-28 17:25:23 +01:00
Holger Hans Peter Freyther
9c279945a1 misc: Avoid using double numbers on our ARM
In the perf the ARM EABI ddiv operation showed up in the most
of expensive symbols. It doesn't really make much sense as the
calls should only be done on configuration.
2013-12-28 11:11:17 +01:00
Holger Hans Peter Freyther
19e87d332f measurement: Speculative performance change
Most timeslots do not have eight lchan. Use the subslots_per_lchan
map to reduce the number of iterations. Looking at the ARM assembly
showed that no loop-unrolling was done so this could be a speed up.
2013-12-28 09:37:59 +01:00
Holger Hans Peter Freyther
cdc6e3028c contrib: Remove the stray "FIVE" print it doesn't make any sense 2013-12-20 11:28:13 +01:00
Holger Hans Peter Freyther
540f608c2c sysmobts: Fix compiler warning by including utils.h
calib_file.c: In function 'next_calib_file_idx':
calib_file.c:126:3: warning: implicit declaration of function 'band_femto2osmo' [-Wimplicit-function-declaration]
2013-12-12 17:34:20 +01:00
Holger Hans Peter Freyther
0a51e1a337 sysmobts: Change the ARRAY_SIZE to a constant number
The parameter "uint8_t mute_state[8]" is actually a "uint8_t *mute_state"
so the ARRAY_SIZE is not what we think it is.

Fixes: Coverity CID 1125885
2013-12-12 17:31:02 +01:00
Holger Hans Peter Freyther
ef852ae86e sysmobts: Fix resource leak in the error condition
Fixes: Coverity CID 1047336
2013-12-12 17:28:52 +01:00
Holger Hans Peter Freyther
96264b6dd9 sysmobts: Remove stray semicolon from the PCU band filtering code.
The information from band_mask has never been used as the return
was executed unconditionally.

Fixes: Coverity CID 1113473
2013-12-12 17:26:15 +01:00
Holger Hans Peter Freyther
2800b347e9 bts: Fix crash of receiving data during the release process
Release/Free the lapdm resources _after_ the channel has
been fully released. Do not forward data unless the lchan
is in the active state.

Reading this code again, there is probably a memory leak for
everytime the PCU will re-connect to the BTS.

 (gdb) p lchan->state
 $4 = LCHAN_S_REL_REQ
 (gdb) bt
 #0  lapd_dl_flush_hist (dl=0x40454894) at lapd_core.c:164
 #1  0x44873b54 in lapd_rx_u (lctx=0xbe9bd5a8, msg=0x92f90) at lapd_core.c:1040
 #2  lapd_ph_data_ind (msg=0x92f90, lctx=0xbe9bd5a8) at lapd_core.c:1644
 #3  0x44876d50 in l2_ph_data_ind (link_id=<optimized out>, chan_nr=<optimized out>,
     le=<optimized out>, msg=0x92f90) at lapdm.c:637
 #4  lapdm_phsap_up (oph=<optimized out>, le=<optimized out>) at lapdm.c:707
 #5  0x0000c504 in handle_ph_data_ind (l1p_msg=0x97358, data_ind=0x97420, fl1=<optimized out>)
     at l1_if.c:774
 #6  l1if_handle_ind (fl1=<optimized out>, msg=0x97358) at l1_if.c:892
2013-11-27 14:29:50 +01:00
Holger Hans Peter Freyther
f56d56c439 sysmobts: Activate the BCCH silently have have state set to active
Use the lchan->rel_act_kind field for the BCCH activatiob by OML.
The lchan's should be marked as active but no event should be sent to
the BSC. This is mostly like the PCU. We can now remove the secnd
argument from lchan_activate.
2013-11-27 14:26:39 +01:00
Holger Hans Peter Freyther
ed9d643036 sysmobts: Make the eeprom/nominal power reading backward compatible
There are existing deployments where the EEPROM either contains a
wrong value and/or the kernel can not be updated to support the
different EEPROM of revD. Revert to the old behavior that if no
nominal can be derived from the model_nr we assume that it is 23.
2013-11-27 13:28:59 +01:00
Holger Hans Peter Freyther
e14ddaf204 sysmobts: Print the model number that is not supported.
When using an old kernel on revD hardware we will read garabge
from the EEPROM and it is nice for debugging to see which model
number has been picked.
2013-11-27 11:46:45 +01:00
Holger Hans Peter Freyther
e968c4224d bts: Fix typos in the log messages 2013-11-27 10:43:01 +01:00
Holger Hans Peter Freyther
082e21dbb5 bts: Fix a typo in the log message 2013-11-27 10:40:31 +01:00
Holger Hans Peter Freyther
a82cc5321e rsl: Rename abis_rsl_sendmsg to avoid symbol clash with libosmo-abis
Nicolas ended up with linker issues due abis_rsl_sendmsg being
defined twice. Rename our version of the function and update the
code.

Patched with:
 @i@
 expression E;
 @@

 - abis_rsl_sendmsg(E)
 + abis_bts_rsl_sendmsg(E)
2013-11-14 10:41:18 +01:00
Jacob Erlbeck
897f97f632 sysmobts: Notify the BSC about all muted lchans
Currently it takes some time (around 30s) until it is detected that
the radio link is down after mute. Not till then the BSC is informed
and the call terminated.

This patch modifies this behaviour by sending a RSL_MT_CONN_FAIL
message with cause RSL_ERR_RADIO_LINK_FAIL for each muted and active
lchan immediately after the corresponding Change Administrative State
Request has been acknowledged.

Ticket: OW#976
Sponsored-by: On-Waves ehf
2013-11-05 16:11:07 +01:00
Jacob Erlbeck
5eef61414a sysmobts: Only set RC state to LOCK if all channels are muted
Currently only mute_state[0] (refers to ts[0]) is inspected to
determine, whether the Radio Carrier's state is set to LOCK.

This patch changes this by looking at all channels and using LOCK
if (and only if) all channels have been muted successfully.

Sponsored-by: On-Waves ehf
2013-11-05 15:51:19 +01:00
Jacob Erlbeck
5b69ec3e72 sysmobts: Use status flags instead of direct LED access
Currently the LEDs are being accessed directly from within the
l1_if.c file. So the handling of rf mute and activate/deactivate both
access LED_RF_ACTIVE directly. This may lead to an inconsistent LED
status.

This patch replaces these calls to sysmobts_led_set() by an abstract
equivalent bts_update_status(), that uses a set of independant status
ids. The associated values can than be combined into a visible LED
status. Currently LED_RF_ACTIVE is on iff BTS_STATUS_RF_ACTIVE is set
and BTS_STATUS_RF_MUTE is not set.

Sponsored-by: On-Waves ehf
2013-11-05 15:51:19 +01:00
Jacob Erlbeck
08571b1588 sysmobts: Do a RF mute when Radio Carrier is locked
Currently a Change Administrative State Request is just applied
unconditionally to the object's state object and then acknowledged.

This patch implements the special handling of setting the Radio
Carriers state to LOCK or UNLOCK. This is done by passing the
appropriate mute command to the L1 layer. Always all radio channels
are affected, it is not possible to lock single radio channels.
On success, an ACK is sent back to the bsc with the new state (based
on the state passed in the callback by the L1 layer). If something
went wrong or the firmware doesn't support RF mute, a NACK
(REQ_NOT_GRANTED) is sent instead.

Note that a NACK for such a request hasn't been sent by the BTS to
the BSC yet, so (albeit it's spec conformant to do so) the BSC must
be prepared to handle this correctly.

Ticket: OW#976
Sponsored-by: On-Waves ehf
2013-11-05 15:46:30 +01:00
Jacob Erlbeck
9ef742f5e7 sysmobts: Add L1 support for the new RF mute request
This adds a new function

  l1if_mute_rf(femtol1_hdl, ch_mute[8])

to set the mute state for each radio channel. On completion and iff
l1if_mute_rf() returned 0 the callback

  oml_mo_rf_lock_chg(mo, ch_mute_state[8], success)

is invoked when the response from the superfemto DSP is received.

Ticket: OW#976
Sponsored-by: On-Waves ehf
2013-11-05 15:46:22 +01:00
Jacob Erlbeck
f3108fafab sysmobts: Add mappings for MuteRfReq/MuteRfCnf
This add the mappings for SuperFemto_PrimId_MuteRfReq and
SuperFemto_PrimId_MuteRfCnf to the arrays femtobts_sysprim_type,
femtobts_sysprim_names, and femtobts_sysprim_req2conf.

Sponsored-by: On-Waves ehf
2013-11-05 15:39:05 +01:00
Jacob Erlbeck
0133ff3866 sysmobts: Add L1P_T_INVALID to l1prim_type
Currently uninitialized elements of the femtobts_sysprim_type array
are mistaken as L1P_T_REQ (which is accidently the first element and
thus 0).

This patch adds a new element to the enum that has the value 0 to
detect uninitialized elements of the femtobts_sysprim_type array.
Those will then show up in the log as 'SYS Prim XXX is not a
Request!'.

This patch also adds missing definitions of the CalibTbl messages
in the femtobts_sysprim_type mapping so that the requests can still
be identified as such.

Sponsored-by: On-Waves ehf
2013-11-05 15:38:57 +01:00
Holger Hans Peter Freyther
f9dd260ee5 pcu: Exit the PCU in case of loss of the sysmobts connection
The PCU is not capable of cleaning up properly. For now simply
exit the PCU in case the sysmobts has exited. This requires
osmo-pcu a30f47613abb7c22a26d534d66e478265a8c2c09 or later.
2013-10-31 15:27:19 +01:00
Harald Welte
99fb43f41a sysmobts calibration: Load further tables even if one fails
Even if one calibration table cannot be loaded, continue to try to
load the other tables, instead of aborting very early.
2013-10-28 10:15:48 +01:00
Harald Welte
b8687024ea sysmobts calibration: skip bands not supported by L1
If L1 tells us that a certain band is not supported, then there is no
point in even trying to read+load calibration tables from EEPROM or
files.
2013-10-28 10:15:47 +01:00
Harald Welte
501673fcf3 sysmbts calibration: print error if we fail to read from EEPROM 2013-10-28 10:15:47 +01:00
Holger Hans Peter Freyther
6321c72522 rsl/pcu: Do not send a CHAN ACT to the BSC on PCU usage
The PCU is forcing the activation of a PDCH. Currently the BSC
will receive a channel act ack for a channel that was not
activated at all. Use the "release_reason" flag of the lchan
to see if we have requested a normal activation or a silent
one.

It feels a bit odd to do it in the TX function but it is the
most easy solution right now. I have added logging so it will
not be totally silent.
2013-10-25 19:14:44 +02:00
Holger Hans Peter Freyther
e851e13413 lchan: Print the name of the channel already in release request 2013-10-25 16:57:02 +02:00
Holger Hans Peter Freyther
d57e67e8da sysmobts: Fix the unit test after the internal band changes 2013-10-10 21:28:35 +02:00
Holger Hans Peter Freyther
ac3fc27257 sysmobts: Attempt to fix the compilation for the v1 hardware 2013-10-10 21:12:39 +02:00
Holger Hans Peter Freyther
ad142f84b3 misc: Fix resource leak when the ioctl is failing
Fixes: Coverity CID 1040759
2013-10-10 21:07:04 +02:00
Harald Welte
b2a8a642d6 sysmobts: Permit local override of transmit power above 23 dBm
This is used in the sysmoBTS 2050, where the maximum power is 40 dBm

We might want to add a safeguard of some kind to prevent people from
overdriving their transmitters.
2013-10-09 23:05:38 +02:00
Harald Welte
5c0e7b1f2c sysmobts: don't call sysmobts_get_nominal_power() twice
... no need for that
2013-10-09 22:35:46 +02:00
Harald Welte
e843f80832 sysmobts: make L1 power configurable
The TRX nominal output power (as seen by OML) is the aggregate power
of any gain internal to the sysmoBTS (and managed by L1) and any
external PA.  This is what is used in trx->nominal_power;

fl1h->l1_power is the transmit power to which we configure the sysmoBTS
L1.  This is 23 dBm (200mW) by default in the sysmoBTS 1002, and 40 dBm
(5W) in the sysmoBTS 2050.  However, if sysmoBTS 2050 is used in
single-TRX configuration, it may be used with higher power, which we can
now configure in the config file / vty.

TODO: A separate, additional field that keeps track of any gain added by
an external PA, e.g. if the sysmoBTS 1002 is used with a sysmoAMP.
2013-10-09 22:35:27 +02:00
Harald Welte
cfa54328e2 sysmobts: Don't use the clock calibration value on external clocks
If the clock is provided by an external (like GPS) clock, we should not
use the calibration value.  The latter is only used in context of the
OCXO or VCTCXO.
2013-10-09 16:06:41 +02:00
Harald Welte
50e63fa9e7 sysmobts: Set nominal transmit power depending on sysmoBTS model
This enables the use of up to 5W for each TRX in a sysmoBTS 2050.
2013-10-09 15:38:33 +02:00
Harald Welte
19009f2d7a Do not attempt to initialize L1 with a band unsupported by hardware
If the EEPROM tells us that a given unit doesn't support a given band,
we shouldn't try to use it, even if the BSC tells us to use an ARFCN in
such an unsupported band.

The reason is simple:  The given BTS unit might have band specific
filter / duplexer / PA.
2013-10-09 15:38:33 +02:00
Harald Welte
de0ca823f1 sysmobts: Read supported bands from EEPROM
...rather than assuming that any v2 hardware supports all bands.
2013-10-09 15:38:32 +02:00
Holger Hans Peter Freyther
6d76c1c701 Merge branch 'shared/libosmo-abis-late-init' 2013-10-06 15:51:56 +02:00
Harald Welte
d92774b4ac abis: delay l1if_reset() until OML link is established 2013-10-06 15:50:36 +02:00
Harald Welte
359fb8caf4 abis: Use OML remote (BSC) address if RSL CONNECT contains no IP
This introduces a new get_signlink_remote_ip() function whcih we also
use in the RSL code to determine the RTP remote address if the CRCX/MDCX
contains no remote IP address IE.
2013-10-06 15:50:36 +02:00
Harald Welte
b77ae3bc60 Call e1inp_vty_init() to make abis interface accessible from VTY
It might not be particularly useful, but then there's no disadvantage
either...
2013-10-06 15:50:36 +02:00
Harald Welte
6d5dc060ea migrate away from our own abis.c code to libosmoabis
libosmoabis has a BTS-side implementation of the IPA protocol for years,
and osmo-bts should have used that all the time.  Unfortunately it had
its own local hack, this patch is migrating to the libosmocore
implementation.
2013-10-06 15:50:36 +02:00
Harald Welte
b469e73148 Use GPS as default clock source on sysmoBTS 2050 2013-10-04 19:58:14 +02:00
Harald Welte
33fe4ca97b add sysmobts-util command-line utility to read/write EEPROM parameters 2013-10-04 18:29:54 +02:00
Harald Welte
870f1f1f84 sysmobts_par: add value_string definitions for parameters 2013-10-04 18:29:25 +02:00
Harald Welte
7410bc3020 sysmobts_par: Implement new EEPROM parameters (trx_nr, model_nr, model_flags) 2013-10-04 18:28:46 +02:00
Harald Welte
ff41a47c65 sysmobts_par: support for negative parameters
by splitting the rerutn code/status from the actual value, we support
negative values to be stored in the EEPROM
2013-10-04 18:27:16 +02:00
Harald Welte
ad89cd978a EEPROM: add model_nr, model_flags and trx_nr to EEPROM 2013-10-04 18:24:34 +02:00
Andreas Eversberg
971a95d259 Remove obsolete osmo-bts-bb code 2013-09-03 17:47:26 +02:00
Holger Hans Peter Freyther
ac98b545a8 sysmobts-calib: For gps the sign/difference appears to be different
For the sysmoBTS 2050 this appears to have a different sign. We can't
test this with NWL right now so we will need to see if this is a case
of ping/pong.
2013-07-26 21:24:37 +02:00
Nicolas J. Bouliane
ad10f0f533 sysmobts: Set the clock calibration to the value read from the eeprom
By default read the clock calibration from the EEPROM. It is
still possible to set it using the cli.

Signed-off-by: Nicolas J. Bouliane <nicolas.bouliane@nutaq.com>
2013-07-16 19:13:46 +02:00
Holger Hans Peter Freyther
fbf97e35eb calib: The call to fscanf can fail and we should check the return value
Coverity wants us to check that fscanf has scanned the amount of
variables we want to have. Initialize the scan result to 0/0.0f
and warn if the scan has failed.

Fixes: Coverity CID 1040774, CID 1040773
2013-07-16 18:36:44 +02:00
Holger Hans Peter Freyther
91d204e2db eeprom: Check the return value of the fseek in all calls
Coverity insists that we should check the return value of the
calls to fseek. In general this is a good idea.

Fixes: Coverity CID 1040770, CID 1040771, CID 1040772
2013-07-16 18:36:44 +02:00
Holger Hans Peter Freyther
f333387748 eeprom: After eeprom_write g_file could point to a closed file
Calling eeprom_write would either re-use g_file or newly open the
file and set g_file but it will close the file as well. This will
lead to other code using fseek/fread on a closed file.

On top of that the general rule for the eeprom code now is that
read and write may not be mixed (due caching and other bits).
2013-07-16 18:36:44 +02:00
Holger Hans Peter Freyther
95c6eed436 oml: Remove the unused nofh variable from oml_mo_tx_sw_act_rep
The variable was assigned but nothing was done with it, just
remove it for now.

Fixes: Coverity CID 1040758
2013-07-16 18:36:44 +02:00
Holger Hans Peter Freyther
612f387fc9 rsl: Fix the audio handling after the 'alignment' handling fix
The issue got introduced in fcdba6bfac
when moving from the uint32_t pointer to a plain int. The code
was now like this:

if (connect_ip > 0) {
   if (connect_ip == 0)
       lookup_ip_based_on_rsl
...

Coverity detected this as logically dead code and it was breaking
audio handling for the osmo-bsc case. Remove the tristate handling,
the RSL behavior is that leaving out port/ip is like specifying it
as zero.

Fixes: Coverity CID 1040769
2013-07-09 18:18:45 +02:00
Holger Hans Peter Freyther
ed966f0428 sysmobts: The code allowed a out of bounds access to temp_type_str
The array has three elements but check was for > _NUM_TEMP_TYPES (3)
so an access at array[3] was possible. It is unlikely to have
happened due the usage of enums. Use ARRAY_SIZE and >= on the real
array to avoid this problem.

Fixes: Coverity CID 1040760
2013-07-09 15:46:50 +02:00
Holger Hans Peter Freyther
481f14d87f sysmobts: Cache the eeprom_Cfg_t for reading tx/rx calib data
The current code has 26 fseek/fread. Only the minority really results
in a call to read. Nevertheless the time for reading during the bootstrap
can take up to 7.82 seconds. Caching the header (which is already done
by fopen/fread) will result in one call to fseek/fread and only
consumes 0.784 seconds.
2013-07-04 17:38:15 +02:00
Holger Hans Peter Freyther
89582f7e77 sysmobts: Add a method to free cached epprom resources
Close the cached file descriptor once the calibration data is
loaded and applied.
2013-07-04 17:38:15 +02:00
Holger Hans Peter Freyther
270cf418fc sysmobts: Read the mac and determine fixup only once during start 2013-07-04 17:38:15 +02:00
Holger Hans Peter Freyther
eebdfb8e6f sysmobts: Fix a typo that broke the ciphering with A5/0 > 0
Commit 5643130664 by Daniel changed
the ciphering to go through the command queue. In this commit the
direction for the ciphering got turned around and was not spotted
by review. It worked in testing due the usage of A5/0 and in that
case the direction did not matter.
2013-07-04 17:37:39 +02:00
Holger Hans Peter Freyther
2523cdbc7f misc: Fix various warnings in the code
sysmobts_vty.c: In function 'activate_lchan':
sysmobts_vty.c:373:3: warning: implicit declaration of function 'lchan_activate' [-Wimplicit-function-declaration]
sysmobts_vty.c:375:3: warning: implicit declaration of function 'lchan_deactivate' [-Wimplicit-function-declaration]

eeprom.c: In function 'eeprom_ReadEthAddr':
eeprom.c:305:5: warning: pointer targets in passing argument 3 of 'eeprom_read' differ in signedness [-Wpointer-sign]
eeprom.c:260:12: note: expected 'char *' but argument is of type 'uint8_t *'
2013-07-01 09:30:02 +02:00
Harald Welte
6404a76661 make oml_mo_state_init() a void function
... so we don't get warnings about not returning anything
2013-06-30 15:29:26 +02:00
Harald Welte
48eca2524c Don't send OML STATE CHANGE before OML is connected
Instead of calling oml_mo_state_chg() [which transmits OML STATE CHG]
during bts_init(), we use a new oml_mo_state_init() function which
simply sets the state.
2013-06-30 15:09:35 +02:00
Holger Hans Peter Freyther
0089ce4178 sysmobts.service: Use multi-user.target as target to fix ordering 2013-06-26 18:03:41 +02:00
Holger Hans Peter Freyther
123caa3c83 calib: Attempt to fix the build for v2.7 of the api headers
This should fix:
calib_file.c: In function 'calib_eeprom_read':
calib_file.c:262: error: 'SuperFemto_SetRxCalibTblReq_t' undeclared (first use in this function)
calib_file.c:262: error: (Each undeclared identifier is reported only once
calib_file.c:262: error: for each function it appears in.)
2013-06-24 11:18:54 +02:00
Holger Hans Peter Freyther
593080ebab calib: Attempt to fix the build for v2.7 of the api headers
This should fix:
calib_file.c: In function 'calib_fixup_rx':
calib_file.c:148: error: 'SuperFemto_SetRxCalibTblReq_t' undeclared (first use in this function)
calib_file.c:148: error: (Each undeclared identifier is reported only once
2013-06-24 11:11:16 +02:00
Holger Hans Peter Freyther
f169a75fc4 sysmobts: Introduce an auto-band config to ease DCS/DCS, PCS/PCS changes
During development one switches from GSM900 to GSM1800 and GSM850 to
GSM1900. This commit attempts to make this switch more easy.

GSM1800 and GSM1900 have overlapping ARFCNs. This means that the
mapping from bands to arfcn is not injective. Because of that I
removed the code to deduce the band from the ARFCN. This was done
in commit 8c3d807b3f. The auto-band
option allows to move between GSM900/GSM1800 and GSM850/GSM1900.

Add a simple testcase with these auto-band configurations.
2013-06-24 08:17:12 +02:00
Holger Hans Peter Freyther
43b4176f0e sysmobts.service: Reset the firmware after the service exited
These should have been ExecStopPost from the beginning. Currently
they reset the firmware while the software is starting. Reload
the DSP and FPGA firmware.
2013-06-24 08:17:12 +02:00
Holger Hans Peter Freyther
266af543e3 sysmobts: Make sure we receive every SACCH frame to count S properly
In case there is no transmitter the S counter might never be decreased.
This means that no radio link timeout will not be sent and the lchan will
remain open forever. There are several ways to resolve this.

The first would be to use the MphTimeInd and after each multiframe check
if there has been a SACCH message for the open lchan's. This could be
similar to the trx_meas_check_compute.

I decided to change fBFILevel to always receive SACCH frames and move
the code down to the PDTCH/PACCH handling and update the comment.
2013-06-24 08:17:05 +02:00
Holger Hans Peter Freyther
718cc9dcac sysmobts: Read multiple primitives at once but only up to 3
In most cases there are multiple messages ready to be read from
the queue and it is more efficient to read them in one go instead
of going through the select again.
2013-06-24 08:02:34 +02:00
Holger Hans Peter Freyther
44eec601bc sysmobts: Use writev for the outgoing data of the write queue
Attempt to write multiple primitives at the same time instead of
the select/write, select/write that is currently done. The queue
size is big enough to hold several entries at the same time and it
is unlikely we get the -EAGAIN from the kernel driver.

The writev code works by assuming that each element in the queue
has the same size. This is not verified by the code and if this
assumption breaks at some point the code will drop primitives or
send some twice.
2013-06-24 08:02:34 +02:00
Holger Hans Peter Freyther
25346fe0d7 sysmobts: Fix potential memory leaks in the prim callback handling
Make sure the l1msg is always freed in the callback. There were
several error conditions were the msgb would not have been freed,
in the case of the calib data and the system information the message
was not freed even in normal condition.

I will modify this code to __use a msgb. This allows to re-use
the allocated msgb across read operations.
2013-06-24 08:02:34 +02:00
Holger Hans Peter Freyther
a7e7537776 lapd: Fix a +ptrsize memory leak for each opened lchan
The lapdm/lapd_core code needs to keep a history of messages sent.
This history is not freed when lapdm_channel_reset is called and
the init code will just allocate a new array. This means there is
a memory leak on every released channel every time it is released.
2013-06-24 08:02:20 +02:00
Harald Welte
ee43f46cb0 calib: Fix for new EEPROM Mode; better log msgs 2013-06-22 19:30:59 +02:00
Harald Welte
40ca16766d calib: Add fixup for incompatible calib data / firmware version
For certain sysmoBTS units, a fixup to the calibration table is needed,
if the firmware is >= 3.3.0.
2013-06-22 19:30:55 +02:00
Harald Welte
2563267757 calib: Read calibration data from EEPROM, not just files
On v2D (and later) hardware, the calibration data can be read directly
from the EEPROM and doesn't have to be read from files.

If there is no trx-calib-path set in the VTY, we will read from EEPROM.
2013-06-22 19:27:13 +02:00
Harald Welte
a899146aea eeprom: wrap DISP_ERROR #ifdef/endif in PERROR() macro
This has the advantage that an user application might simply re-define
the PERROR() macro rather than patching the code all over the place.
2013-06-22 19:22:45 +02:00
Harald Welte
6002d17c24 eeprom: cache the file descriptor instead of fopen/fclose all the time 2013-06-22 19:22:45 +02:00
Harald Welte
d675de9c23 initial import of EEPROM calibration read routines 2013-06-22 19:22:45 +02:00
Holger Hans Peter Freyther
6ebabb560e sysmobts: Do not write "trx-calibration-path (null)" in the config file
When not specifying a config path, then saving the running config
it would end up as "(null)" and then leads to an error like this:

 <0006> calib_file.c:147 Failed to open '(null)/calib_rxu_850.cfg' for calibration data.

Add a NULL check to avoid this issue when writing the config file.
2013-06-20 18:17:34 +02:00
Holger Hans Peter Freyther
d98f2f35ec misc: Please ignore the commit. It is done to test a jenkins trigger 2013-06-20 17:24:32 +02:00
Nicolas J. Bouliane
fcdba6bfac rsl: fix the unaligned memory access
the armv5 can do 32bit/16bit reads only from the aligned address
use tlv.h macro to copy data to local variable

Signed-off-by: Nicolas J. Bouliane <nicolas.bouliane@nutaq.com>
2013-06-20 13:50:44 +02:00
Holger Hans Peter Freyther
19cefb0097 sysmobts: Fix a crash when the DSP2ARM queue runs full
When not reading quick enough from the queue we will get a bogus
response which will lead to marking the lchan as broken and to
clear the sapi queue. The sapi_queue_dispatch was checking if the
queue was empty before calling the callback but not taking into
account that it might have been flushed.

Stop processing if the queue was empty before calling the callback
or if it is empty after the callback.

Backtrace:
 #0  0x4eb1f1cc in raise () from /lib/libc.so.6
 #1  0x4eb22f48 in abort () from /lib/libc.so.6
 #2  0x4ecc2cb8 in talloc_abort (reason=<optimized out>) at talloc.c:167
 #3  0x4ecbc854 in talloc_abort_unknown_value () at talloc.c:180
 #4  0x4ecc6bc8 in talloc_chunk_from_ptr (ptr=0x4ec2d494) at talloc.c:192
 #5  _talloc_free (ptr=0x4ec2d494) at talloc.c:517
 #6  talloc_free (ptr=0x4ec2d494) at talloc.c:990
 #7  0x0000f294 in sapi_queue_exeute (lchan=0x402414a0) at oml.c:528
 #8  0x0000f2d4 in sapi_queue_send (lchan=0x402414a0) at oml.c:542
 #9  0x0000f3e0 in sapi_queue_dispatch (lchan=0x402414a0, status=-4) at oml.c:565
 #10 0x000114d0 in lchan_deact_compl_cb (trx=0x4021e038, l1_msg=0x7e690) at oml.c:1269
 #11 0x0000d70c in l1if_handle_l1prim (wq=1, fl1h=0x607c8, msg=0x7e690) at l1_if.c:938
2013-06-20 13:50:44 +02:00
Alexander Huemer
d07ee75fc6 Makefile.am: Use AM_CPPFLAGS
Since automake 1.13 INCLUDES is depricates and causes a warning
2013-06-12 08:01:00 +02:00
Holger Hans Peter Freyther
c03fe5af31 sysmobts: Allow to enable realtime priority for the BTS process
The latency to respond to a PH-READY_TO_SEND.ind may not be higher
than 18ms. Currently we are using nice to increase our priority but
for a heavily loaded cell this is not enough. Add an option to enable
realtime scheduling and use it in the screenrc.

Linux offers two realtime scheduling classes these are SCHED_FIFO
and SCHED_RR. For SCHED_FIFO the process is running as long as possible
(potentially taking all the CPU and never yielding it), for SCHED_RR
the process can still be pre-empted at the end of the timeslice.

Using SCHED_RR appears to be the more safe option as a run-a-way
sysmobts process will not be able to take all the CPU time.

For a very loaded cell we also require to use readv/writev to allow
writing multiple primitives in one syscall.
2013-05-11 08:34:36 +02:00
Holger Hans Peter Freyther
9be5f8c9c0 measurement: Mark the internal functions as internal
In terms of assembly code this only removes the ".global FN" from
the code. GCC does not attempt to inline it right now.
2013-05-04 13:12:20 +02:00
Holger Hans Peter Freyther
0d194268fb sysmobts: Use msgb_free instead of talloc_free to free the message
Currently msgb_free is calling talloc_free but we might introduce
a msgb pool in the future. So make sure to use the designated free
method for the msgb.
2013-05-01 19:03:43 +02:00
Holger Hans Peter Freyther
bd3250a456 sysmobts: Print the lchan name for the S counter. 2013-05-01 19:03:30 +02:00
Holger Hans Peter Freyther
3a6220cae2 rsl: Add the channel name to the act nack and conn fail message 2013-04-30 21:56:50 +02:00
Nicolas J. Bouliane
6a4c8a8596 osmo-bts: fix linking order in Makefile.am
On some system (e.g. ubuntu) libosmovty must precede libosmocore
otherwise we get undefined reference errors while linking.

Signed-off-by: Nicolas J. Bouliane <nicolas.bouliane@nutaq.com>
2013-04-18 20:32:23 +02:00
Holger Hans Peter Freyther
17dd79a3ae sysmobts.service: Install the sysmobts.service at the default target 2013-04-07 11:53:16 +02:00
Holger Hans Peter Freyther
636cad95a7 sysmobts: Document the known MphConfig conflict in the code
Right now changing the TxPower through the VTY could conflict with
a channel activation.
2013-03-24 09:05:05 +01:00
Holger Hans Peter Freyther
0809ae6941 sysmobts: Do not re-configure the channel on non-active channels
In case the channel is not active we can omit the external requests
to modify it. For the channel modification the higher level is already
acking it and for the ciphering it is probably too late to do anything.
2013-03-24 09:05:05 +01:00
Holger Hans Peter Freyther
4c4fd284ae oml: Use the queue for the release handling of a channel
There are three new commands. There are two markers and a deactivate
command. The markers are used to wait until all previous commands are
executed and then to decide if the SAPI needs to be released at all.

When asked to release the SACCH the marker will be queued, then on
execution of the marker the SACCH in Up-/Downlink will be released.

For the RF Channel Release we use another marker, when the marker is
executed we check all the SAPIs we want to release. It is possible that
the queue looks like this:
   (SACCH_REL_MARKER is done) REL_MARKER, SACCH DEACT, SACCH DEACT

This could happen if a BSC sends SACCH Deactivate and RF Channel Release
at the same time. We deal with issue by changing the SAPI state to the
REL_REQ state and check_sapi_release will not ask for another release. So
after the execution the queue will look like this:

  SACCH DEACT, FACCH DEACT, TCHF DEACT..

This code does not check that all allocated SAPIs are released. The
lchan_deactivate_sapis could be changed to go through all sapis_dl
and sapis_ul to fix that.

The normal flow should now be:
1.)  lchan_deactivate
2.)  Check if the queue is empty then go to 4
3.)  REL_MARKER is executed and lchan_deactivate_sapis is called
4.)  For all SAPIs to be released, check if they are allocated and
     then schedule a CMD_DEACTIVATE. If there is an error remember
     something went wrong but continue.
5.)  Once all commands are executed send the channel release ack.

For the release markers we need to be careful as they might not schedule
any work. E.g. if the BSC sends two SACCH DEACTIVATE the second marker
will not generate any release requests and we should proceed with the
next command. Make sapi_queue_command return 1 in case the command has
been directly executed. So a queue like SACCH_REL_MARKER, LOGCH will
result in LOGCH, SACCH DEACT Rx, SACCH DEACT Tx but a 0 will be returned
and the sapi_queue_next will then call sapi_queue_exeute again.

NITB has been modified to trigger these corner cases more easily.
* Do not send IMM.ASSIGNMENT for some timeslots to go through the
error path
* Issue multile SACCH deactivates in the normal release mode
* Send rsl_chan_mode_modify_req before the SACCH DEACT and also when
the RLL is being released.
2013-03-24 09:05:05 +01:00
Daniel Willmann
42cc93efb6 oml: Print out power setting in txpower completion callback 2013-03-24 09:05:05 +01:00
Daniel Willmann
4e46cb8961 oml: Use sapi command queue for setting the logical channel params 2013-03-24 09:05:05 +01:00
Daniel Willmann
5643130664 oml: Enqueue ciphering message through sapi cmd queue as well 2013-03-24 09:05:05 +01:00
Daniel Willmann
376183fcf0 oml: Introduce a SAPI queue for activation and deactivation of SAPIs
Put all SAPI requests into a queue and handle them one after another.
Begin with the channel activation. Once the queue is empty the channel
activate will be sent. For the BCCH activation we do not want to send
a channel activation message and this is why we set the lchan->state
to NONE.

One change is that we do not attempt to call the ciphering routines on
the BCCH anymore.

This change is necessary to fix issues with LCHANs staying open and being
marked as broken by the BSC and will help in implementing handover support
as this requires a re-configuration of the lchan on the fly.
2013-03-24 09:05:04 +01:00
Holger Hans Peter Freyther
fb0c9f0613 measurement: Add debug helper when we have a report for an inactive channel 2013-03-24 09:05:04 +01:00
Holger Hans Peter Freyther
9d91c60875 sysmobts: Prepare to address the documented limitation of this code 2013-03-24 09:05:04 +01:00
Holger Hans Peter Freyther
470a6ced9a oml: Only shut the bts down once
If the shutdown timer is already running do not deactivate the RF and
do not close the trx. This is addressing another instance of the following
warning:

[ERROR] : DeviceMng_ValidateL1Handle() => Invalid layer 1 handle
2013-03-23 11:39:32 +01:00
Andreas Eversberg
118eb43ba5 fixup e2cde1f48379657402332b5a95d4ce242d63069a 2013-03-18 18:05:24 +01:00
Andreas Eversberg
cdc5a4dc38 Add VTY option to define minimum C/I level for RACH and normal burst 2013-03-17 17:43:02 +01:00
Andreas Eversberg
5cbc7e9167 Get RSSI from received uplink data and send to PCU
This bumps the PCU API version and thus requires a new version of the
code on the sysmoBTS side!
2013-03-17 17:43:02 +01:00
Holger Hans Peter Freyther
4ad8d4d3c0 sysmobts: Name the screen and use '-X quit' to shut it down
Use "kill -2 0" for the PCU as SIGTERM is not handled yet. With
the current set of code the stop function will stop both the PCU
and the BTS.
2013-03-16 23:29:59 +01:00
Holger Hans Peter Freyther
e45fc86359 respawn: Adjust the oom score for the supervisor and bts/pcu, increase sleep
Make the script mostly unkillable due to OOM and make sure that the
process has a score of zero. Wait 10 seconds before re-launching.

The combination of ( && exec ) & appears to save one sub-process. The
script has been tested with bash and busybox's ash.
2013-03-16 14:15:50 +01:00
Andreas Eversberg
54dd949e62 Fix: Stop RADIO LINK TIMEOUT couter S from counting, if it has reached 0
In case that the counter S reached 0, it will stay 0. Subsequent received
good and bad SACCH frames must not cause to trigger radio link failure
again. Once the BSC has been indicated about link failure, it will release
channel.

The counting of S has been moved to a seperate function.

This patch will ensure that the link failure is indicated only once. But
even if the link failure would be sent multiple times, the BSC should
ignore it. The BSC releases the channel and may only reuse it after confirm
from BTS. (There cannot be any link failure indications after confirm of
channel release.)

The allowed timeout value range is 4..64, as defined in TS 05.08, so if the
BSC sends an attribute with value out of range or other failure criterion,
the Set BTS Attributes message is NACKed.
2013-03-15 21:41:27 +01:00
Harald Welte
620be0bbed OML: fix broken curly braces while parsing SET BTS ATTR
Looking at the problem, it's a surprise that the old code was working at
all...  (Thanks to jolly for pointing this out)
2013-03-14 11:20:09 +01:00
Harald Welte
f0bdc1e562 RSL: Fix Channel Number IE in Common Channel RSL messages
As per Chapter 9.3 of TS 08.58, we have to use RSL_IE_CHAN_NR instead
of the zero we were implicitly using so far.
2013-03-13 12:42:01 +01:00
Andreas Eversberg
294fd1b650 Added radio link timeout procedure according to TS 05.08 Chapter 5.2
Chapter 5.2 applies to MS procedure, but 5.3 (BSS procedure) defines no
exact criterion, so I decided to use the procedure equivalent to MS.

The criterion is based on a counter S, which is initialized to a preset
RADIO_LINK_TIMEOUT, which can be configured via VTY. Whenever a received
SACCH block is bad, S is counted down by one. If SACCH block is
successfully decoded, S is counted up by two, but never above initial
RADIO_LINK_TIMEOUT value. If S reaches 0, an RSL Connection Failure
Indication with cause RF Radio Link Failure is sent to BSC, which then
aborts channel.

Use link timeout value from BSC via OML attribute.

How to test:
- Set "debug" for "meas" logging.
- Start silent call to an attached mobile.
- Remove battery from mobile or shield mobile.
- Watch S count down.
2013-03-11 11:47:41 +01:00
Harald Welte
19f212951a l1_if: if ul_power_target==0, hard-code MS power to what RSL says
RSL CHAN ACT contains a MS_POWER IE which is intended to be used as the
initial power level for the MS, before the UL power control loop is
starting.

In our case, if ul_power_target != 0, then the DSP takes care of power
control.  If ul_power_target == 0, then we instruct the phone to
constantly use the value specified by the BSC in the MS_POWER IE.

FIXME: Actually implement a proper power control algoritihm inside
osmo-bts so we don't have to rely on the DSP implementation.
2013-03-07 10:07:18 +00:00
Harald Welte
cf4e3501a1 gsmtap: Put the RxLevel and RxQual in uplink GSMTAP 2013-03-06 19:58:26 +00:00
Holger Hans Peter Freyther
d9da7813a6 doc: Remove the rtp bind-ip from the example
This option is not needed anymore, let's remove it.
2013-03-02 18:12:22 +01:00
Holger Hans Peter Freyther
b7eb9865df calib: Use 2.4.0 as cut-off for the firmware, log errors
In case opening a calibration file is failing an error will will be
logged, the caller and implementation were inconsistent about the API
version that is supported for the calibration data, attempt to make
the cut-off at 2.4.0.
2013-02-27 11:10:33 +01:00
Holger Hans Peter Freyther
dd2a51ed32 tests: Share the stub between the paging and ciphering tests 2013-02-27 10:44:43 +01:00
Holger Hans Peter Freyther
faba73a812 sysmobts: Improve the shutdown of the DSP on exit
Issue the RfDeactivate.REQ before sending the MphClose.REQ. Ideally
we would issue MphClose.REQ after the RfDeactivate.CNF but this is
not possible right now.

The current approach makes the following warning of the DSP go away
on shutdown. This was tested with my E71 and an active silent-call
using a SDCCH.

DSP Warning:
[ERROR] : DeviceMng_ValidateL1Handle() => Invalid layer 1 handle
2013-02-27 10:44:01 +01:00
Holger Hans Peter Freyther
305d8314bc Merge branch 'zecke/request-queuing'
* Simplify the callback signature. The trx is now the first argument.
* Embed the calibration data into the femtol1_hdl.

Tests:
* All commits are compile tested
* All commits bring up the radio (without using calibration data)
* Calibration data loading has been tested with the merge
* All commits allow a IMSI Attach and a MO Call (to an invalid unknown
  number). All channels are freed after this. It has been tested with
  the E71.
2013-02-27 10:31:30 +01:00
Holger Hans Peter Freyther
5e46e4b488 sysmobts: Fix a memory leak when no callback is set
The TxPower handled used to call the requestion function without
a callback. In that case the msgb is leaked. The code still allows
the callback to be NULL so we will just delete the message in that
case.
2013-02-27 09:07:18 +01:00
Holger Hans Peter Freyther
3d383c22c7 sysmobts: Remove the is_system_primitive from l1if_req_compl
All users (but the gsm_compl) of the l1if_req_compl use it with
is_system_primitive=1. We can now remove this parameter from the
method. Introduce _l1if_req_compl that will insert the item into
the queue for us.
2013-02-27 09:07:18 +01:00
Holger Hans Peter Freyther
654fe73b78 sysmobts: We can now pass the trx to the callback change the signatures 2013-02-27 09:07:18 +01:00
Holger Hans Peter Freyther
6142f9262a sysmobts: Remove the trx parameter from the signature
l1if_gsm_req_compl everyone is passing the trx as data pointer right
now, remove it from the request procedure right now as it can be
deducted from the femtol1_hdl.
2013-02-27 09:07:17 +01:00
Holger Hans Peter Freyther
64c5e3a19c sysmobts: Embed the calib state in the femtol1_hdl and use hdl->priv 2013-02-27 09:07:17 +01:00
Holger Hans Peter Freyther
b6942ffeb9 sysmobts: Use the hdl->priv in l1if_req_compl for all callers 2013-02-27 09:07:17 +01:00
Holger Hans Peter Freyther
ff4f789249 sysmobts: Remove the data parameter from the l1if_gsm_req_compl
Pass in the trx argument at the lower level as everyone is using
the fl1h->priv now.
2013-02-27 09:07:17 +01:00
Holger Hans Peter Freyther
0890e274b1 sysmobts: Use the fl1h->priv and get the ts back from the response 2013-02-27 09:07:17 +01:00
Holger Hans Peter Freyther
60b090ac53 sysmobts: Use the fl1h->priv to get the trx instead of using the lchan
I am working toward killing the last argument of the l1if_gsm_req_compl
and just have the trx inside the callback signature.
2013-02-27 09:07:17 +01:00
Holger Hans Peter Freyther
dc9148d035 Merge branch 'zecke/calib-pch-agch-follow'
Introduce bcch_ccch to scan the PCH on top of the BCCH. The AGCH is
included in the PCH. CBCH/NCH are not included at this point.
2013-02-13 17:19:19 +01:00
Holger Hans Peter Freyther
1897f03d4c calib: Attempt to follow the PCH as well and print the SAPI..
AGCH is reported as part of the PCH because we are not searching
for the BS-AG-BLKS-RES inside the SI3 and do not use MphConfigReq
to change this setting.
2013-02-13 17:17:59 +01:00
Holger Hans Peter Freyther
6ac2e68467 calib: Print the frame number decoded as t1/t2/t3 2013-02-13 10:34:40 +01:00
Holger Hans Peter Freyther
225cf82290 calib: Provide the fn and block number for each frame 2013-02-13 10:34:40 +01:00
Harald Welte
18708dd3b6 RSL: further rtp local bind related fixes
If the CRCX does not indicate the remote IP address, then we still were
binding to 0.0.0.0 and used that address successively in the CRCX_ACK.

As a workaround, we now use the source IP address of the RTP socket,
assuming that the outbound routes to BSC and the MGW are identical.
This is of course not always true, but I don't think there are any
better alternatives...
2013-02-09 14:17:01 +01:00
Harald Welte
98407bd457 rsl: Fix compiler warning in use of osmo_rtp_get_bound_ip_port()
for whatever reason i decided that a port number in
osmo_rtp_get_bound_ip_port() needs to be a int * and not a uint16_t * at
the time, so we have to deal with this here rather than breaking the
ABI.
2013-02-09 11:44:07 +01:00
Harald Welte
0bb2974b37 Fix determination of locally bound IP for RTP sockets
After we create a socket and bind it to INADDR_ANY, we cannot yet use
getsockname() to resolve the locally bound IP.  This only works after
the socket has been connected to the remote IP.  So we have to move the
osmo_rtp_get_bound_ip_port() to a code section after
osmo_rtp_socket_connect() has already happened.

With the code prior to this commit, unless "rtp bind-ip" was used in the
config file, we reported "0.0.0.0" as the "Source IP AddresS" in the IPA
CRCX ACK to the BSC.   This is of course wrong, as the BSC will then use
this "0.0.0.0" as destination address for the incoming RTP stream :(

Please note that for this fix to work, you also need a libosmoabis.git
with commit d426d458ca96ba29793e35b1b2a73fbcb3b2c888 which actually
causes osmo_rtp_socket_connect() to actually issue connect() on the
socket at all.
2013-02-09 11:38:30 +01:00
Harald Welte
550d22be5b Deprecate the "rtp bind-ip" configuration directive
Instead of explicitly having to specify the local IP address for RTP
sockets in the BTS, we just use "0.0.0.0" instead, which gets
translated to INADDR_ANY.

We still accept the configuration directive in old config files, but
when we write, the line will no longer be re-written to the file.

TODO: IMHO, the IPA RSL CRCX/MDCX actually permit the BSC to specify the
IP address on the BTS side, and we probably simply ignore this at this
point.
2013-02-04 22:16:23 +01:00
Harald Welte
6a2d89f48d Make sure SACCH fill frame is correctly aligned for L1 header
When we send a fill frame on SACCH, we need to have two bytes for the L1
header at the beginning (inserted by the DSP).

Thanks for Andreas Eversberg for pointing it out.
2013-02-04 22:16:23 +01:00
Harald Welte
3ff2fc4378 RSL: don't store MS power in lchan->bs_power but lchan->ms_power
As we currently don't use any BSC-based MS power control in either
OpenBSC nor in OsmoBTS, this bug has never shown up so far.

Thanks to Andreas Eversberg for spotting this.
2013-01-25 16:00:20 +01:00
Daniel Willmann
bcd50d3219 oml: Create mph_send_activate_req for sending the activation request
Move the channel activation out of the loop into a dedicated function.
This is done in preparation of separating the decision to activate
something and sending the request.
2013-01-24 12:15:10 +01:00
Holger Hans Peter Freyther
f0c5a424af sysmobts: Send GSM requests using the l1if_gsm_req_compl method
Prepare to change the queue and callback handling. For the TX power
VTY command it is still possible that it will conflict with other
callbacks and the easiest way is to beging with sending these requests
through another method that allows us a more strict test.
2013-01-23 18:23:58 +01:00
Holger Hans Peter Freyther
76a1bf6136 sysmobts: Help in calling the right callback for l1if_req_compl
The wait list code has a limitation that for two requests of the
same kind it does not know where the confirmation belongs to. This
limitation is triggered when two lchan's get activated/deactivated
at the same time and is noticed once we start to count the SAPIs.

Set the hLayer3 to the lchan identifier, use the trx as closure for
the callback and resolve the lchan in the callback using these two
bits of information.
2013-01-23 18:23:58 +01:00
Holger Hans Peter Freyther
e210f1a864 sysmobts: Rename the hLayer2<->lchan to hLayer<->lchan
We are using this conversion for both hLayer2 and hLayer3. Make the
function name more generic to indicate that this function can be used
with the hLayer3.

The functions that call the methods were updated using spatch and
@rule1@
expression E;
expression F;
@@
- l1if_hLayer2_to_lchan(E, F)
+ l1if_hLayer_to_lchan(E, F)
@rule2@
expression E;
expression F;
@@
- l1if_lchan_to_hLayer2(E)
+ l1if_lchan_to_hLayer(E)
2013-01-23 18:23:58 +01:00
Holger Hans Peter Freyther
61e739912f sysmobts: Ignore too short messages on the FACCH (but also the SDCCH)
During call testing using an E71 and dialing a number of an expired (lac
set to 0) subscribe the CC Release and other messages are sent after or
during the channel modification. This appears to lead to some reception
issues in the DSP code and giving use empty (u8size == 0) FACCH frames.
The real issue might be inside the MNCC code of NITB and the lack of a
size check inside the LAPDm code (our msgb has enough data though).
Passing the empty FACCH frame into the LAPDm code lead to the generation
of RSL ERROR INDICATION with cause FRAME_UNIMPL as some bits were zero.

Add a check for 0 into the FACCH code. As the code path is shared with
SDCCH it is also discarding zero sized SDCCH frames. These have not been
observed during my testing. The lacking size check in LAPDm will be
addressed separately.

During call testing the IPA CRCX was also failing due the BTS trying
to bind to an unassigned IP address.
2013-01-23 18:22:11 +01:00
Holger Hans Peter Freyther
4a303c7c38 oml: Fix memory leak in the callback 2013-01-23 18:22:11 +01:00
Harald Welte
8597a278d6 l1_if: don't print measurements a second time in case of error 2013-01-23 17:42:17 +01:00
Holger Hans Peter Freyther
6ddd632adf sysmobts: Use a newline at the end of the SACCH u8Size < 2 message
The other message in this method is using a \n as well and the line
is generally now followed with a LOGPC.

It looked like this:
<0006> l1_if.c:672 SACCH with size 0<2 !?!<0006> tch.c:540 (bts=0,trx=0,ts=2,ss=0) Rx Payload size 0
2013-01-17 21:14:40 +01:00
Holger Hans Peter Freyther
0903001d9b calibration: The clock error is absolute for anything but the netlisten
We only need to run this once and we know the clock error. In case it
is 0/0 we know that we didn't receive one of the two clocks. This could
be because the GPS doesn't have a fix. I accidently pushed this code into
the master branch and it is too late to rebase.
2013-01-15 22:47:38 +01:00
Harald Welte
c2371cc011 fix message: the PCU is not a call control application 2013-01-15 18:57:59 +01:00
Daniel Willmann
91816acfb8 paging: De-duplicate paging lifetime and max queue length variables
These attributes are saved in paging_state, we don't need to save them a
second time in struct gsm_bts_role_bts. Add get and set methods for
these attributes and use them consitently in the VTY code.
2013-01-13 17:24:45 +01:00
Holger Hans Peter Freyther
fad5b08625 WIP... use gps for calibration.. 2013-01-12 21:44:48 +01:00
Harald Welte
24b2128e29 add PCU respawning to contrib screenrc and respawn scripts 2013-01-11 17:36:56 +01:00
Holger Hans Peter Freyther
3177f2b42f sysmobts: Fix the comment referring to the value of the timeout
Right now it is 30 seconds and not 10.
2013-01-03 16:39:41 +01:00
Holger Hans Peter Freyther
552989ad57 common: Fix faulty memcpy statement in the paging code
This was experienced by Daniel on his 64bit machine. The paging
expiration time was too high and not set by the code at all. Using
gdb watchpoints he found the place where the memory is written. The
issue is that the size of the pointer (8) and not the size of the
data structure was copied (3).

Fix the issue by assigning the de-referenced value. gcc generates
the same code as if we had written:
 memcpy(&ps->chan_desc, chan_desc, sizeof(*chan_desc));
2013-01-01 21:33:18 +01:00
Holger Hans Peter Freyther
222a6a5e23 l1_if: Fix typo... call it femtobts 2012-12-29 12:23:09 +01:00
Holger Hans Peter Freyther
0670138ffc tch: Do not print that there is no audio data on a TCH/H
When only signalling is used on the TCH/H it is normal that there
is no TCH data. Save CPU time by not printing the message. This
needs to be moved to be edge triggered.
2012-12-26 19:50:10 +01:00
Holger Hans Peter Freyther
19cf0e81b3 ciphering: Handle ciphering support for A5/3 correctly
This was found and debugged by Sylvain. The BTS will always support
A5/0 so we do not keep track of that, the first bit of the flags is
used for A5/1, second for A5/2... but for RSL there is an offset to
go from RSL to A5(x). Add a testcase and change the code.
2012-12-26 18:55:54 +01:00
Daniel Willmann
5f408f934c tests: Don't delete atconfig in clean
This file is created in ./configure so we shouldn't remove it with make.

Otherwise ./configure && make clean && make check fails
with:

make[3]: *** No rule to make target `atconfig', needed by `check-local'.
Stop.
2012-12-26 11:31:30 +01:00
Holger Hans Peter Freyther
fc9920830e misc: Package the configuration and contrib directory 2012-12-22 16:05:10 +01:00
Holger Hans Peter Freyther
c559dde69d misc: Package our version of the gsm_data.h to make the code compilable
The sharing with OpenBSC is not complete yet. We will need to include
our version of gsm_data.h instead of the normal OpenBSC version.
2012-12-22 16:02:05 +01:00
Holger Hans Peter Freyther
3bcf3a5fd0 sysmobts: Add all header files to the EXTRA_DIST to fix make distcheck 2012-12-22 15:03:19 +01:00
Holger Hans Peter Freyther
359b2cf469 misc: Fix the make distcheck of the osmo-bts code
* Comment out the osmo-bts-bb/Makefile as we have removed it from
  the SUBDIRS and are not packaging the code right now
* Add missing include files for the build
2012-12-22 14:59:12 +01:00
Holger Hans Peter Freyther
20c5702e0f paging: Update the test output to make the test pass 2012-12-22 14:44:00 +01:00
Holger Hans Peter Freyther
ce7559fbec misc: Fix compilation on debian stable with GCC 4.4.5
l1_transp_hw.c:89: error: redefinition of typedef 'dummyprim'
l1_transp_hw.c:88: note: previous declaration of 'dummyprim' was here
2012-12-22 14:37:30 +01:00
Holger Hans Peter Freyther
0d30b5d818 Merge branch 'zecke/openbsc-incdir'
Allow to have the OpenBSC directory somewhere else. This is required
to build osmo-bts on the public jenkins installation. The default is
the old behavior to look for OpenBSC next to the osmo-bts code.
2012-12-20 19:23:01 +01:00
Holger Hans Peter Freyther
56698b84e0 openbsc: Check for the presence of the gsm_data_shared.h header file 2012-12-20 19:18:13 +01:00
Holger Hans Peter Freyther
db51f0d73e openbsc: Introduce autoconf support to set the OpenBSC include directory
Add autoconf support to set the path to the OpenBSC include directory
so that openbsc/gsm_data_shared.h can be found there.
2012-12-20 19:02:37 +01:00
Holger Hans Peter Freyther
bcae2abff8 openbsc: Prepare to allow to have the OpenBSC directory somewhere else
Right now osmo-bts requires access to one OpenBSC header file and
this requires that openbsc and osmo-bts git are in the same directory.
Begin with making the location of the OpenBSC sourcecode configurable.

This approach will allow to build osmo-bts on our Jenkins installation
but now has the risk of more code including the openbsc/*.h header files.
2012-12-20 19:02:37 +01:00
Holger Hans Peter Freyther
d1ffab96ca misc: Forward declare calib_load to address a compiler warning 2012-12-20 19:01:27 +01:00
Holger Hans Peter Freyther
61a1f99680 misc: Forward declare l1if_set_ciphering to avoid a compiler warning 2012-12-20 19:01:27 +01:00
Holger Hans Peter Freyther
4fd0a84cf8 misc: Change the method to return void instead of int and garbage
The method was not returning anything and the callers did not use
the result. Change it to void for now.
2012-12-20 19:01:27 +01:00
Holger Hans Peter Freyther
9bd5afa014 misc: Include pcu_if.h for pcu_tx_pag_req in rsl.c and make it const
The rsl.c code was calling the paging request with a const pointer,
change the signature to make the code const.
2012-12-20 19:01:27 +01:00
Holger Hans Peter Freyther
8d8ff80890 misc: Forward declare the load_timer_start to address compiler warning 2012-12-20 19:01:27 +01:00
Holger Hans Peter Freyther
6f93861cfc misc: load_ind_period is uint8_t and 60*100 is bigger than that.
Address the compiler warning and truncate the value by hand.
2012-12-20 19:01:27 +01:00
Holger Hans Peter Freyther
6ae49691af sysmobts: Transmit the UI idle frame as a LAPDm Command
When transmitting an idle frame the BTS should transmit it as a
command and not a response. This is unbreaking the mobile application
of osmocomBB.
2012-11-26 01:33:52 +01:00
Harald Welte
2bad1363e9 Add VTY configuration of paging queue size and lifetime of paging records
This may be adding bells and whistles that nobody wants to touch, but at
least for current analysis/optimiziation they are useful to have.  Later
on they should probably be removed again and/or obsoleted by OML
messages for configuration of paging behaviour by the BSC.
2012-11-24 22:28:44 +01:00
Harald Welte
9858a7defe paging: send CCCH load indications even if paging load below threshold
This is mainly as OpenBSC is adjusting the amount of paging commands it
sends based on this magic value 0xffff.
2012-11-24 22:28:44 +01:00
Andreas Eversberg
a57fac59c6 Use tlvp_val16_unal() / tlvp_val32_unal() to align 16 and 32 bit values
This is required for CPUs < armv6, to access 16 and 32 values at right
memory locations.
2012-11-20 11:37:10 +01:00
Harald Welte
36e73dd7ed Ensure osmo-bts builds agsainst sysmobts-v1 headers (again) 2012-11-18 10:38:32 +01:00
Harald Welte
17dd7fad72 VTY: print length/depth of paging queue in 'show bts' 2012-11-10 18:33:12 +01:00
Harald Welte
ce826f3fc4 VTY: make target uplink Rx level VTY-configurable
We used to have -75 dBm as the target value for the uplink receive
level.  Now this is configurable.

The parameter is used as input into the power control loop that adjusts
the MS transmit power in order to achieve the target rx value on the
BTS Rx input.
2012-11-10 18:15:53 +01:00
Harald Welte
7350736054 l1_if: Dump measurement data in case we receive SACCH without data
On the uplink SACCH, we should at least receive the two bytes SACCH
uplink header from the phone.  But sometimes we don't.  Log this more
verbosely.
2012-11-10 18:06:02 +01:00
Harald Welte
fffbfd9890 l1_if: add 'log level' to dump_meas_res() function 2012-11-10 18:04:54 +01:00
Harald Welte
a066e334dd Measurement: Correctly report L1 SACCH uplink header in RSL
For whatever reason, the order of fields in the L1 SACCH header is
different from 04.04 (Um) and 08.58 (A-bis).  Please note that it's not
just a different bit order, but actually logically re-ordering the
fields within the byte, while keeping the bit-order/-endian.

We now correctly report the L1 transmit power up the stack.
2012-11-10 18:02:13 +01:00
Holger Hans Peter Freyther
dd4b8a2507 systemd: Use realtime scheduling for the BTS to read msg queues
We need to read the Layer1 message queues fast enough, switch on
realtime processing for that. Move the firmware init after the
process execution to have some time for the firmware to reload
before the application sysmobts is restarted.
2012-11-02 11:16:21 +01:00
Harald Welte
d1335d878b sysmobts: Add support for reading calibration tables
'trx-calibration-path' is the new VTY command indicating the path
name where the calibration files can be found.

Calibration is only implemented for SUPERFEMTO API version 2.4.0 or
later.
2012-10-28 10:58:41 +01:00
Harald Welte
98a4404279 l1_transp_fw: don't use printf() and dont print things twice 2012-10-28 10:17:42 +01:00
Harald Welte
5705cfaebc properly display the header file versions 2012-10-28 10:01:21 +01:00
Harald Welte
c3646a80a7 sysmobts: Add code to read calibration files
... and convert them to L1 primitives.  The code is not yet used.
2012-10-27 21:44:18 +02:00
Harald Welte
8debeeeeea make it build against sysmobts v2 APO 0.1, 0.2, 1.0, 2.0, 2.1, 2.2, 2.4 and 3.0 2012-10-27 18:06:03 +02:00
Andreas Eversberg
255343db4b Fix: Remove Bad frame Indicator from PDCH blocks before sending via GSMTAP 2012-10-22 10:34:08 +02:00
Harald Welte
4fe622cf9c OML: TA is a 8bit value, not 16bit
... as jolly correctly pointed out.
2012-10-04 18:13:19 +02:00
Andreas Eversberg
4168d885cf Fix: Set correct paging group for IMM.ASS on PCH 2012-09-29 20:32:00 +02:00
Andreas Eversberg
c1ad2ac20f PCU: Add PCH confirm, raise PCU interface version to 4
The confirm is required, so PCU knows when an IMMEDIATE ASSIGN message has
has been sent on PCH. The PCU will start packet flow after that confirm.
2012-09-29 20:31:40 +02:00
Andreas Eversberg
0efca9a1f9 Set correct GSMTAP channel type for PDTCH/PACCH 2012-09-29 20:31:15 +02:00
Holger Hans Peter Freyther
ef2cb5ab7f misc: Disable the color in the default setting
When forwarding the log messages to logger or systemd the ansi
escape sequence can confuse the app collecting the data.
2012-09-20 15:27:35 +02:00
Holger Hans Peter Freyther
4d197c96d8 systemd: Add a service for the sysmobts
Migrate the LED and firmware reloading into a systemd service. This
makes the respawn and screen obsolete as it will be done with systemd
and the journal script.
2012-09-20 15:24:50 +02:00
Holger Hans Peter Freyther
d127ddbfcc sysmobts: Fix the init script for systemd.
The rcS file is not part of the lsb. There is little need to
include this file.
2012-09-13 19:46:18 +02:00
Harald Welte
f91924bb18 sysmobts VTY: update to new libosmocore
libosmocore 40832fcfb58c8c97c66e098c5705352ac5beea8e and later contain
the vty_cmd_string_from_valstr() function, so we shouldn't have
a local / deprecated copy anymore.
2012-08-17 12:40:52 +02:00
Holger Hans Peter Freyther
8c3d807b3f sysmobts: Do not ignore the band configuration of the BTS.
The band was derived from the ARFCN but this does not work for
PCS1900/DCS1800 due overlapping ARFCNs. Use the already existing
band configuration to select the band for the MphInitReq. The
dsp firmware will complain if the band/arfcn do not match.
2012-08-09 12:15:41 +02:00
Andreas Eversberg
7daa093df7 PCU: Removed -P option, so GPRS support is always enabled 2012-07-26 21:14:02 +02:00
Holger Hans Peter Freyther
b86bf060d3 sysmobts: Support older firmware on the RevB hardware
For the firmware used on RevB the GsmL1_Prim_t was bigger than
the femtobts control structure. Solve it by introducing a macro
that will select the biggest size and use this macro. This is a
follow up fix for 08fce19cfc.
2012-07-26 20:18:53 +02:00
Holger Hans Peter Freyther
fde8e6dc0c vty: Remove TS_NODE and LCHAN_NODE as they are not used. 2012-07-25 14:34:22 +02:00
Holger Hans Peter Freyther
a9dee426d7 misc: Ignore some of the auto generated files 2012-07-25 14:16:51 +02:00
Holger Hans Peter Freyther
d777a19bb8 contrib: Add a python script to start sysmobts-remote and dump docs
This starts sysmobts-remote and dumps the documentation about the
VTY to the doc/ directory.

$ ./contrib/dump_docs.py
this writes doc/vty_reference.xml
2012-07-25 14:14:05 +02:00
Holger Hans Peter Freyther
e5a04ea35d vty: Document the gsmtap SAPI and the dsp trace flags parameters
Introduce femtobts_tracef_docs with some more information about
the traceflags, add parameters to the vty_cmd_string_from_valstr
for specifying the separator, the suffix and if the name should
be lowered.
2012-07-25 13:18:28 +02:00
Holger Hans Peter Freyther
1c74191ff0 vty: Document parameters of the unit-id and the band selection 2012-07-25 13:15:34 +02:00
Holger Hans Peter Freyther
93c087892c tests: Use the right name for the struct (not that it matters) 2012-07-25 12:03:52 +02:00
Harald Welte
7c2427c020 l1_if: indicate against which api header files we were compiled 2012-07-22 22:47:06 +02:00
Harald Welte
678321d013 determine (and use) the API version as indicated in the header files
From our header files v2.4 onwards, we include some macros that allow us
to do compile-time checks for the API header version.  As older headers
don't have those macros, we have to fall back to assume it will be v2.2
2012-07-22 22:42:36 +02:00
Harald Welte
e729a3d595 add missing stub functions to ensure paging_test compiles
FIXME: hlayer1 and l1if function calls are not acceptable in src/common !
2012-07-22 22:19:56 +02:00
Andreas Eversberg
1195148fc6 Send RR paging requests to PCU, in order to page on PACCH 2012-07-21 13:19:43 +02:00
Andreas Eversberg
1ddb183736 Enable direct access to PDTCH queue of DSP by PCU
Use "-P -M" to enable PCU and direct access.
2012-07-21 13:18:45 +02:00
Holger Hans Peter Freyther
c2d3e45571 sysmobts: The array size for the clocksources has increased to 10. 2012-07-20 15:30:06 +02:00
Holger Hans Peter Freyther
27baa4c3de sysmobts-calib: Add support for pre-production revb hardware
The board version wasn't exposed in the revb DSP interface.
2012-07-20 15:30:06 +02:00
Holger Hans Peter Freyther
b3eb6da2db misc: Quote the warning to avoid additional warning 2012-07-20 15:30:06 +02:00
Andreas Eversberg
d40d4d6071 Allow L1 forward proxy to provide all 4 queues to seperate applications
Different applications can now connect to L1 forward proxy or access DSP
directly, if they use different message queues.
2012-07-19 20:33:37 +02:00
Andreas Eversberg
08fce19cfc Allocate correct message size for L1 primitives
This is required for using firmware v2.4
2012-07-19 20:29:56 +02:00
Andreas Eversberg
0390d54ade logging: Fixed order of logging categories in enum list
The enum list must have the same order as the logging description
structure. Otherwiese libosmocore will crash when writing loglevels at
VTY.
2012-07-16 18:50:55 +02:00
Andreas Eversberg
0c470759da PCU: Add verion number of PCU interface to PCU INFO IND message
The client (PCU) can check if it is compiled with a different version.
2012-07-16 18:50:26 +02:00
Andreas Eversberg
5a53eff4cb sysmobts L1: fix memory leaks for GPRS
we have to hand off the PH-RTS.ind to the PCU interface _before_
we allocate a response msgb/primitive.
2012-07-16 18:48:55 +02:00
Andreas Eversberg
990d1da8a4 PCU interface: fix memory leaks in error paths 2012-07-16 18:48:37 +02:00
Holger Hans Peter Freyther
65d4d5108a calib: Create a new header file and move it. 2012-07-12 09:08:13 +02:00
Holger Hans Peter Freyther
0cfefa0e12 calib: Add code to change the BSIC/TSC before following the BCCH. 2012-07-12 09:08:13 +02:00
Holger Hans Peter Freyther
4253150bab calib: Use base 16 encoding for the dsp trace flags 2012-07-11 23:14:35 +02:00
Harald Welte
38420fb951 add new sysmobst-mgr daemon
This daemon is taking care of counting the number of hours in operation
and to watch the system temperature as determined by internal
temperature sensors.

Later, it will export an external interface for firmware reload, as well
as a way to raise OML ALARMs in case of temperature issues or other
problems.
2012-07-11 01:32:42 +02:00
Harald Welte
3696c6946d OML: add missing ntohs() for UL/DL_TBF_EXT
Thanks to Andreas for spotting this.
2012-07-11 01:26:32 +02:00
Harald Welte
438a28714d l1_if: skip processing of measurement results on PDTCH
In case of PDTCH, the PCU has to process measurements, not the BTS.
2012-07-09 15:51:42 +02:00
Harald Welte
c1368d4ebe PCU: remove german warnings from the code 2012-07-08 23:53:32 +02:00
Andreas Eversberg
744f745d7a PCU: Add PCU socket interface to BTS.
A special command line option "-P" is used to enable socket interface
and signal available GPRS MO object to BSC.
2012-07-08 20:50:02 +02:00
Andreas Eversberg
8169b0bd85 Add BTS to list at the beginning of bts_init()
During init process, signals might be sent. PCU receives these signals and
requires that BTS instance is already in the list.
2012-07-08 20:20:51 +02:00
Andreas Eversberg
07b37853a4 PCU: Add PCU socket interface prototype header file 2012-07-08 19:59:41 +02:00
Andreas Eversberg
bf2a18e623 debug: Add new debugging class for PCU interface (DPCU) 2012-07-08 19:41:41 +02:00
Andreas Eversberg
66f1fe15e9 signal: Add signals for setting/change of GPRS MO attributes 2012-07-08 19:38:39 +02:00
Andreas Eversberg
07891a0908 paging: Alow to store CCCH messages in paging records
This is required for PCU to send IMMEDIATE ASSIGNMENT messages on PCH.
A message in a paging record is sent only once.
2012-07-08 18:55:45 +02:00
Andreas Eversberg
343cae60b6 lchan: Activate PTCCH/PRACH/PDTCH/PACCH when activating PDCH 2012-07-08 18:48:58 +02:00
Andreas Eversberg
ea15101896 Fixed check for RACH (random access) delay 2012-07-08 18:44:40 +02:00
Andreas Eversberg
b57e17394b Fixes for handling of GPRS NSE/NSVC/CELL MO 2012-07-08 18:03:04 +02:00
Holger Hans Peter Freyther
b19592f713 paging: Update the unit test that would have caused the previous
The unit test created and used the paging request in the same
second and was passing because of that. Add a second test with
a delay to force now to not be equal to the expiration time.
2012-07-05 23:31:18 +02:00
Holger Hans Peter Freyther
cb7697074e paging: Expire paging requests after the expiration time
The paging needs to expire when the expiration time is smaller
than the current time.
2012-07-05 23:15:02 +02:00
Harald Welte
71b216d995 l1_if: don't enable any GSMTAP by default
the user has to explicitly enable it in the VTY
2012-07-05 15:31:06 +02:00
Harald Welte
d53ae2d0f1 sysmobts_vty: Fix setting GSMTAP sapi, as well as save/restore in cfg 2012-07-05 15:31:05 +02:00
Harald Welte
5f8a3149fe sysmobts: avoid sending duplicate RSL CHAN ACT ACK
This is just an intermediary hack, until we get proper lchan manager
threads...
2012-07-05 15:31:05 +02:00
Holger Hans Peter Freyther
eda6c26360 calib: Add a mode to follow the BCCH of a given cell. 2012-07-05 00:17:02 +02:00
Harald Welte
6b561bb7ba Add 12.21 handling for GPRS NSE/NSVC/CELL MO
We now bring the GPRS related MO up in DEPENDENCY state and parse
the various NS, BSSGP and RLC parameters as set by the BSC via 12.21/OML.
2012-06-28 08:59:48 +02:00
Harald Welte
fa8014f181 make sure we don't send CCCH LOAD IND before we have an Abis link 2012-06-21 16:46:05 +02:00
Harald Welte
61fb64d252 rsl: use correct headroom size for load indications 2012-06-18 22:17:28 +08:00
Harald Welte
54b8af0f64 use default value of 63 for maximum timing advance
As the careful commitlog reader Andreas points out:  When the BSC does
not sent NM_ATT_MAX_TA, then it would be zero instead of the specified
default value of 63.
2012-06-15 23:24:55 +08:00
Holger Hans Peter Freyther
9fdefc6ffe respawn: The BTS should not be nice, make sure the BTS is the most favorable 2012-06-15 14:53:15 +02:00
Harald Welte
13e92be8bf Implement NM_ATT_MAX_TA in sysmobts backend 2012-06-15 14:54:33 +08:00
Harald Welte
e01a47aad4 Update README and reflect that we now have CCCH LOAD IND 2012-06-15 11:17:15 +08:00
Harald Welte
babbbbf6ee CCCH LOAD IND: Avoid divide-by-zero
The total count of RACH or PCH slots should never be zero, as they
constantly increment.  However, just as a safeguard, we introduce
an explicit handign to avoid divide-by-zero situations
2012-06-15 11:15:11 +08:00
Harald Welte
821bf067e4 RSL: Add CCCH LOAD INDICATION for RACH
We now count the total number of RACH slots, the number with rx level
above the busy threshold, and the number of valid access bursts.

This data is used to generate RSL CCCH LOAD INDICATION for the RACH.
2012-06-15 11:07:03 +08:00
Harald Welte
c882b85d8c system information: avoid modulo 0 / SIGFPE
As Holger pointed out, it may well be the case that there are no system
information messages to be sent at TC=4, and we should avoid a modulo by
0.  I'm simply sending SI2 instead now, as it isn't forbidden to send it
more often than the minimum at TC=2...
2012-06-14 11:51:16 +08:00
Harald Welte
565cf0d8ab attempt to make CCCH Load Indications for PCH work 2012-06-14 11:48:24 +08:00
Holger Hans Peter Freyther
a540332df3 sysmobts-calib: Add a utility to calibrate the sysmobts v2 hardware
It has been tested with the OCXO and the network listen mode of the
firmware. For other sources we are not required to synchronize to
the network and the tool needs to be adjusted.
2012-06-12 18:17:11 +02:00
Holger Hans Peter Freyther
ad3e31dc4b sysmobts: The meaning of the clock value changed from v1 to v2
In v2 the calibration value is the clock error in ppb that needs
to be compensated. Create a V2 specific implementation. Write the
clock value unconditionally as it is initialized to 0 by default
and not 0xffff.
2012-06-05 09:34:33 +02:00
Holger Hans Peter Freyther
1c069cd0a0 sysmobts-v1: Fix compilation by using the right define 2012-06-05 09:17:26 +02:00
Harald Welte
0455e51cd5 Use git-generated PACKAGE_VERSION in IPA IDTAG_SWVERSION
We previously used to send the bogus string "0815" which was a hack
from early development time, but is obviously not a generally useful
idea.
2012-06-03 11:47:42 +02:00
Harald Welte
ad09615acb add known limitations 2012-06-03 11:01:31 +02:00
Harald Welte
2100a2e16f sysinfo: Make our SI scheduling more complete
We now implement the fairly complex rules for schedulign of
SI 2bis/2ter/2quater, 13 and 9 on TC=4 and TC=5 of the BCCH Norm.

The patch is currently untested.
2012-06-03 07:37:52 +02:00
Harald Welte
c58968be02 sysinfo: Schedule SI 2bis and 2ter
In case we have neighbor cells in different bands, we should send those
SI...
2012-06-02 22:20:58 +02:00
Harald Welte
799ea59c2f sysmobts: set the RF ACTIVE LED when we bring RF up
Once we get RF-ACTIVATE.conf from L1, we now enable the corresponding
LED.  We also switch it off on RF-DEACTIVATE.conf.  We do _not_ switch
it off when osmo-bts crashes or terminates before RF-DEACTIVATE.conf.

The latter is intentional, as RF may very well still be active at that
point.  The re-spawning script will re-set the DSP and therby turn off
the RF and then disable the LED.

A better solution might be to do all this in the kernel driver for the
DSP.
2012-06-01 00:06:58 +02:00
Harald Welte
700c645478 add /var/lock/bts_rf_lock and /var/run/osmo-bts.pid for rf control
an external application can create /var/lock/bts_rf_lock and then kill
the pid in /var/run/osmo-bts.pid in order to shut down the BTS.  Any
re-spawning scripts will trigger, but osmo-bts will refuse to start up
until /var/lock/bts_rf_lock is removed again.
2012-05-31 21:02:18 +02:00
Harald Welte
346e531222 sysmobts: fix double-free if msgq cannot be opened 2012-05-31 20:58:57 +02:00
Holger Hans Peter Freyther
b18f00f162 contrib: Re-load the firmware before restarting the main application
More recent firmware appears to have issues even after a clean shutdown,
make sure to fully reset the DSP before starting the BTS software.
2012-05-14 22:29:45 +02:00
Harald Welte
268c7f02fd sysmobts l1: make sure to read messages of arbitrary size
... and warn if the size is not what we expect.

This is required to work with sysmobts-v2 firmware >= v2.1, as the
SuperFemto_Prim_t is now larger than the GsmL1_Prim_t.
2012-05-13 15:25:27 +02:00
Harald Welte
388b9d0a35 Adapt to L1 firmware/API version 2.1 2012-05-13 14:16:28 +02:00
Holger Hans Peter Freyther
9de1e9f914 sysmobts: Document the values of the clock-source in the vty command
We need to have a documentation for each possible value, add some simple
ones for the available clock sources.
2012-05-12 08:49:35 +02:00
Holger Hans Peter Freyther
7fe0838588 sysmobts: Save the clock-source to the config file
Make the clock names lower case to match with the vty command.
2012-05-12 08:49:35 +02:00
Holger Hans Peter Freyther
3af5426d71 sysmobts: Add Network Listen as clock source for the bts 2012-05-12 08:49:35 +02:00
Harald Welte
e6ed814dc3 update the readme 2012-05-05 14:44:29 +02:00
Holger Hans Peter Freyther
f7fd2e4798 sysmo-bts: Use HW_SYSMOBTS_V1 to select the development hardware 2012-04-28 17:05:03 +02:00
Holger Hans Peter Freyther
36a3b0d85b sysmo-bts: Move the payload setting into a new method
Make this code a bit easier to read by moving the payload setting
into a new method.
2012-04-28 17:04:56 +02:00
Harald Welte
36179bbcdf RSL / SI: Make sure to have correct LAPDm header in SI5/SI6 on SACCH
SI5/SI6 and other messages on SACCH need the C/R and the EA bit set in
the LAPDm header.  Most devices accept a broken header, but especially
the Wavecom Q2686 responds with tons of RR STATUS messages if there is
any invalid bit.
2012-04-27 15:12:46 +02:00
Harald Welte
227c57728a fix the idle filling to comply with 04.06 5.4.2.3 (UI frame 0 byte len)
We used to send some crap before, which most phones happily accepted but
some (particularly the Wavecom Q2686) didn't really like at all.
2012-04-26 21:00:18 +02:00
Holger Hans Peter Freyther
baa88d542c lchan: I forgot to handle TchH in my recent lchan fixes, add it to another place
Harald fixed the issue for the activation by adding TchH, but this
needs to be added for de-activation as well.
2012-04-20 10:44:43 +02:00
Harald Welte
d28b9940b9 sach_deact -> sacch_deact (follow spelling fix in openbsc) 2012-04-19 23:51:50 +02:00
Harald Welte
2b7aace0b5 add vty-configurable loopback mode
this allows the BTS to loop-back any incoming data on a TCH
2012-04-19 22:29:07 +02:00
Harald Welte
b1644b22d0 Fix TCH/H channel activation after zecke's recent lchan fixes 2012-04-19 22:27:55 +02:00
Harald Welte
bcd08888f9 add VTY command to manually alter transmit power
this allows for quick manual tx power changes from the VTY, particularly
useful in type approval or other measurements.
2012-04-19 20:19:21 +02:00
Harald Welte
9aa6d9496b l1_if: allow for l1prim or sysprim without a completion callback 2012-04-19 20:18:53 +02:00
Harald Welte
ff9e904926 fix VTY help strings related to TRX 2012-04-19 19:47:55 +02:00
Harald Welte
f19ee66096 add a VTY command for activating PDCH channels (in EGPRS mode)
This allows us to do RF measurements (EDGE EVM and the like) even
without having any PCU/RLC/MAC code as of now.

To use it, configure PDCH type timeslots (e.g. TS 7) in the BSC and then
use "trx 0 7 activate 0" to manually activate the PDTCH lchan on top
of that timeslot.  The BTS will now happily transmit EDGE/8PSK data.
2012-04-19 19:22:53 +02:00
Harald Welte
4301b09137 delete dead code 2012-04-19 17:22:38 +02:00
Harald Welte
f5a0a439e9 ciphering: Make sure to initialize lchan to no ciphering when activating
The ciphering parameters in L1 are persistent accross MPH
deactivate/activate, so we need to make sure to always initialize them
cleanly at RSL CHAN ACT time.  This has the added benefit that we can
also activate channels that have encryption enabled from the very
beginning (required for encrypted handover).
2012-04-19 10:06:00 +02:00
Harald Welte
bf91f06eca Improve logging of L1 MPH request by printing the direction
where previously we would only see
<0006> oml.c:931 (bts=0,trx=0,ts=1,ss=0) MPH-DEACTIVATE.req (FACCH/F)
we now get
<0006> oml.c:931 (bts=0,trx=0,ts=1,ss=0) MPH-DEACTIVATE.req (FACCH/F RxUL)

to notice it is modifying the receive path in the uplink direction.
2012-04-19 09:50:02 +02:00
Holger Hans Peter Freyther
b0150b7ad4 lchan: Refuse to activate a non-idle lchan. 2012-04-19 09:39:54 +02:00
Holger Hans Peter Freyther
d7718280c9 lchan: Send the ACT ACK/NACK after the Layer1 has handled act/deact
Send the RSL ACT ACK/NACK after the Layer1 firmware has acked the
activation/deactivation. In case the channel can not be activated
we will send a NACK. In case the channel can not be deactivated we
will send an ACK and the next time the channel is activated we will
send a NACK. The release ack will be sent once the TxDownlink of the
TCH/SDCCH is closed.

Change the rsl_tx_chan_nack method to create a new msgb to be used
by the hardware layer, change the return value to ask the caller to
delete the msgb.
2012-04-19 09:39:54 +02:00
Holger Hans Peter Freyther
1e2b3259b9 lchan: Separate the Uplink/Downlink in activate/deactivate 2012-04-19 09:39:53 +02:00
Holger Hans Peter Freyther
29e1fdd994 lchan: Deactivate the SACCH only once, use the sach_deact flag for that
Use the deact_sach (renamed to deact_sacch in master) to remember if the
SACCH has been disabled. This should fix the case of lchan errors due releasing
the lchan twice.
2012-04-19 09:39:52 +02:00
Holger Hans Peter Freyther
af02387183 lchan: rsl_tx_chan_nack will re-use the msgb, do not msgb_free
Do not msgb_free the msg as it will be re-used inside the nack
method and return 1 so the caller does not free the msgb. This
ownership model needs some consideration but the usage of ref
counts will not yield good results.
2012-04-19 09:39:52 +02:00
Holger Hans Peter Freyther
f78f35880f lchan: Fix crashes when the specified lchan can not be found
gsm_lchan_name will crash if the lchan is NULL. Introduce an error_report
method that will do the right thing in the future and report the error.
2012-04-19 09:39:51 +02:00
Holger Hans Peter Freyther
eac221b4ea lchan: Fix the state transition in the deactivate handler
If the deactivation is failing the channel needs to be moved into
and error state, if the deactivation completed the channel needs to
be set to the none state and set the state to release reqeust on
the deactivation.
2012-04-19 09:39:51 +02:00
Holger Hans Peter Freyther
f4f69ee6fc lchan: Similar to OpenBSC use a set method to change the state
By making all modifications through lchan_set_state we can easily
add code to verify the state transition.
2012-04-19 09:39:15 +02:00
Holger Hans Peter Freyther
f1052b812d sysmobts: Add an option to query the hardware version. 2012-04-19 09:38:30 +02:00
Holger Hans Peter Freyther
0be33e3add common: Add the copyright text to the vty_app_info
This will make app -V print the copyright information like the other
applications of our universe. An BTS integration that want to list
additionaly copyright holders needs to access the vty_app_info and create
a new copyright string.
2012-04-19 09:38:30 +02:00
Harald Welte
b03f8ae4f0 ciphering: Better state tracking and HACK around L1 race condition
We now check if the received message is an LAPDm I frame in order to
determine if we have received the first valid encrypted message on the
radio link.  This relates to the fact that we often see 'old' UI frames
coming up from L1, even after it has confirmed decryption has been
enabled.
2012-04-19 09:35:03 +02:00
Harald Welte
d9ab45d1aa Support for ciphering
When the RR CIPH MODE CMD is transmitted to the MS, we need to tell the
L1 to enable decryption on RX.  After the first received frame has been
decrypted successfully, we will enable encryption also on transmit.

This has been tested with A5/1 so far, but A5/2 and A5/3 should work
exactly identical.
2012-04-19 08:22:29 +02:00
Harald Welte
51f9693ba6 make HR channels work for voice, not only signalling
without this, we would set a FR_V1 codec on a TCH/H channel, which
the L1 is obviously not happy with.
2012-04-19 08:21:41 +02:00
Harald Welte
b34faf6f8c TCH: Add support for the L1 RTP mode
In L1 RTP mode, the L1 already does all the bit-shifting and re-ordering
required for the RTP formats (which have different bit/nibble order than
the ETSI/3GPP encodings, for some odd reason).

We don't enable it by default yet, as only HR/FR/EFR work with it, but
AMR has some yet to be debugged problem.

Enabling USE_L1_RTP_MODE would save some CPU cycles on the ARM side.
2012-04-18 20:03:18 +02:00
Holger Hans Peter Freyther
bc74b7f432 femtobts: The separate pdch/tch queues are not available in the old firmware
The old firmware does not expose separate queues for PDCH and TCH. The change
appears to be too intrusive and I will try to find a more elegant solution.
2012-04-14 14:56:58 +02:00
Holger Hans Peter Freyther
f4a5bd2dd2 sysmobts: Handle options before allocating the bts
This way -h/--version will always work, even when the underlying
hardware is not available.
2012-04-14 14:36:23 +02:00
Holger Hans Peter Freyther
58f419c7ce misc: Use sizeof(uint32_t) instead of simply using 32 bit
This code would break in case we shrink the bitmap, use sizeof instead.
2012-04-14 01:03:28 +02:00
Holger Hans Peter Freyther
11a787df24 femtobts: Use HW_FEMTOBTS instead of HW_VERSION_1 to select femtobts
Our header files use HW_FEMTOBTS guards to select the older femtobts
design. Use the same macro in the bts code.
2012-04-14 00:54:40 +02:00
Holger Hans Peter Freyther
caaa7e9d7b misc: Address a compiler warning and add an assert to a branch
The compiler can not know that the "int priv_nr" will hold the
enum values of the write queue, add a default branch and add a
warning and an assert there.

l1_transp_hw.c:108:1: warning: control reaches end of non-void function [-Wreturn-type]
2012-04-12 22:31:06 +02:00
Holger Hans Peter Freyther
666fec7ff2 misc: Fix compiler warning about printing a ptrdiff
Use 't' modifier for pointer diff in the printf statement.

oml.c: In function ‘oml_rx_set_bts_attr’:
oml.c:403:3: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 9 has type ‘int’ [-Wformat]
2012-04-12 21:54:48 +02:00
Holger Hans Peter Freyther
76aa95453f misc: Fix compiler warning of the femtobts_clksrc_names
femtobts.c:249:2: warning: excess elements in array initializer [enabled by default]
femtobts.c:249:2: warning: (near initialization for ‘femtobts_clksrc_names’) [enabled by default]
2012-04-12 21:52:22 +02:00
Harald Welte
c623c4e589 oml: temporary debug hack 2012-04-05 02:48:16 +02:00
Harald Welte
2ed209c758 Increase head-room in IPA messages received
Without that headroom, I ran into an abort due to insufficient headroom
in the LAPDm code.
2012-04-05 01:16:46 +02:00
Harald Welte
a0970249bf osmo-bts-sysmo: Add gsmtap for uplink 2012-04-05 00:41:35 +02:00
Harald Welte
f4d14b3f2e set the default log mask for the L1 a bit more reasonable 2012-03-18 23:27:27 +01:00
Harald Welte
d25b6a752b osmo-bts-sysmo: Add GSMTAP support for transmit (DL) path
there are VTY commands that can be used to filter which particular
L1 sapis (channel types) should be sent in GSMTAP.
2012-03-18 23:24:12 +01:00
Harald Welte
3cf942792a correctly print SAPI in log file on MPH-ACTIVATE.req 2012-03-18 21:46:44 +01:00
Harald Welte
21724bbaed Fix debug print of MPH-CONFIG.req 2012-03-18 21:35:15 +01:00
Harald Welte
12b95405ff print human-readable SAPI name on MPH-[DE]ACT.{req,conf} 2012-03-18 21:34:05 +01:00
Harald Welte
452112e823 Ensure that ADM_STATE IE is presnent when sending NM_MT_CHG_ADM_STATE_ACK 2012-03-18 21:25:45 +01:00
Harald Welte
d0e6749327 Issue MPH-CLOSE.req during shutdown
If we don't do this on recent L1, the L1 will refuse the open after
re-starting osmo-bts.

There still is an issue in case osmo-bts crashes.  We should have a
respawn loop that re-loads the DSP firmware before re-starting osmo-bts,
just to make sure...
2012-03-17 14:25:34 +01:00
Harald Welte
b81c5d4699 introduce a command that permits setting the clock source via vty
the default source is the OCXO
2012-03-17 14:08:51 +01:00
Harald Welte
fe0c13f8bd OML: when allocating merged tlvp arrays for MO, use bts context
'ts' is not a talloc-managed pointer but an offset into the bts
structure.  As such, we cannot pass it to talloc as context!
2012-03-15 23:39:53 +01:00
Harald Welte
3525f2c038 we currently run the board alwasy in clock master mode 2012-03-15 23:39:37 +01:00
Harald Welte
20d73555a2 update to new "superfemto.h" header file naming 2012-03-15 21:27:21 +01:00
Harald Welte
47589f10a4 Introduce a HW_VERSION_1 #define
This #define helps us to distinguish the subtle API differences between
the earlier v1 (2011) hardware and the later v2 (2012) model.
2012-03-07 18:05:57 +01:00
Harald Welte
f1cbd81984 prepare for splitting L1 queue into signalling/tch/pdtch
We don't use multiple queues yet, but we very well might end up using
them soon.
2012-03-07 18:05:18 +01:00
Harald Welte
818cb2d314 update config file to parse correctly 2012-02-10 13:32:58 +01:00
Holger Hans Peter Freyther
6dd7c4fb57 misc: Check return value of msgb _alloc functions
Attempt to catch all functions that allocate a msgb and didn't
check the return value of the allocation.
2012-01-23 10:22:09 +01:00
Holger Hans Peter Freyther
bb9647f651 ipa: Send the DLCX Indication with the right message discriminator
The IPA messages for RTP should use the IPA vendor as message
discriminator.
2012-01-15 18:09:40 +01:00
Holger Hans Peter Freyther
2e677958d2 efr: Add efr to the femtobts_tch_pl_names array
Parts of the code check if GsmL1_TchPlType_Efr is defined, others
parts don't. Follow the easy route and assume it is defined.
2012-01-14 21:47:59 +01:00
Holger Hans Peter Freyther
771e77dff0 oml: Use talloc_free(ptr) instead of talloc_free(ptr_to_ptr)
tp_merged points to memory allocated by talloc_zero, no need to
hand the address of that to talloc itself.
2012-01-14 21:47:49 +01:00
Holger Hans Peter Freyther
62579c7a34 oml: Mention the SAPI that is activated in the log message
I was wondering why the channel was activated twice but it needs
to be activated for each SAPI.
2012-01-14 21:47:41 +01:00
Holger Hans Peter Freyther
4cd68dc4d7 bts: Use msgb_dequeue and msgb_enqueue for the AGCH queue
The TODO item still applies to somehow limit the queue of incoming
messages and drop older ones first. A sane limit would be the number
of channels (+ or * 2).
2012-01-14 21:47:30 +01:00
Holger Hans Peter Freyther
eab71534ef sysmo: handle_ph_data_ind has paths rc is not initialized
rc might not be initialized when going through the default
statement but also hitting a break inside the switch case
statement for GsmL1_Sapi_Sacch.

l1_if.c:530:2: warning: Undefined or garbage value returned to caller
        return rc;
2012-01-14 21:47:19 +01:00
Harald Welte
6e121417a5 RSL: fix typo in comment 2012-01-14 12:35:40 +01:00
Harald Welte
7a44e47ed6 OML SET CHAN ATTR: merge TS attributes (not BTS) and fix mem leak
We have to
 * merge the new attributes with the exiting TS (not BTS) attributes
 * in case of success, attach the new merged attributes to our state
 * in case of success, free the old attributes

Thanks to Holger for pointing this out.
2012-01-14 12:28:17 +01:00
Holger Hans Peter Freyther
6aa2a574fb sysmo-bts: The code is not used (and built), remove it.
The BTS is using the LAPDm code in polling mode, there will be no
callbacks (e.g. a BTS does not transmit RACH bursts). Remove the code.
2012-01-11 19:27:54 +01:00
Holger Hans Peter Freyther
fd58d925a8 bts.h: lchan_init_lapdm is listed twice, remove one 2011-12-11 12:57:36 +01:00
Holger Hans Peter Freyther
b0985e3fa5 test: Introduce a very simple test for the paging subsystem
Check that adding a paging command works, check that it is expired
after the first call to paging_gen_msg. The test will be extended
to test the scheduling and selection of the various paging messages.
2011-12-01 09:14:32 +01:00
Holger Hans Peter Freyther
467e149763 paging: Provide functions to check the internal state of the paging system 2011-12-01 09:09:18 +01:00
Holger Hans Peter Freyther
127ec05b4e paging: Do not crash if we get called for the wrong frame/t1/t2/t3
If someone wants to have paging for a wrong frame, gracefully return
and do not fill the output buffer. Because we are on the wrong frame
I think it is best to not fill the frame, this is why I did not add a
check to l1_if.c to generate an empty frame.
2011-12-01 08:47:53 +01:00
Holger Hans Peter Freyther
79da6f3283 misc: Move the cmr_index into the #if 0 block as it is only used there 2011-11-29 21:55:12 +01:00
Harald Welte
143bb812dc LAPDm: Use lapdm_channel_exit() and avoid copy+paste bug
We have to either lapdm_exit() both DCCH and ACCH (not 2x ACCH) or
rather call lapdm_channel_exit() which does that for us.

Thanks to Holger Freyther for spotting this bug.
2011-11-29 12:15:16 +01:00
Harald Welte
fe4893e625 RSL: Actually check if BSC-requested cipher is supported 2011-11-24 17:46:22 +01:00
Holger Hans Peter Freyther
2660812084 audio: Make bts_model_rtp_rx_cb compatible with the prototype 2011-11-07 14:26:48 +01:00
Holger Hans Peter Freyther
5cdcf8a837 sysmo-bts: Include bts.h for bts_shutdown, remove unused variable 2011-11-07 14:13:29 +01:00
Holger Hans Peter Freyther
efdb45d5d0 common: Include bts.h for bts_shutdown 2011-11-07 14:09:53 +01:00
Holger Hans Peter Freyther
477f35e78c sysmo-bts: Use the z modifier to print the result of sizeof 2011-11-07 14:08:46 +01:00
Holger Hans Peter Freyther
187871e2ca sysmobts-vty: Fix compiler warnings about the clock value
The first one just sets the val to 0xffff, the second converted
the value to integer twice.

sysmobts_vty.c: In function ‘cfg_trx_clkcal_def’:
sysmobts_vty.c:109:15: warning: unused variable ‘clkcal’ [-Wunused-variable]
sysmobts_vty.c: In function ‘cfg_trx_clkcal’:
sysmobts_vty.c:122:15: warning: unused variable ‘clkcal’ [-Wunused-variable]
2011-11-07 14:02:02 +01:00
Holger Hans Peter Freyther
b10d74d821 config: Rename llapdm -> llapd in the example configuration 2011-11-07 13:48:02 +01:00
Harald Welte
9582883235 add VTY based way to set clock calibration of sysmobts L1 2011-10-12 13:36:22 +02:00
Harald Welte
c373448e03 fix various compiler warnings across the code
this deals with unused cocde, unused variables and undeclared symbols in
various places.
2011-09-19 20:46:51 +02:00
Harald Welte
7899dc5fcf sysmobts: fix initial codec mode computation
There is no off-by-one between osmocom and L1 definitions...
2011-09-09 23:55:39 +02:00
Harald Welte
215d9eecdd sysmobts: channel activation changes for v2.4 L1 DSP firmware
We now have to explicitly indicate the tchPlType at channel activation
type, so L1 knows which channel decoder to use (FR, EFR, AMR, ...)

Also, we properly implement the initial codec mode selection as per TS
05.09
2011-09-09 23:30:46 +02:00
Harald Welte
06636b6155 AMR: change definition of amr_get_initial_mode() return value
AMR: return AMR_CODEC_MODE (0..3) instead of full range
2011-09-09 23:29:27 +02:00
Harald Welte
9508fb80a4 Introduce new amr.[ch] for AMR related functions 2011-09-09 22:32:45 +02:00
Harald Welte
4ccca1ce36 OML: make sure max_power_red is scaled by 2 to convert from 12.21 to dBm 2011-09-09 22:04:09 +02:00
Harald Welte
a4a3574b1d update osmo-bts to conform to L1 v2.4 API changes 2011-09-09 15:12:52 +02:00
Harald Welte
2c40d02f27 Inquire DSP/FPGA version at BTS boot and check band compatibility 2011-09-09 14:10:57 +02:00
Harald Welte
16c0ab92c1 add commands to configure RTP jitter buffer
there's one global setting for the BTS default value, plus an
interactive command to change the buffer of an active lchan on the fly
2011-09-08 15:21:39 +02:00
124 changed files with 16881 additions and 2404 deletions

29
.gitignore vendored
View File

@@ -4,17 +4,46 @@ Makefile.in
Makefile Makefile
.deps .deps
btsconfig.h
btsconfig.h.in
aclocal.m4 aclocal.m4
autom4te.cache autom4te.cache
config.log config.log
config.status config.status
config.guess
config.sub
configure configure
compile
depcomp depcomp
install-sh install-sh
missing missing
stamp-h1
core core
core.* core.*
contrib/sysmobts-calib/sysmobts-calib
src/osmo-bts-sysmo/l1fwd-proxy src/osmo-bts-sysmo/l1fwd-proxy
src/osmo-bts-sysmo/sysmobts src/osmo-bts-sysmo/sysmobts
src/osmo-bts-sysmo/sysmobts-remote src/osmo-bts-sysmo/sysmobts-remote
src/osmo-bts-sysmo/sysmobts-mgr
tests/atconfig
tests/package.m4
tests/agch/agch_test
tests/paging/paging_test
tests/cipher/cipher_test
tests/sysmobts/sysmobts_test
tests/misc/misc_test
tests/testsuite
tests/testsuite.log
# Possible generated file
doc/vty_reference.xml
# Backups, vi, merges
*.sw?
*.orig
*.sav

View File

@@ -1,3 +1,15 @@
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
SUBDIRS = include src SUBDIRS = include src tests
# package the contrib and doc
EXTRA_DIST = \
contrib/dump_docs.py contrib/screenrc-l1fwd contrib/sysmobts.service \
contrib/l1fwd.init contrib/screenrc-sysmobts contrib/respawn.sh \
contrib/sysmobts.init contrib/sysmobts-calib/Makefile \
contrib/sysmobts-calib/sysmobts-calib.c \
contrib/sysmobts-calib/sysmobts-layer1.c \
contrib/sysmobts-calib/sysmobts-layer1.h \
doc/examples/osmo-bts.cfg \
doc/examples/sysmobts-mgr.cfg

63
README
View File

@@ -1,10 +1,59 @@
Repsoitory for new BTS-side A-bis code Repository for new the Osmocom BTS implementation.
This is the code that was originally developed inside osmocom-bb.git This code implementes the Layer 2 and higher of a more or less
for turning modified OsmocomBB-supported phones into a simplistic BTS. conventional GSM BTS (Base Transceiver Station) - however, using an
Abis/IP interface, rather than the old-fashioned E1/T1.
Specificallt, 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
and the Layer1 interface.
Right now, only one hardware and Layer1 are supported: The sysmocom
sysmoBTS.
There is some experimental and way incomplete code to use a couple of
OsmocomBB phones and run them in the BTS. However, the required code
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.
== Known Limitations ==
As of June 3, 2012, the following known limitations exist in this
implementation:
=== Common Core ===
* No Extended BCCH support
* System Information limited to 1,2,2bis,2ter,2quater,3,4,5,6,9,13
* No RATSCCH in AMR
* No OML (TS 12.21) alarms yet (temperature, ...)
* Only single-TRX BTS at this point
* Will reject TS 12.21 STARTING TIME in SET BTS ATTR / SET CHAN ATTR
* 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
* No support of Bter frame / ENHANCED MEASUREMENT REPORT
=== 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.
However, the BTS-side A-bis is also going to be needed for other projects, thus
the split.
It doesn't really build yet, as a lot of dependencies have not yet been
resolved.

View File

@@ -4,6 +4,7 @@ AC_INIT([osmo-bts],
[openbsc-devel@lists.openbsc.org]) [openbsc-devel@lists.openbsc.org])
AM_INIT_AUTOMAKE([dist-bzip2]) AM_INIT_AUTOMAKE([dist-bzip2])
AC_CONFIG_TESTDIR(tests)
dnl kernel style compile messages dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -24,6 +25,8 @@ PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.9)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty) PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 0.0.7) PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 0.0.7)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3) PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis)
AC_MSG_CHECKING([whether to enable sysmocom-bts hardware support]) AC_MSG_CHECKING([whether to enable sysmocom-bts hardware support])
AC_ARG_ENABLE(sysmocom-bts, AC_ARG_ENABLE(sysmocom-bts,
@@ -33,11 +36,45 @@ AC_ARG_ENABLE(sysmocom-bts,
AC_MSG_RESULT([$enable_sysmocom_bts]) AC_MSG_RESULT([$enable_sysmocom_bts])
AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes") AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "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],
[AS_HELP_STRING([--with-openbsc=INCLUDE_DIR],
[OpenBSC include directory for openbsc/gsm_data_shared.h])],
[openbsc_incdir="$withval"],
[openbsc_incdir="`cd $srcdir; pwd`/../openbsc/openbsc/include"])
AC_SUBST([OPENBSC_INCDIR], $openbsc_incdir)
oldCPPFLAGS=$CPPFLAGS
CPPFLAGS="$CPPFLAGS -I$OPENBSC_INCDIR -I$srcdir/include $LIBOSMOCORE_CFLAGS"
AC_CHECK_HEADER([openbsc/gsm_data_shared.h],[],
[AC_MSG_ERROR([openbsc/gsm_data_shared.h can not be found in $openbsc_incdir])],
[#include <osmo-bts/tx_power.h>])
CPPFLAGS=$oldCPPFLAGS
# Check for the sbts2050_header.h that was added after the 3.6 release
oldCPPFLAGS=$CPPFLAGS
CPPFLAGS="$CPPFLAGS -I$OPENBSC_INCDIR $LIBOSMOCORE_CFLAGS"
AC_CHECK_HEADER([sysmocom/femtobts/sbts2050_header.h],
[sysmo_uc_header="yes"],[])
CPPFLAGS=$oldCPPFLAGS
if test "$sysmo_uc_header" = "yes" ; then
AC_DEFINE(BUILD_SBTS2050, 1, [Define if we want to build SBTS2050])
fi
AM_CONDITIONAL(BUILD_SBTS2050, test "x$sysmo_uc_header" = "xyes")
AM_CONFIG_HEADER(btsconfig.h)
AC_OUTPUT( AC_OUTPUT(
src/Makefile src/Makefile
src/common/Makefile src/common/Makefile
src/osmo-bts-sysmo/Makefile src/osmo-bts-sysmo/Makefile
src/osmo-bts-bb/Makefile
include/Makefile include/Makefile
include/osmo-bts/Makefile include/osmo-bts/Makefile
tests/Makefile
tests/paging/Makefile
tests/agch/Makefile
tests/cipher/Makefile
tests/sysmobts/Makefile
tests/misc/Makefile
Makefile) Makefile)

40
contrib/dump_docs.py Executable file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python
"""
Start the process and dump the documentation to the doc dir
"""
import socket, subprocess, time,os
env = os.environ
env['L1FWD_BTS_HOST'] = '127.0.0.1'
bts_proc = subprocess.Popen(["./src/osmo-bts-sysmo/sysmobts-remote",
"-c", "./doc/examples/osmo-bts.cfg"], env = env,
stdin=None, stdout=None)
time.sleep(1)
try:
sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sck.setblocking(1)
sck.connect(("localhost", 4241))
sck.recv(4096)
# Now send the command
sck.send("show online-help\r")
xml = ""
while True:
data = sck.recv(4096)
xml = "%s%s" % (xml, data)
if data.endswith('\r\nOsmoBTS> '):
break
# Now write everything until the end to the file
out = open('doc/vty_reference.xml', 'w')
out.write(xml[18:-11])
out.close()
finally:
# Clean-up
bts_proc.kill()
bts_proc.wait()

13
contrib/respawn-only.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
PID=$$
echo "-1000" > /proc/$PID/oom_score_adj
trap "{ kill 0; kill -2 0; }" EXIT
while [ -f $1 ]; do
(echo "0" > /proc/self/oom_score_adj && exec nice -n -20 $*) &
LAST_PID=$!
wait $LAST_PID
sleep 10s
done

View File

@@ -1,4 +1,18 @@
#!/bin/sh #!/bin/sh
PID=$$
echo "-1000" > /proc/$PID/oom_score_adj
trap "kill 0" EXIT
while [ -e /etc/passwd ]; do while [ -e /etc/passwd ]; do
$* cat /lib/firmware/sysmobts-v?.bit > /dev/fpgadl_par0
sleep 2s
cat /lib/firmware/sysmobts-v?.out > /dev/dspdl_dm644x_0
sleep 1s
echo "0" > /sys/class/leds/activity_led/brightness
(echo "0" > /proc/self/oom_score_adj && exec nice -n -20 $*) &
LAST_PID=$!
wait $LAST_PID
sleep 10s
done done

View File

@@ -1,3 +1,5 @@
chdir /tmp chdir /tmp
screen -t BTS 0 /etc/osmocom/respawn.sh /usr/bin/sysmobts -c /etc/osmocom/osmo-bts.cfg screen -t BTS 0 /etc/osmocom/respawn.sh /usr/bin/sysmobts -c /etc/osmocom/osmo-bts.cfg -r 1 -M
screen -t PCU 1 /etc/osmocom/respawn-only.sh /usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg -e
screen -t MGR 2 /etc/osmocom/respawn-only.sh /usr/bin/sysmobts-mgr -n -c /etc/osmocom/sysmobts-mgr.cfg
detach detach

View File

@@ -0,0 +1,10 @@
CFLAGS=`pkg-config --cflags libosmocore` -Wall -Werror
LIBS=`pkg-config --libs libosmocore libosmogsm`
all: sysmobts-calib
sysmobts-calib: sysmobts-calib.o sysmobts-layer1.o
$(CC) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ -lrt $(LIBS)
clean:
@rm -f sysmobts-calib *.o

View File

@@ -0,0 +1,537 @@
/* OCXO/TCXO based calibration utility */
/*
* (C) 2012-2013 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 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 <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <unistd.h>
#include <math.h>
#define _GNU_SOURCE
#include <getopt.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1types.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/utils.h>
#include "sysmobts-layer1.h"
enum actions {
ACTION_SCAN,
ACTION_CALIB,
ACTION_BCCH,
ACTION_BCCH_CCCH,
};
static const char *modes[] = {
[ACTION_SCAN] = "scan",
[ACTION_CALIB] = "calibrate",
[ACTION_BCCH] = "bcch",
[ACTION_BCCH_CCCH] = "bcch_ccch",
};
static const char *bands[] = {
[GsmL1_FreqBand_850] = "850",
[GsmL1_FreqBand_900] = "900",
[GsmL1_FreqBand_1800] = "1800",
[GsmL1_FreqBand_1900] = "1900",
};
struct channel_pair {
int min;
int max;
};
static const struct channel_pair arfcns[] = {
[GsmL1_FreqBand_850] = { .min = 128, .max = 251 },
[GsmL1_FreqBand_900] = { .min = 1, .max = 124 },
[GsmL1_FreqBand_1800] = { .min = 512, .max = 885 },
[GsmL1_FreqBand_1900] = { .min = 512, .max = 810 },
};
static const char *clk_source[] = {
[SuperFemto_ClkSrcId_Ocxo] = "ocxo",
[SuperFemto_ClkSrcId_Tcxo] = "tcxo",
[SuperFemto_ClkSrcId_External] = "external",
[SuperFemto_ClkSrcId_GpsPps] = "gps",
[SuperFemto_ClkSrcId_Trx] = "trx",
[SuperFemto_ClkSrcId_Rx] = "rx",
[SuperFemto_ClkSrcId_Edge] = "edge",
[SuperFemto_ClkSrcId_NetList] = "netlisten",
};
static const struct value_string sapi_names[GsmL1_Sapi_NUM+1] = {
{ GsmL1_Sapi_Fcch, "FCCH" },
{ GsmL1_Sapi_Sch, "SCH" },
{ GsmL1_Sapi_Sacch, "SACCH" },
{ GsmL1_Sapi_Sdcch, "SDCCH" },
{ GsmL1_Sapi_Bcch, "BCCH" },
{ GsmL1_Sapi_Pch, "PCH" },
{ GsmL1_Sapi_Agch, "AGCH" },
{ GsmL1_Sapi_Cbch, "CBCH" },
{ GsmL1_Sapi_Rach, "RACH" },
{ GsmL1_Sapi_TchF, "TCH/F" },
{ GsmL1_Sapi_FacchF, "FACCH/F" },
{ GsmL1_Sapi_TchH, "TCH/H" },
{ GsmL1_Sapi_FacchH, "FACCH/H" },
{ GsmL1_Sapi_Nch, "NCH" },
{ GsmL1_Sapi_Pdtch, "PDTCH" },
{ GsmL1_Sapi_Pacch, "PACCH" },
{ GsmL1_Sapi_Pbcch, "PBCCH" },
{ GsmL1_Sapi_Pagch, "PAGCH" },
{ GsmL1_Sapi_Ppch, "PPCH" },
{ GsmL1_Sapi_Pnch, "PNCH" },
{ GsmL1_Sapi_Ptcch, "PTCCH" },
{ GsmL1_Sapi_Prach, "PRACH" },
{ 0, NULL }
};
static int action = ACTION_SCAN;
static int band = GsmL1_FreqBand_900;
static int calib = SuperFemto_ClkSrcId_Ocxo;
static int source = SuperFemto_ClkSrcId_NetList;
static int dsp_flags = 0x0;
static int cal_arfcn = 0;
static int initial_cor = 0;
static int steps = -1;
static void print_usage(void)
{
printf("Usage: sysmobts-calib ARGS\n");
}
static void print_help(void)
{
printf(" -h --help this text\n");
printf(" -c --clock "
"ocxo|tcxo|external|gps|trx|rx|edge\n");
printf(" -s --calibration-source "
"ocxo|tcxo|external|gps|trx|rx|edge|netlisten\n");
printf(" -b --band 850|900|1800|1900\n");
printf(" -m --mode scan|calibrate|bcch|bcch_ccch\n");
printf(" -a --arfcn NR arfcn for calibration\n");
printf(" -d --dsp-flags NR dsp mask for debug log\n");
printf(" -t --threshold level\n");
printf(" -i --initial-clock-correction COR.\n");
printf(" -t --steps STEPS\n");
}
static int find_value(const char **array, int size, char *value)
{
int i = 0;
for (i = 0; i < size; ++i) {
if (array[i] == NULL)
continue;
if (strcmp(value, array[i]) == 0)
return i;
}
printf("Failed to find: '%s'\n", value);
exit(-2);
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"calibration-source", 1, 0, 's'},
{"clock", 1, 0, 'c'},
{"mode", 1, 0, 'm'},
{"band", 1, 0, 'b'},
{"dsp-flags", 1, 0, 'd'},
{"arfcn", 1, 0, 'a'},
{"initial-clock-correction", 1, 0, 'i'},
{"steps", 1, 0, 't'},
{0, 0, 0, 0},
};
c = getopt_long(argc, argv, "hs:c:m:b:d:a:i:t:",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
print_usage();
print_help();
exit(0);
case 's':
source = find_value(clk_source,
ARRAY_SIZE(clk_source), optarg);
break;
case 'c':
calib = find_value(clk_source,
ARRAY_SIZE(clk_source), optarg);
break;
case 'm':
action = find_value(modes,
ARRAY_SIZE(modes), optarg);
break;
case 'b':
band = find_value(bands,
ARRAY_SIZE(bands), optarg);
break;
case 'd':
dsp_flags = strtol(optarg, NULL, 16);
break;
case 'a':
cal_arfcn = atoi(optarg);
break;
case 'i':
initial_cor = atoi(optarg);
break;
case 't':
steps = atoi(optarg);
break;
default:
printf("Unhandled option, terminating.\n");
exit(-1);
}
}
if (source == calib) {
printf("Clock source and reference clock may not be the same.\n");
exit(-3);
}
if (calib == SuperFemto_ClkSrcId_NetList) {
printf("Clock may not be network listen.\n");
exit(-4);
}
if (action == ACTION_CALIB && source == SuperFemto_ClkSrcId_NetList) {
if (cal_arfcn == 0) {
printf("Please specify the reference ARFCN.\n");
exit(-5);
}
if (cal_arfcn < arfcns[band].min || cal_arfcn > arfcns[band].max) {
printf("ARFCN(%d) is not in the given band.\n", cal_arfcn);
exit(-6);
}
}
}
#define CHECK_RC(rc) \
if (rc != 0) \
return EXIT_FAILURE;
#define CHECK_RC_MSG(rc, msg) \
if (rc != 0) { \
printf("%s: %d\n", msg, rc); \
return EXIT_FAILURE; \
}
#define CHECK_COND_MSG(cond, rc, msg) \
if (cond) { \
printf("%s: %d\n", msg, rc); \
return EXIT_FAILURE; \
}
struct scan_result
{
uint16_t arfcn;
float rssi;
};
static int scan_cmp(const void *arg1, const void *arg2)
{
struct scan_result *elem1 = (struct scan_result *) arg1;
struct scan_result *elem2 = (struct scan_result * )arg2;
float diff = elem1->rssi - elem2->rssi;
if (diff > 0.0)
return 1;
else if (diff < 0.0)
return -1;
else
return 0;
}
static int scan_band()
{
int arfcn, rc, i;
/* Scan results.. at most 400 items */
struct scan_result results[400];
memset(&results, 0, sizeof(results));
int num_scan_results = 0;
printf("Going to scan bands.\n");
for (arfcn = arfcns[band].min; arfcn <= arfcns[band].max; ++arfcn) {
float mean_rssi;
printf(".");
fflush(stdout);
rc = power_scan(band, arfcn, 10, &mean_rssi);
CHECK_RC_MSG(rc, "Power Measurement failed");
results[num_scan_results].arfcn = arfcn;
results[num_scan_results].rssi = mean_rssi;
num_scan_results++;
}
qsort(results, num_scan_results, sizeof(struct scan_result), scan_cmp);
printf("\nSorted scan results (weakest first):\n");
for (i = 0; i < num_scan_results; ++i)
printf("ARFCN %3d: %.4f\n", results[i].arfcn, results[i].rssi);
return 0;
}
static int calib_get_clock_error(void)
{
int rc, clkErr, clkErrRes;
printf("Going to determine the clock offset.\n");
rc = rf_clock_info(&clkErr, &clkErrRes);
CHECK_RC_MSG(rc, "Clock info failed.\n");
if (clkErr == 0 && clkErrRes == 0) {
printf("Failed to get the clock info. Are both clocks present?\n");
return -1;
}
/*
* Empiric gps error determination. With revE and firmware v3.3
* the clock error for TCXO to GPS appears to have a different
* sign. The device in question doesn't have a networklisten mode
* so it is impossible to verify that this only applies to GPS.
*/
if (source == SuperFemto_ClkSrcId_GpsPps)
clkErr *= -1;
/* this is an absolute clock error */
printf("The calibration value is: %d\n", clkErr);
return 0;
}
static int calib_clock_after_sync(void)
{
int rc, clkErr, clkErrRes, iteration, cor;
iteration = 0;
cor = initial_cor;
printf("Trying to calibrate now and reducing clock error.\n");
for (iteration = 0; iteration < steps || steps <= 0; ++iteration) {
if (steps > 0)
printf("Iteration %d/%d with correction: %d\n", iteration, steps, cor);
else
printf("Iteration %d with correction: %d\n", iteration, cor);
rc = rf_clock_info(&clkErr, &clkErrRes);
CHECK_RC_MSG(rc, "Clock info failed.\n");
/*
* TODO: use the clock error resolution here, implement it as a
* a PID controller..
*/
/* Picocell class requires 0.1ppm.. but that is 'too easy' */
if (fabs(clkErr / 1000.0f) <= 0.05f) {
printf("The calibration value is: %d\n", cor);
return 1;
}
cor -= clkErr / 2;
rc = set_clock_cor(cor, calib, source);
CHECK_RC_MSG(rc, "Clock correction failed.\n");
}
return -1;
}
static int find_initial_clock(HANDLE layer1, int *clock)
{
int i;
printf("Trying to find an initial clock value.\n");
for (i = 0; i < 1000; ++i) {
int rc;
int cor = i * 150;
rc = wait_for_sync(layer1, cor, calib, source);
if (rc == 1) {
printf("Found initial clock offset: %d\n", cor);
*clock = cor;
break;
} else {
CHECK_RC_MSG(rc, "Failed to set new clock value.\n");
}
cor = i * -150;
rc = wait_for_sync(layer1, cor, calib, source);
if (rc == 1) {
printf("Found initial clock offset: %d\n", cor);
*clock = cor;
break;
} else {
CHECK_RC_MSG(rc, "Failed to set new clock value.\n");
}
}
return 0;
}
static int calib_clock_netlisten(void)
{
int rc, cor = initial_cor;
float mean_rssi;
HANDLE layer1;
rc = power_scan(band, cal_arfcn, 10, &mean_rssi);
CHECK_RC_MSG(rc, "ARFCN measurement scan failed");
if (mean_rssi < -118.0f)
printf("ARFCN has weak signal for calibration: %f\n", mean_rssi);
/* initial lock */
rc = follow_sch(band, cal_arfcn, calib, source, &layer1);
if (rc == -23)
rc = find_initial_clock(layer1, &cor);
CHECK_RC_MSG(rc, "Following SCH failed");
/* now try to calibrate it */
rc = set_clock_cor(cor, calib, source);
CHECK_RC_MSG(rc, "Clock setup failed.");
calib_clock_after_sync();
rc = mph_close(layer1);
CHECK_RC_MSG(rc, "MPH-Close");
return EXIT_SUCCESS;
}
static int calib_clock(void)
{
int rc;
/* now try to calibrate it */
rc = set_clock_cor(initial_cor, calib, source);
CHECK_RC_MSG(rc, "Clock setup failed.");
calib_get_clock_error();
return EXIT_SUCCESS;
}
static int bcch_follow(void)
{
int rc, cor = initial_cor;
float mean_rssi;
HANDLE layer1;
rc = power_scan(band, cal_arfcn, 10, &mean_rssi);
CHECK_RC_MSG(rc, "ARFCN measurement scan failed");
if (mean_rssi < -118.0f)
printf("ARFCN has weak signal for calibration: %f\n", mean_rssi);
/* initial lock */
rc = follow_sch(band, cal_arfcn, calib, source, &layer1);
if (rc == -23)
rc = find_initial_clock(layer1, &cor);
CHECK_RC_MSG(rc, "Following SCH failed");
/* identify the BSIC and set it as TSC */
rc = find_bsic();
CHECK_COND_MSG(rc < 0, rc, "Identifying the BSIC failed");
rc = set_tsc_from_bsic(layer1, rc);
CHECK_RC_MSG(rc, "Setting the TSC failed");
/* follow the bcch */
rc = follow_bcch(layer1);
CHECK_RC_MSG(rc, "Follow BCCH");
/* follow the pch */
if (action == ACTION_BCCH_CCCH) {
rc = follow_pch(layer1);
CHECK_RC_MSG(rc, "Follow BCCH/CCCH");
}
/* now wait for the PhDataInd */
for (;;) {
uint32_t fn;
uint8_t block;
uint8_t data[23];
size_t size;
struct gsm_time gsmtime;
GsmL1_Sapi_t sapi;
rc = wait_for_data(data, &size, &fn, &block, &sapi);
if (rc == 1)
continue;
CHECK_RC_MSG(rc, "No Data Indication");
gsm_fn2gsmtime(&gsmtime, fn);
printf("%02u/%02u/%02u %6s %s\n",
gsmtime.t1, gsmtime.t2, gsmtime.t3,
get_value_string(sapi_names, sapi),
osmo_hexdump(data, size));
}
rc = mph_close(layer1);
CHECK_RC_MSG(rc, "MPH-Close");
return EXIT_SUCCESS;
}
int main(int argc, char **argv)
{
int rc;
handle_options(argc, argv);
printf("Initializing the Layer1\n");
rc = initialize_layer1(dsp_flags);
CHECK_RC(rc);
printf("Fetching system info.\n");
rc = print_system_info();
CHECK_RC(rc);
printf("Opening RF frontend with clock(%d) and correction(%d)\n",
calib, initial_cor);
rc = activate_rf_frontend(calib, initial_cor);
CHECK_RC(rc);
if (action == ACTION_SCAN)
return scan_band();
else if (action == ACTION_BCCH || action == ACTION_BCCH_CCCH)
return bcch_follow();
else {
if (source == SuperFemto_ClkSrcId_NetList)
return calib_clock_netlisten();
return calib_clock();
}
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,783 @@
/* Layer1 handling for the DSP/FPGA */
/*
* (C) 2012-2013 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 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 <stdlib.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h>
#include "sysmobts-layer1.h"
#define ARRAY_SIZE(ar) (sizeof(ar)/sizeof((ar)[0]))
#define BTS_DSP2ARM "/dev/msgq/superfemto_dsp2arm"
#define BTS_ARM2DSP "/dev/msgq/superfemto_arm2dsp"
#define L1_SIG_ARM2DSP "/dev/msgq/gsml1_sig_arm2dsp"
#define L1_SIG_DSP2ARM "/dev/msgq/gsml1_sig_dsp2arm"
int set_clock_cor(int clock_cor, int calib, int source);
static int wait_read_ignore(int seconds);
static int sys_dsp2arm = -1,
sys_arm2dsp = -1,
sig_dsp2arm = -1,
sig_arm2dsp = -1;
static int sync_indicated = 0;
static int time_indicated = 0;
static int open_devices()
{
sys_dsp2arm = open(BTS_DSP2ARM, O_RDONLY);
if (sys_dsp2arm == -1) {
perror("Failed to open dsp2arm system queue");
return -1;
}
sys_arm2dsp = open(BTS_ARM2DSP, O_WRONLY);
if (sys_arm2dsp == -1) {
perror("Failed to open arm2dsp system queue");
return -2;
}
sig_dsp2arm = open(L1_SIG_DSP2ARM, O_RDONLY);
if (sig_dsp2arm == -1) {
perror("Failed to open dsp2arm sig queue");
return -3;
}
sig_arm2dsp = open(L1_SIG_ARM2DSP, O_WRONLY);
if (sig_arm2dsp == -1) {
perror("Failed to open arm2dsp sig queue");
return -4;
}
return 0;
}
/**
* Send a primitive to the system queue
*/
static int send_primitive(int primitive, SuperFemto_Prim_t *prim)
{
prim->id = primitive;
return write(sys_arm2dsp, prim, sizeof(*prim)) != sizeof(*prim);
}
/**
* Wait for a confirmation
*/
static int wait_primitive(int wait_for, SuperFemto_Prim_t *prim)
{
memset(prim, 0, sizeof(*prim));
int rc = read(sys_dsp2arm, prim, sizeof(*prim));
if (rc != sizeof(*prim)) {
printf("Short read in %s: %d\n", __func__, rc);
return -1;
}
if (prim->id != wait_for) {
printf("Got primitive %d but waited for %d\n",
prim->id, wait_for);
return -2;
}
return 0;
}
/* The Cnf for the Req, assume it is a +1 */
static int answer_for(int primitive)
{
return primitive + 1;
}
static int send_recv_primitive(int p, SuperFemto_Prim_t *prim)
{
int rc;
rc = send_primitive(p, prim);
if (rc != 0)
return -1;
rc = wait_primitive(answer_for(p), prim);
if (rc != 0)
return -2;
return 0;
}
static int answer_for_sig(int prim)
{
static const GsmL1_PrimId_t cnf[] = {
[GsmL1_PrimId_MphInitReq] = GsmL1_PrimId_MphInitCnf,
[GsmL1_PrimId_MphCloseReq] = GsmL1_PrimId_MphCloseCnf,
[GsmL1_PrimId_MphConnectReq] = GsmL1_PrimId_MphConnectCnf,
[GsmL1_PrimId_MphActivateReq] = GsmL1_PrimId_MphActivateCnf,
[GsmL1_PrimId_MphConfigReq] = GsmL1_PrimId_MphConfigCnf,
[GsmL1_PrimId_MphMeasureReq] = GsmL1_PrimId_MphMeasureCnf,
};
if (prim < 0 || prim >= ARRAY_SIZE(cnf)) {
printf("Unknown primitive: %d\n", prim);
exit(-3);
}
return cnf[prim];
}
static int is_indication(int prim)
{
return
prim == GsmL1_PrimId_MphTimeInd ||
prim == GsmL1_PrimId_MphSyncInd ||
prim == GsmL1_PrimId_PhConnectInd ||
prim == GsmL1_PrimId_PhReadyToSendInd ||
prim == GsmL1_PrimId_PhDataInd ||
prim == GsmL1_PrimId_PhRaInd;
}
static int send_recv_sig_prim(int p, GsmL1_Prim_t *prim)
{
int rc;
prim->id = p;
rc = write(sig_arm2dsp, prim, sizeof(*prim));
if (rc != sizeof(*prim)) {
printf("Failed to write: %d\n", rc);
return -1;
}
do {
rc = read(sig_dsp2arm, prim, sizeof(*prim));
if (rc != sizeof(*prim)) {
printf("Failed to read: %d\n", rc);
return -2;
}
} while (is_indication(prim->id));
if (prim->id != answer_for_sig(p)) {
printf("Wrong L1 result got %d wanted %d for prim: %d\n",
prim->id, answer_for_sig(p), p);
return -3;
}
return 0;
}
static int wait_for_indication(int p, GsmL1_Prim_t *prim)
{
int rc;
memset(prim, 0, sizeof(*prim));
struct timespec start_time, now_time;
clock_gettime(CLOCK_MONOTONIC, &start_time);
/*
* TODO: select.... with timeout. The below will work 99% as we will
* get time indications very soonish after the connect
*/
for (;;) {
clock_gettime(CLOCK_MONOTONIC, &now_time);
if (now_time.tv_sec - start_time.tv_sec > 10) {
printf("Timeout waiting for indication.\n");
return -4;
}
rc = read(sig_dsp2arm, prim, sizeof(*prim));
if (rc != sizeof(*prim)) {
printf("Failed to read.\n");
return -1;
}
if (!is_indication(prim->id)) {
printf("No indication: %d\n", prim->id);
return -2;
}
if (p != prim->id && prim->id == GsmL1_PrimId_MphSyncInd) {
printf("Got sync.\n");
sync_indicated = 1;
continue;
}
if (p != prim->id && prim->id == GsmL1_PrimId_MphTimeInd) {
time_indicated = 1;
continue;
}
if (p != prim->id) {
printf("Wrong indication got %d wanted %d\n",
prim->id, p);
return -3;
}
break;
}
return 0;
}
static int set_trace_flags(uint32_t dsp)
{
SuperFemto_Prim_t prim;
memset(&prim, 0, sizeof(prim));
prim.u.setTraceFlagsReq.u32Tf = dsp;
return send_primitive(SuperFemto_PrimId_SetTraceFlagsReq, &prim);
}
static int reset_and_wait()
{
int rc;
SuperFemto_Prim_t prim;
memset(&prim, 0, sizeof(prim));
rc = send_recv_primitive(SuperFemto_PrimId_Layer1ResetReq, &prim);
if (rc != 0)
return -1;
if (prim.u.layer1ResetCnf.status != GsmL1_Status_Success)
return -2;
return 0;
}
/**
* Open the message queues and (re-)initialize the DSP and FPGA
*/
int initialize_layer1(uint32_t dsp_flags)
{
if (open_devices() != 0) {
printf("Failed to open devices.\n");
return -1;
}
if (set_trace_flags(dsp_flags) != 0) {
printf("Failed to set dsp flags.\n");
return -2;
}
if (reset_and_wait() != 0) {
printf("Failed to reset the firmware.\n");
return -3;
}
return 0;
}
/**
* Print systems infos
*/
int print_system_info()
{
int rc;
SuperFemto_Prim_t prim;
memset(&prim, 0, sizeof(prim));
rc = send_recv_primitive(SuperFemto_PrimId_SystemInfoReq, &prim);
if (rc != 0) {
printf("Failed to send SystemInfoRequest.\n");
return -1;
}
if (prim.u.systemInfoCnf.status != GsmL1_Status_Success) {
printf("Failed to request SystemInfoRequest.\n");
return -2;
}
#define INFO_DSP(x) x.u.systemInfoCnf.dspVersion
#define INFO_FPGA(x) x.u.systemInfoCnf.fpgaVersion
#ifdef FEMTOBTS_NO_BOARD_VERSION
#define BOARD_REV(x) -1
#define BOARD_OPT(x) -1
#else
#define BOARD_REV(x) x.u.systemInfoCnf.boardVersion.rev
#define BOARD_OPT(x) x.u.systemInfoCnf.boardVersion.option
#endif
printf("DSP v%d.%d.%d FPGA v%d.%d.%d Rev: %d Option: %d\n",
INFO_DSP(prim).major, INFO_DSP(prim).minor, INFO_DSP(prim).build,
INFO_FPGA(prim).major, INFO_FPGA(prim).minor, INFO_FPGA(prim).build,
BOARD_REV(prim), BOARD_OPT(prim));
#undef INFO_DSP
#undef INFO_FPGA
#undef BOARD_REV
#undef BOARD_OPT
return 0;
}
int activate_rf_frontend(int clock_source, int initial_cor)
{
int rc;
SuperFemto_Prim_t prim;
memset(&prim, 0, sizeof(prim));
prim.u.activateRfReq.timing.u8TimSrc = 1;
prim.u.activateRfReq.msgq.u8UseTchMsgq = 0;
prim.u.activateRfReq.msgq.u8UsePdtchMsgq = 0;
prim.u.activateRfReq.rfTrx.iClkCor = initial_cor;
prim.u.activateRfReq.rfTrx.clkSrc = clock_source;
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
prim.u.activateRfReq.rfRx.iClkCor = initial_cor;
prim.u.activateRfReq.rfRx.clkSrc = clock_source;
#endif
rc = send_recv_primitive(SuperFemto_PrimId_ActivateRfReq, &prim);
return rc;
}
static int mph_init(int band, int arfcn, HANDLE *layer1)
{
int rc;
GsmL1_Prim_t prim;
memset(&prim, 0, sizeof(prim));
prim.u.mphInitReq.deviceParam.devType = GsmL1_DevType_Rxd;
prim.u.mphInitReq.deviceParam.freqBand = band;
prim.u.mphInitReq.deviceParam.u16Arfcn = arfcn;
prim.u.mphInitReq.deviceParam.u16BcchArfcn = arfcn;
prim.u.mphInitReq.deviceParam.fRxPowerLevel = -75.f;
prim.u.mphInitReq.deviceParam.u8AutoTA = 1;
rc = send_recv_sig_prim(GsmL1_PrimId_MphInitReq, &prim);
if (rc != 0) {
printf("Failed to initialize the physical channel.\n");
return -1;
}
if (prim.u.mphInitCnf.status != GsmL1_Status_Success) {
printf("MPH Init failed.\n");
return -2;
}
#if 0
if (prim.u.mphInitCnf.freqBand != band) {
printf("Layer1 ignored the band: %d\n",
prim.u.mphInitCnf.freqBand);
return -3;
}
#endif
*layer1 = prim.u.mphInitCnf.hLayer1;
return 0;
}
int mph_close(HANDLE layer1)
{
int rc;
GsmL1_Prim_t prim;
memset(&prim, 0, sizeof(prim));
prim.u.mphCloseReq.hLayer1 = layer1;
rc = send_recv_sig_prim(GsmL1_PrimId_MphCloseReq, &prim);
if (rc != 0) {
printf("Failed to close the MPH\n");
return -6;
}
if (prim.u.mphCloseCnf.status != GsmL1_Status_Success) {
printf("MPH Close failed.\n");
return -7;
}
return 0;
}
int follow_sch(int band, int arfcn, int clock, int ref, HANDLE *layer1)
{
int rc;
GsmL1_Prim_t prim;
time_indicated = 0;
sync_indicated = 0;
rc = mph_init(band, arfcn, layer1);
if (rc != 0)
return rc;
/* 1.) Connect */
memset(&prim, 0, sizeof(prim));
prim.u.mphConnectReq.hLayer1 = *layer1;
prim.u.mphConnectReq.u8Tn = 0;
prim.u.mphConnectReq.logChComb = GsmL1_LogChComb_IV;
rc = send_recv_sig_prim(GsmL1_PrimId_MphConnectReq, &prim);
if (rc != 0) {
printf("Failed to connect.\n");
return -1;
}
if (prim.u.mphConnectCnf.status != GsmL1_Status_Success) {
printf("Connect failed.\n");
return -2;
}
if (prim.u.mphConnectCnf.u8Tn != 0) {
printf("Wrong timeslot.\n");
return -3;
}
/* 2.) Activate */
memset(&prim, 0, sizeof(prim));
prim.u.mphActivateReq.hLayer1 = *layer1;
prim.u.mphActivateReq.u8Tn = 0;
prim.u.mphActivateReq.sapi = GsmL1_Sapi_Sch;
prim.u.mphActivateReq.dir = GsmL1_Dir_RxDownlink;
rc = send_recv_sig_prim(GsmL1_PrimId_MphActivateReq, &prim);
if (rc != 0) {
printf("Activation failed.\n");
return -4;
}
if (prim.u.mphActivateCnf.status != GsmL1_Status_Success) {
printf("Activation not successful.\n");
return -5;
}
/* 3.) Wait for indication... TODO: check... */
printf("Waiting for connect indication.\n");
rc = wait_for_indication(GsmL1_PrimId_PhConnectInd, &prim);
if (rc != 0) {
printf("Didn't get a connect indication.\n");
return rc;
}
/* 4.) Indication Syndication TODO: check... */
if (!sync_indicated) {
printf("Waiting for sync indication.\n");
rc = wait_for_indication(GsmL1_PrimId_MphSyncInd, &prim);
if (rc < 0) {
printf("Didn't get a sync indication.\n");
return -23;
} else if (rc == 0) {
if (!prim.u.mphSyncInd.u8Synced) {
printf("Failed to get sync.\n");
return -23;
} else {
printf("Synced.\n");
}
}
} else {
printf("Already synced.\n");
}
return 0;
}
static int follow_sapi(HANDLE layer1, const GsmL1_Sapi_t sapi)
{
int rc;
GsmL1_Prim_t prim;
/* 1.) Activate BCCH or such... */
memset(&prim, 0, sizeof(prim));
prim.u.mphActivateReq.hLayer1 = layer1;
prim.u.mphActivateReq.u8Tn = 0;
prim.u.mphActivateReq.sapi = sapi;
prim.u.mphActivateReq.dir = GsmL1_Dir_RxDownlink;
rc = send_recv_sig_prim(GsmL1_PrimId_MphActivateReq, &prim);
if (rc != 0) {
printf("Activation failed.\n");
return -4;
}
if (prim.u.mphActivateCnf.status != GsmL1_Status_Success) {
printf("Activation not successful.\n");
return -5;
}
/* 2.) Wait for indication... */
printf("Waiting for connect indication.\n");
rc = wait_for_indication(GsmL1_PrimId_PhConnectInd, &prim);
if (rc != 0) {
printf("Didn't get a connect indication.\n");
return rc;
}
if (prim.u.phConnectInd.sapi != sapi) {
printf("Got a connect indication for the wrong type: %d\n",
prim.u.phConnectInd.sapi);
return -6;
}
/* 3.) Wait for PhDataInd... */
printf("Waiting for data.\n");
rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim);
if (rc != 0) {
printf("Didn't get data.\n");
return rc;
}
return 0;
}
int follow_bcch(HANDLE layer1)
{
return follow_sapi(layer1, GsmL1_Sapi_Bcch);
}
int follow_pch(HANDLE layer1)
{
return follow_sapi(layer1, GsmL1_Sapi_Pch);
}
int find_bsic(void)
{
int rc, i;
GsmL1_Prim_t prim;
printf("Waiting for SCH data.\n");
for (i = 0; i < 10; ++i) {
uint8_t bsic;
rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim);
if (rc < 0) {
printf("Didn't get SCH data.\n");
return rc;
}
if (prim.u.phDataInd.sapi != GsmL1_Sapi_Sch)
continue;
bsic = (prim.u.phDataInd.msgUnitParam.u8Buffer[0] >> 2) & 0xFF;
return bsic;
}
printf("Giving up finding the SCH\n");
return -1;
}
int set_tsc_from_bsic(HANDLE layer1, int bsic)
{
int rc;
int tsc = bsic & 0x7;
GsmL1_Prim_t prim;
memset(&prim, 0, sizeof(prim));
prim.u.mphConfigReq.hLayer3 = 0x23;
prim.u.mphConfigReq.hLayer1 = layer1;
prim.u.mphConfigReq.cfgParamId = GsmL1_ConfigParamId_SetNbTsc;
prim.u.mphConfigReq.cfgParams.setNbTsc.u8NbTsc = tsc;
rc = send_recv_sig_prim(GsmL1_PrimId_MphConfigReq, &prim);
if (rc != 0) {
printf("Failed to send configure.\n");
}
if (prim.u.mphConfigCnf.status != GsmL1_Status_Success) {
printf("Failed to set the config cnf.\n");
return -1;
}
return 0;
}
int set_clock_cor(int clock_cor, int calib, int source)
{
int rc;
SuperFemto_Prim_t prim;
memset(&prim, 0, sizeof(prim));
prim.u.rfClockSetupReq.rfTrx.iClkCor = clock_cor;
prim.u.rfClockSetupReq.rfTrx.clkSrc = calib;
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
prim.u.rfClockSetupReq.rfRx.iClkCor = clock_cor;
prim.u.rfClockSetupReq.rfRx.clkSrc = calib;
#endif
prim.u.rfClockSetupReq.rfTrxClkCal.clkSrc = source;
rc = send_recv_primitive(SuperFemto_PrimId_RfClockSetupReq, &prim);
if (rc != 0) {
printf("Failed to set the clock setup.\n");
return -1;
}
if (prim.u.rfClockSetupCnf.status != GsmL1_Status_Success) {
printf("Clock setup was not successfull.\n");
return -2;
}
return 0;
}
int rf_clock_info(int *clkErr, int *clkErrRes)
{
SuperFemto_Prim_t prim;
memset(&prim, 0, sizeof(prim));
int rc;
/* reset the counter */
prim.u.rfClockInfoReq.u8RstClkCal = 1;
rc = send_recv_primitive(SuperFemto_PrimId_RfClockInfoReq, &prim);
if (rc != 0) {
printf("Failed to reset the clock info.\n");
return -1;
}
/* wait for a value */
wait_read_ignore(15);
/* ask for the current counter/error */
memset(&prim, 0, sizeof(prim));
prim.u.rfClockInfoReq.u8RstClkCal = 0;
rc = send_recv_primitive(SuperFemto_PrimId_RfClockInfoReq, &prim);
if (rc != 0) {
printf("Failed to get the clock info.\n");
return -2;
}
printf("Error: %d Res: %d\n",
prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErr,
prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErrRes);
*clkErr = prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErr;
*clkErrRes = prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErrRes;
return 0;
}
int power_scan(int band, int arfcn, int duration, float *mean_rssi)
{
int rc;
HANDLE layer1;
GsmL1_Prim_t prim;
/* init */
rc = mph_init(band, arfcn, &layer1);
if (rc != 0)
return rc;
/* mph measure request */
memset(&prim, 0, sizeof(prim));
prim.u.mphMeasureReq.hLayer1 = layer1;
prim.u.mphMeasureReq.u32Duration = duration;
rc = send_recv_sig_prim(GsmL1_PrimId_MphMeasureReq, &prim);
if (rc != 0) {
printf("Failed to send measurement request.\n");
return -4;
}
if (prim.u.mphMeasureCnf.status != GsmL1_Status_Success) {
printf("MphMeasureReq was not confirmed.\n");
return -5;
}
*mean_rssi = prim.u.mphMeasureCnf.fMeanRssi;
/* close */
rc = mph_close(layer1);
return rc;
}
/**
* Wait for indication...
*/
int wait_for_sync(HANDLE layer1, int cor, int calib, int source)
{
GsmL1_Prim_t prim;
int rc;
rc = set_clock_cor(cor, calib, source);
if (rc != 0) {
printf("Failed to set the clock correction.\n");
return -1;
}
sync_indicated = 0;
rc = wait_for_indication(GsmL1_PrimId_MphSyncInd, &prim);
if (rc < 0 && rc != -4) {
return rc;
} else if (rc == 0) {
if (!prim.u.mphSyncInd.u8Synced) {
printf("Failed to get sync.\n");
return 0;
}
printf("Synced.\n");
return 1;
}
return 0;
}
int wait_for_data(uint8_t *data, size_t *size, uint32_t *fn, uint8_t *block, GsmL1_Sapi_t *sap)
{
GsmL1_Prim_t prim;
int rc;
rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim);
if (rc < 0)
return rc;
if (prim.u.phDataInd.sapi == GsmL1_Sapi_Sch)
return 1;
*size = prim.u.phDataInd.msgUnitParam.u8Size;
*fn = prim.u.phDataInd.u32Fn;
*block = prim.u.phDataInd.u8BlockNbr;
*sap = prim.u.phDataInd.sapi;
memcpy(data, prim.u.phDataInd.msgUnitParam.u8Buffer, *size);
return 0;
}
/**
* Make sure the pipe is not running full.
*
*/
static int wait_read_ignore(int seconds)
{
int max, rc;
fd_set fds;
struct timeval timeout;
max = sys_dsp2arm > sig_dsp2arm ? sys_dsp2arm : sig_dsp2arm;
timeout.tv_sec = seconds;
timeout.tv_usec = 0;
while (1) {
FD_ZERO(&fds);
FD_SET(sys_dsp2arm, &fds);
FD_SET(sig_dsp2arm, &fds);
rc = select(max + 1, &fds, NULL, NULL, &timeout);
if (rc == -1) {
printf("Failed to select.\n");
return -1;
} else if (rc) {
if (FD_ISSET(sys_dsp2arm, &fds)) {
SuperFemto_Prim_t prim;
rc = read(sys_dsp2arm, &prim, sizeof(prim));
if (rc != sizeof(prim)) {
perror("Failed to read system primitive");
return -2;
}
}
if (FD_ISSET(sig_dsp2arm, &fds)) {
GsmL1_Prim_t prim;
rc = read(sig_dsp2arm, &prim, sizeof(prim));
if (rc != sizeof(prim)) {
perror("Failed to read signal primitiven");
return -3;
}
}
} else if (timeout.tv_sec <= 0 && timeout.tv_usec <= 0) {
break;
}
#ifndef __linux__
#error "Non portable code"
#endif
}
return 0;
}

View File

@@ -0,0 +1,45 @@
#ifndef SYSMOBTS_LAYER_H
#define SYSMOBTS_LAYER_H
#include <sysmocom/femtobts/superfemto.h>
#ifdef FEMTOBTS_API_VERSION
#define SuperFemto_PrimId_t FemtoBts_PrimId_t
#define SuperFemto_Prim_t FemtoBts_Prim_t
#define SuperFemto_PrimId_SystemInfoReq FemtoBts_PrimId_SystemInfoReq
#define SuperFemto_PrimId_SystemInfoCnf FemtoBts_PrimId_SystemInfoCnf
#define SuperFemto_SystemInfoCnf_t FemtoBts_SystemInfoCnf_t
#define SuperFemto_PrimId_SystemFailureInd FemtoBts_PrimId_SystemFailureInd
#define SuperFemto_PrimId_ActivateRfReq FemtoBts_PrimId_ActivateRfReq
#define SuperFemto_PrimId_ActivateRfCnf FemtoBts_PrimId_ActivateRfCnf
#define SuperFemto_PrimId_DeactivateRfReq FemtoBts_PrimId_DeactivateRfReq
#define SuperFemto_PrimId_DeactivateRfCnf FemtoBts_PrimId_DeactivateRfCnf
#define SuperFemto_PrimId_SetTraceFlagsReq FemtoBts_PrimId_SetTraceFlagsReq
#define SuperFemto_PrimId_RfClockInfoReq FemtoBts_PrimId_RfClockInfoReq
#define SuperFemto_PrimId_RfClockInfoCnf FemtoBts_PrimId_RfClockInfoCnf
#define SuperFemto_PrimId_RfClockSetupReq FemtoBts_PrimId_RfClockSetupReq
#define SuperFemto_PrimId_RfClockSetupCnf FemtoBts_PrimId_RfClockSetupCnf
#define SuperFemto_PrimId_Layer1ResetReq FemtoBts_PrimId_Layer1ResetReq
#define SuperFemto_PrimId_Layer1ResetCnf FemtoBts_PrimId_Layer1ResetCnf
#define SuperFemto_PrimId_NUM FemtoBts_PrimId_NUM
#define HW_SYSMOBTS_V1 1
#define SUPERFEMTO_API(x,y,z) FEMTOBTS_API(x,y,z)
#endif
extern int initialize_layer1(uint32_t dsp_flags);
extern int print_system_info();
extern int activate_rf_frontend(int clock_source, int clock_cor);
extern int power_scan(int band, int arfcn, int duration, float *mean_rssi);
extern int follow_sch(int band, int arfcn, int calib, int reference, HANDLE *layer1);
extern int follow_bch(HANDLE layer1);
extern int find_bsic(void);
extern int set_tsc_from_bsic(HANDLE layer1, int bsic);
extern int set_clock_cor(int clock_corr, int calib, int source);
extern int rf_clock_info(int *clkErr, int *clkErrRes);
extern int mph_close(HANDLE layer1);
extern int wait_for_sync(HANDLE layer1, int cor, int calib, int source);
extern int follow_bcch(HANDLE layer1);
extern int follow_pch(HANDLE layer1);
extern int wait_for_data(uint8_t *data, size_t *size, uint32_t *fn, uint8_t *block, GsmL1_Sapi_t *sapi);
#endif

View File

@@ -0,0 +1,12 @@
[Unit]
Description=sysmocom sysmoBTS manager
[Service]
Type=simple
ExecStart=/usr/bin/sysmobts-mgr -ns -c /etc/osmocom/sysmobts-mgr.cfg
Restart=always
RestartSec=2
[Install]
WantedBy=multi-user.target

View File

@@ -9,14 +9,12 @@
# Description: # Description:
### END INIT INFO ### END INIT INFO
. /etc/default/rcS
case "$1" in case "$1" in
start) start)
/usr/bin/screen -d -m -c /etc/osmocom/screenrc-sysmobts /usr/bin/screen -d -m -c /etc/osmocom/screenrc-sysmobts -S sysmobts
;; ;;
stop) stop)
echo "This script doesn't support stop" /usr/bin/screen -d -r sysmobts -X quit
exit 1 exit 1
;; ;;
restart|reload|force-reload) restart|reload|force-reload)

19
contrib/sysmobts.service Normal file
View File

@@ -0,0 +1,19 @@
[Unit]
Description=sysmocom sysmoBTS
[Service]
Type=simple
ExecStartPre=/bin/sh -c 'echo 0 > /sys/class/leds/activity_led/brightness'
ExecStart=/usr/bin/sysmobts -s -c /etc/osmocom/osmo-bts.cfg -M
ExecStopPost=/bin/sh -c 'echo 0 > /sys/class/leds/activity_led/brightness'
ExecStopPost=/bin/sh -c 'cat /lib/firmware/sysmobts-v?.bit > /dev/fpgadl_par0 ; sleep 3s; cat /lib/firmware/sysmobts-v?.out > /dev/dspdl_dm644x_0; sleep 1s'
Restart=always
RestartSec=2
RestartPreventExitStatus=1
# The msg queues must be read fast enough
CPUSchedulingPolicy=rr
CPUSchedulingPriority=1
[Install]
WantedBy=multi-user.target

61
doc/control_interface.txt Normal file
View File

@@ -0,0 +1,61 @@
The osmo-bts control interface is currently supporting the following operations:
h2. generic
h3. trx.0.thermal-attenuation
The idea of this paramter is to attenuate the system output power as part of
thermal management. In some cases the PA might be passing a critical level,
so an external control process can use this attribute to reduce the system
output power.
Please note that all values in the context of transmit power calculation
are integers in milli-dB (1/10000 bel), so the below example is setting
the attenuation at 3 dB:
<pre>
bsc_control.py -d localhost -p 4238 -s trx.0.thermal-attenuation 3000
Got message: SET_REPLY 1 trx.0.thermal-attenuation 3000
</pre>
<pre>
bsc_control.py -d localhost -p 4238 -g trx.0.thermal-attenuation
Got message: GET_REPLY 1 trx.0.thermal-attenuation 3000
</pre>
h2. sysmobts specific
h3. trx.0.clock-info
obtain information on the current clock status:
<pre>
bsc_control.py -d localhost -p 4238 -g trx.0.clock-info
Got message: GET_REPLY 1 trx.0.clock-info -100,ocxo,0,0,gps
</pre>
which is to be interpreted as:
* current clock correction value is -100 ppb
* current clock source is OCXO
* deviation between clock source and calibration source is 0 ppb
* resolution of clock error measurement is 0 ppt (0 means no result yet)
* current calibration source is GPS
When this attribute is set, any value passed on is discarded, but the clock
calibration process is re-started.
h3. trx.0.clock-correction
This attribute can get and set the current clock correction value:
<pre>
bsc_control.py -d localhost -p 4238 -g trx.0.clock-correction
Got message: GET_REPLY 1 trx.0.clock-correction -100
</pre>
<pre>
bsc_control.py -d localhost -p 4238 -s trx.0.clock-correction -- -99
Got message: SET_REPLY 1 trx.0.clock-correction success
</pre>

View File

@@ -3,7 +3,7 @@
!! !!
! !
log stderr log stderr
logging color 1 logging color 0
logging timestamp 0 logging timestamp 0
logging level all everything logging level all everything
logging level rsl info logging level rsl info
@@ -13,15 +13,9 @@ log stderr
logging level meas notice logging level meas notice
logging level pag info logging level pag info
logging level l1c info logging level l1c info
logging level l1p debug logging level l1p info
logging level dsp debug logging level dsp debug
logging level abis notice logging level abis notice
logging level lglobal notice
logging level llapdm notice
logging level linp notice
logging level lmux notice
logging level lmi notice
logging level lmib notice
! !
line vty line vty
no login no login
@@ -30,4 +24,3 @@ bts 0
band 1800 band 1800
ipa unit-id 1234 0 ipa unit-id 1234 0
oml remote-ip 192.168.100.11 oml remote-ip 192.168.100.11
rtp bind-ip 192.168.100.239

View File

@@ -0,0 +1,24 @@
!
! SysmoMgr (0.3.0.141-33e5) configuration saved from vty
!!
!
log stderr
logging filter all 1
logging color 1
logging timestamp 0
logging level all everything
logging level temp info
logging level fw info
logging level find info
logging level lglobal notice
logging level llapd notice
logging level linp notice
logging level lmux notice
logging level lmi notice
logging level lmib notice
logging level lsms notice
!
line vty
no login
!
sysmobts-mgr

View File

@@ -1 +1,3 @@
SUBDIRS = osmo-bts SUBDIRS = osmo-bts
noinst_HEADERS = openbsc/gsm_data.h

View File

@@ -1,2 +1,3 @@
noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \ 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 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

View File

@@ -3,25 +3,12 @@
#include <osmocom/core/select.h> #include <osmocom/core/select.h>
#include <osmocom/core/timer.h> #include <osmocom/core/timer.h>
#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmo-bts/gsm_data.h> #include <osmo-bts/gsm_data.h>
#define OML_RETRY_TIMER 5 #define OML_RETRY_TIMER 5
#define OML_PING_TIMER 20 #define OML_PING_TIMER 20
struct ipabis_link {
int state;
struct gsm_bts *bts; /* set, if OML link */
struct gsm_bts_trx *trx; /* set, if RSL link */
struct osmo_fd bfd;
struct osmo_timer_list timer;
struct msgb *rx_msg;
struct llist_head tx_queue;
int ping, pong, id_resp;
uint32_t ip;
};
enum { enum {
LINK_STATE_IDLE = 0, LINK_STATE_IDLE = 0,
LINK_STATE_RETRYING, LINK_STATE_RETRYING,
@@ -29,14 +16,14 @@ enum {
LINK_STATE_CONNECT, LINK_STATE_CONNECT,
}; };
int abis_tx(struct ipabis_link *link, struct msgb *msg); void abis_init(struct gsm_bts *bts);
struct msgb *abis_msgb_alloc(int headroom); struct e1inp_line *abis_open(struct gsm_bts *bts, char *dst_host,
void abis_push_ipa(struct msgb *msg, uint8_t proto); char *model_name);
int abis_open(struct ipabis_link *link, uint32_t ip);
void abis_close(struct ipabis_link *link);
int abis_oml_sendmsg(struct msgb *msg); int abis_oml_sendmsg(struct msgb *msg);
int abis_rsl_sendmsg(struct msgb *msg); int abis_bts_rsl_sendmsg(struct msgb *msg);
uint32_t get_signlink_remote_ip(struct e1inp_sign_link *link);
#endif /* _ABIS_H */ #endif /* _ABIS_H */

17
include/osmo-bts/amr.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef _OSMO_BTS_AMR_H
#define _OSMO_BTS_AMR_H
#include <osmo-bts/gsm_data.h>
#define AMR_TOC_QBIT 0x04
#define AMR_CMR_NONE 0xF
void amr_log_mr_conf(int ss, int logl, const char *pfx,
struct amr_multirate_conf *amr_mrc);
int amr_parse_mr_conf(struct amr_multirate_conf *amr_mrc,
const uint8_t *mr_conf, unsigned int len);
unsigned int amr_get_initial_mode(struct gsm_lchan *lchan);
#endif /* _OSMO_BTS_AMR_H */

View File

@@ -3,6 +3,12 @@
#include <osmo-bts/gsm_data.h> #include <osmo-bts/gsm_data.h>
enum bts_global_status {
BTS_STATUS_RF_ACTIVE,
BTS_STATUS_RF_MUTE,
BTS_STATUS_LAST,
};
extern void *tall_bts_ctx; extern void *tall_bts_ctx;
int bts_init(struct gsm_bts *bts); int bts_init(struct gsm_bts *bts);
@@ -18,13 +24,20 @@ int trx_link_estab(struct gsm_bts_trx *trx);
void bts_new_si(void *arg); void bts_new_si(void *arg);
void bts_setup_slot(struct gsm_bts_trx_ts *slot, uint8_t comb); void bts_setup_slot(struct gsm_bts_trx_ts *slot, uint8_t comb);
int lchan_init_lapdm(struct gsm_lchan *lchan);
int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg); int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg);
struct msgb *bts_agch_dequeue(struct gsm_bts *bts); struct msgb *bts_agch_dequeue(struct gsm_bts *bts);
void bts_update_agch_max_queue_length(struct gsm_bts *bts);
int bts_agch_max_queue_length(int T, int bcch_conf);
int bts_ccch_copy_msg(struct gsm_bts *bts, uint8_t *out_buf, struct gsm_time *gt,
int is_ag_res);
uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time); uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time);
uint8_t *lchan_sacch_get(struct gsm_lchan *lchan, struct gsm_time *g_time); uint8_t *lchan_sacch_get(struct gsm_lchan *lchan);
int lchan_init_lapdm(struct gsm_lchan *lchan);
void load_timer_start(struct gsm_bts *bts);
void bts_update_status(enum bts_global_status which, int on);
#endif /* _BTS_H */ #endif /* _BTS_H */

View File

@@ -19,7 +19,7 @@ int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
void *obj); void *obj);
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg, int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, void *obj); struct tlv_parsed *new_attr, int obj_kind, void *obj);
int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo, int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
void *obj); void *obj);
@@ -29,11 +29,23 @@ int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp); 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_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_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_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, uint8_t *rtp_pl, void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
unsigned int rtp_pl_len); 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);
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);
#endif #endif

16
include/osmo-bts/cbch.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/bts.h>
/* incoming SMS broadcast command from RSL */
int bts_process_smscb_cmd(struct gsm_bts *bts,
struct rsl_ie_cb_cmd_type cmd_type,
uint8_t msg_len, const uint8_t *msg);
/* call-back from bts model specific code when it wants to obtain a CBCH
* block for a given gsm_time. outbuf must have 23 bytes of space. */
int bts_cbch_get(struct gsm_bts *bts, uint8_t *outbuf, struct gsm_time *g_time);

View File

@@ -0,0 +1,4 @@
#pragma once
int bts_ctrl_cmds_install(struct gsm_bts *bts);
struct ctrl_handle *bts_controlif_setup(struct gsm_bts *bts);

View File

@@ -6,10 +6,21 @@
#include <osmocom/gsm/lapdm.h> #include <osmocom/gsm/lapdm.h>
#include <osmo-bts/paging.h> #include <osmo-bts/paging.h>
#include <osmo-bts/tx_power.h>
#define GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT 41
#define GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DISABLE 999999
#define GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT 41
#define GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT 91
struct pcu_sock_state;
struct smscb_msg;
struct gsm_network { struct gsm_network {
struct llist_head bts_list; struct llist_head bts_list;
unsigned int num_bts; unsigned int num_bts;
uint16_t mcc, mnc;
struct pcu_sock_state *pcu_state;
}; };
/* data structure for BTS related data specific to the BTS role */ /* data structure for BTS related data specific to the BTS role */
@@ -36,19 +47,63 @@ struct gsm_bts_role_bts {
/* Input parameters from OML */ /* Input parameters from OML */
int16_t busy_thresh; /* in dBm */ int16_t busy_thresh; /* in dBm */
uint16_t averaging_slots; uint16_t averaging_slots;
/* Internal data */
unsigned int total; /* total nr */
unsigned int busy; /* above busy_thresh */
unsigned int access; /* access bursts */
} rach; } rach;
} load; } load;
uint8_t ny1; uint8_t ny1;
uint8_t max_ta; uint8_t max_ta;
/* AGCH queuing */
struct llist_head agch_queue; struct llist_head agch_queue;
int agch_queue_length;
int agch_max_queue_length;
int agch_queue_thresh_level; /* Cleanup threshold in percent of max len */
int agch_queue_low_level; /* Low water mark in percent of max len */
int agch_queue_high_level; /* High water mark in percent of max len */
/* TODO: Use a rate counter group instead */
uint64_t agch_queue_dropped_msgs;
uint64_t agch_queue_merged_msgs;
uint64_t agch_queue_rejected_msgs;
uint64_t agch_queue_agch_msgs;
uint64_t agch_queue_pch_msgs;
struct paging_state *paging_state; struct paging_state *paging_state;
char *bsc_oml_host; char *bsc_oml_host;
char *rtp_bind_host; unsigned int rtp_jitter_buf_ms;
struct {
uint8_t ciphers; /* flags A5/1==0x1, A5/2==0x2, A5/3==0x4 */
} support;
struct {
uint8_t tc4_ctr;
} si;
uint8_t radio_link_timeout;
/* 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 */
struct smscb_msg *cur_msg; /* current SMS-CB */
} smscb_state;
};
enum lchan_ciph_state {
LCHAN_CIPH_NONE,
LCHAN_CIPH_RX_REQ,
LCHAN_CIPH_RX_CONF,
LCHAN_CIPH_TXRX_REQ,
LCHAN_CIPH_TXRX_CONF,
}; };
#define bts_role_bts(x) ((struct gsm_bts_role_bts *)(x)->role) #define bts_role_bts(x) ((struct gsm_bts_role_bts *)(x)->role)
#include "../../openbsc/openbsc/include/openbsc/gsm_data_shared.h" #include "openbsc/gsm_data_shared.h"
struct femtol1_hdl; struct femtol1_hdl;
@@ -57,4 +112,13 @@ static inline struct femtol1_hdl *trx_femtol1_hdl(struct gsm_bts_trx *trx)
return trx->role_bts.l1h; return trx->role_bts.l1h;
} }
void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state);
/* cipher code */
#define CIPHER_A5(x) (1 << (x-1))
int bts_supports_cipher(struct gsm_bts_role_bts *bts, int rsl_cipher);
#endif /* _GSM_DATA_H */ #endif /* _GSM_DATA_H */

View File

@@ -0,0 +1,12 @@
#pragma once
enum {
HANDOVER_NONE = 0,
HANDOVER_ENABLED,
HANDOVER_WAIT_FRAME,
};
void handover_rach(struct gsm_lchan *lchan, uint8_t ra, uint8_t acc_delay);
void handover_frame(struct gsm_lchan *lchan);
void handover_reset(struct gsm_lchan *lchan);

View File

@@ -14,6 +14,8 @@ enum {
DL1C, DL1C,
DL1P, DL1P,
DDSP, DDSP,
DPCU,
DHO,
DABIS, DABIS,
DRTP, DRTP,
DSUM, DSUM,

View File

@@ -3,8 +3,6 @@
int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm); int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm);
int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn);
int ts_meas_check_compute(struct gsm_bts_trx_ts *ts, uint32_t fn);
int trx_meas_check_compute(struct gsm_bts_trx *trx, uint32_t fn); int trx_meas_check_compute(struct gsm_bts_trx *trx, uint32_t fn);
/* build the 3 byte RSL uplinke measurement IE content */ /* build the 3 byte RSL uplinke measurement IE content */

View File

@@ -0,0 +1,20 @@
/*
* Routines to check the structurally integrity of messages
*/
#pragma once
struct msgb;
/**
* Classification of OML message. ETSI for plain GSM 12.21
* messages and IPA/Osmo for manufacturer messages.
*/
enum {
OML_MSG_TYPE_ETSI,
OML_MSG_TYPE_IPA,
OML_MSG_TYPE_OSMO,
};
int msg_verify_ipa_structure(struct msgb *msg);
int msg_verify_oml_structure(struct msgb *msg);

View File

@@ -1,6 +1,11 @@
#ifndef _OML_H #ifndef _OML_H
#define _OML_H #define _OML_H
struct gsm_bts;
struct gsm_abis_mo;
struct msgb;
int oml_init(void); int oml_init(void);
int down_oml(struct gsm_bts *bts, struct msgb *msg); int down_oml(struct gsm_bts *bts, struct msgb *msg);
@@ -9,10 +14,19 @@ int oml_send_msg(struct msgb *msg, int is_mauf);
int oml_mo_send_msg(struct gsm_abis_mo *mo, struct msgb *msg, uint8_t msg_type); int oml_mo_send_msg(struct gsm_abis_mo *mo, struct msgb *msg, uint8_t msg_type);
int oml_mo_opstart_ack(struct gsm_abis_mo *mo); int oml_mo_opstart_ack(struct gsm_abis_mo *mo);
int oml_mo_opstart_nack(struct gsm_abis_mo *mo, uint8_t nack_cause); int oml_mo_opstart_nack(struct gsm_abis_mo *mo, uint8_t nack_cause);
int oml_mo_statechg_ack(struct gsm_abis_mo *mo);
int oml_mo_statechg_nack(struct gsm_abis_mo *mo, uint8_t nack_cause);
/* Change the state and send STATE CHG REP */ /* Change the state and send STATE CHG REP */
int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state); int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state);
/* First initialization of MO, does _not_ generate state changes */
void oml_mo_state_init(struct gsm_abis_mo *mo, int op_state, int avail_state);
/* Update admin state and send ACK/NACK */
int oml_mo_rf_lock_chg(struct gsm_abis_mo *mo, uint8_t mute_state[8],
int success);
/* Transmit STATE CHG REP even if there was no state change */ /* Transmit STATE CHG REP even if there was no state change */
int oml_tx_state_changed(struct gsm_abis_mo *mo); int oml_tx_state_changed(struct gsm_abis_mo *mo);

View File

@@ -6,13 +6,28 @@
#include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/gsm/protocol/gsm_04_08.h>
struct paging_state; struct paging_state;
struct gsm_bts_role_bts;
/* initialize paging code */ /* initialize paging code */
struct paging_state *paging_init(void *ctx, unsigned int num_paging_max, struct paging_state *paging_init(struct gsm_bts_role_bts *btsb,
unsigned int num_paging_max,
unsigned int paging_lifetime); unsigned int paging_lifetime);
/* (re) configure paging code */
void paging_config(struct paging_state *ps,
unsigned int num_paging_max,
unsigned int paging_lifetime);
void paging_reset(struct paging_state *ps); void paging_reset(struct paging_state *ps);
/* The max number of paging entries */
unsigned int paging_get_queue_max(struct paging_state *ps);
void paging_set_queue_max(struct paging_state *ps, unsigned int queue_max);
/* The lifetime of a paging entry */
unsigned int paging_get_lifetime(struct paging_state *ps);
void paging_set_lifetime(struct paging_state *ps, unsigned int lifetime);
/* update with new SYSTEM INFORMATION parameters */ /* update with new SYSTEM INFORMATION parameters */
int paging_si_update(struct paging_state *ps, struct gsm48_control_channel_descr *chan_desc); int paging_si_update(struct paging_state *ps, struct gsm48_control_channel_descr *chan_desc);
@@ -20,7 +35,24 @@ int paging_si_update(struct paging_state *ps, struct gsm48_control_channel_descr
int paging_add_identity(struct paging_state *ps, uint8_t paging_group, int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
const uint8_t *identity_lv, uint8_t chan_needed); const uint8_t *identity_lv, uint8_t chan_needed);
/* Add an IMM.ASS message to the paging queue */
int paging_add_imm_ass(struct paging_state *ps, const uint8_t *data,
uint8_t len);
/* generate paging message for given gsm time */ /* generate paging message for given gsm time */
int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt); int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt,
int *is_empty);
/* inspection methods below */
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 #endif

18
include/osmo-bts/pcu_if.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef _PCU_IF_H
#define _PCU_IF_H
int pcu_tx_info_ind(void);
int pcu_tx_rts_req(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr);
int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len,
int8_t rssi);
int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint8_t ra, uint32_t fn);
int pcu_tx_time_ind(uint32_t fn);
int pcu_tx_pag_req(const uint8_t *identity_lv, uint8_t chan_needed);
int pcu_tx_pch_data_cnf(uint32_t fn, uint8_t *data, uint8_t len);
int pcu_sock_init(void);
void pcu_sock_exit(void);
#endif /* _PCU_IF_H */

View File

@@ -0,0 +1,153 @@
#ifndef _PCUIF_PROTO_H
#define _PCUIF_PROTO_H
#define PCU_IF_VERSION 0x05
/* msg_type */
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
#define PCU_IF_MSG_DATA_CNF 0x01 /* confirm (e.g. transmission on PCH) */
#define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */
#define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send request */
#define PCU_IF_MSG_RACH_IND 0x22 /* receive RACH */
#define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */
#define PCU_IF_MSG_ACT_REQ 0x40 /* activate/deactivate PDCH */
#define PCU_IF_MSG_TIME_IND 0x52 /* GSM time indication */
#define PCU_IF_MSG_PAG_REQ 0x60 /* paging request */
/* sapi */
#define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */
#define PCU_IF_SAPI_AGCH 0x02 /* assignment on AGCH */
#define PCU_IF_SAPI_PCH 0x03 /* paging/assignment on PCH */
#define PCU_IF_SAPI_BCCH 0x04 /* SI on BCCH */
#define PCU_IF_SAPI_PDTCH 0x05 /* packet data/control/ccch block */
#define PCU_IF_SAPI_PRACH 0x06 /* packet random access channel */
#define PCU_IF_SAPI_PTCCH 0x07 /* packet TA control channel */
/* flags */
#define PCU_IF_FLAG_ACTIVE (1 << 0)/* BTS is active */
#define PCU_IF_FLAG_SYSMO (1 << 1)/* access PDCH of sysmoBTS directly */
#define PCU_IF_FLAG_CS1 (1 << 16)
#define PCU_IF_FLAG_CS2 (1 << 17)
#define PCU_IF_FLAG_CS3 (1 << 18)
#define PCU_IF_FLAG_CS4 (1 << 19)
#define PCU_IF_FLAG_MCS1 (1 << 20)
#define PCU_IF_FLAG_MCS2 (1 << 21)
#define PCU_IF_FLAG_MCS3 (1 << 22)
#define PCU_IF_FLAG_MCS4 (1 << 23)
#define PCU_IF_FLAG_MCS5 (1 << 24)
#define PCU_IF_FLAG_MCS6 (1 << 25)
#define PCU_IF_FLAG_MCS7 (1 << 26)
#define PCU_IF_FLAG_MCS8 (1 << 27)
#define PCU_IF_FLAG_MCS9 (1 << 28)
struct gsm_pcu_if_data {
uint8_t sapi;
uint8_t len;
uint8_t data[162];
uint32_t fn;
uint16_t arfcn;
uint8_t trx_nr;
uint8_t ts_nr;
uint8_t block_nr;
int8_t rssi;
} __attribute__ ((packed));
struct gsm_pcu_if_rts_req {
uint8_t sapi;
uint8_t spare[3];
uint32_t fn;
uint16_t arfcn;
uint8_t trx_nr;
uint8_t ts_nr;
uint8_t block_nr;
} __attribute__ ((packed));
struct gsm_pcu_if_rach_ind {
uint8_t sapi;
uint8_t ra;
int16_t qta;
uint32_t fn;
uint16_t arfcn;
} __attribute__ ((packed));
struct gsm_pcu_if_info_trx {
uint16_t arfcn;
uint8_t pdch_mask; /* PDCH channels per TS */
uint8_t spare;
uint8_t tsc[8]; /* TSC per channel */
uint32_t hlayer1;
} __attribute__ ((packed));
struct gsm_pcu_if_info_ind {
uint32_t version;
uint32_t flags;
struct gsm_pcu_if_info_trx trx[8]; /* TRX infos per BTS */
uint8_t bsic;
/* RAI */
uint16_t mcc, mnc, lac, rac;
/* NSE */
uint16_t nsei;
uint8_t nse_timer[7];
uint8_t cell_timer[11];
/* cell */
uint16_t cell_id;
uint16_t repeat_time;
uint8_t repeat_count;
uint16_t bvci;
uint8_t t3142;
uint8_t t3169;
uint8_t t3191;
uint8_t t3193_10ms;
uint8_t t3195;
uint8_t n3101;
uint8_t n3103;
uint8_t n3105;
uint8_t cv_countdown;
uint16_t dl_tbf_ext;
uint16_t ul_tbf_ext;
uint8_t initial_cs;
uint8_t initial_mcs;
/* NSVC */
uint16_t nsvci[2];
uint16_t local_port[2];
uint16_t remote_port[2];
uint32_t remote_ip[2];
} __attribute__ ((packed));
struct gsm_pcu_if_act_req {
uint8_t activate;
uint8_t trx_nr;
uint8_t ts_nr;
uint8_t spare;
} __attribute__ ((packed));
struct gsm_pcu_if_time_ind {
uint32_t fn;
} __attribute__ ((packed));
struct gsm_pcu_if_pag_req {
uint8_t sapi;
uint8_t chan_needed;
uint8_t identity_lv[9];
} __attribute__ ((packed));
struct gsm_pcu_if {
/* context based information */
uint8_t msg_type; /* message type */
uint8_t bts_nr; /* bts number */
uint8_t spare[2];
union {
struct gsm_pcu_if_data data_req;
struct gsm_pcu_if_data data_cnf;
struct gsm_pcu_if_data data_ind;
struct gsm_pcu_if_rts_req rts_req;
struct gsm_pcu_if_rach_ind rach_ind;
struct gsm_pcu_if_info_ind info_ind;
struct gsm_pcu_if_act_req act_req;
struct gsm_pcu_if_time_ind time_ind;
struct gsm_pcu_if_pag_req pag_req;
} u;
} __attribute__ ((packed));
#endif /* _PCUIF_PROTO_H */

View File

@@ -1,6 +1,16 @@
#ifndef _RSL_H #ifndef _RSL_H
#define _RSL_H #define _RSL_H
/**
* What kind of release/activation is done? A silent one for
* the PDCH or one triggered through RSL?
*/
enum {
LCHAN_REL_ACT_RSL,
LCHAN_REL_ACT_PCU,
LCHAN_REL_ACT_OML,
};
int down_rsl(struct gsm_bts_trx *trx, struct msgb *msg); int down_rsl(struct gsm_bts_trx *trx, struct msgb *msg);
int rsl_tx_rf_res(struct gsm_bts_trx *trx); int rsl_tx_rf_res(struct gsm_bts_trx *trx);
int rsl_tx_chan_rqd(struct gsm_bts_trx *trx, struct gsm_time *gtime, int rsl_tx_chan_rqd(struct gsm_bts_trx *trx, struct gsm_time *gtime,
@@ -8,12 +18,18 @@ int rsl_tx_chan_rqd(struct gsm_bts_trx *trx, struct gsm_time *gtime,
int rsl_tx_est_ind(struct gsm_lchan *lchan, uint8_t link_id, uint8_t *data, int len); 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, struct gsm_time *gtime);
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); int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan);
int rsl_tx_hando_det(struct gsm_lchan *lchan, uint8_t *ho_delay);
/* call-back for LAPDm code, called when it wants to send msgs UP */ /* call-back for LAPDm code, called when it wants to send msgs UP */
int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx); int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx);
int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause); int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause);
int rsl_tx_ccch_load_ind_pch(struct gsm_bts *bts, uint16_t paging_avail);
int rsl_tx_ccch_load_ind_rach(struct gsm_bts *bts, uint16_t total,
uint16_t busy, uint16_t access);
struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr); struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr);

View File

@@ -9,6 +9,10 @@ enum sig_subsys {
enum signals_global { enum signals_global {
S_NEW_SYSINFO, S_NEW_SYSINFO,
S_NEW_OP_STATE,
S_NEW_NSE_ATTR,
S_NEW_CELL_ATTR,
S_NEW_NSVC_ATTR,
}; };
#endif #endif

View File

@@ -0,0 +1,76 @@
#pragma once
#include <stdint.h>
#include <osmocom/core/timer.h>
/* our unit is 'milli dB" or "milli dBm", i.e. 1/1000 of a dB(m) */
#define to_mdB(x) (x * 1000)
/* PA calibration table */
struct pa_calibration {
int gain_mdB[1024]; /* gain provided at given ARFCN */
/* FIXME: thermal calibration */
};
/* representation of a RF power amplifier */
struct power_amp {
/* nominal gain of the PA */
int nominal_gain_mdB;
/* table with calibrated actual gain for each ARFCN */
struct pa_calibration calib;
};
/* Transmit power related parameters of a transceiver */
struct trx_power_params {
/* specified maximum output of TRX at full power, has to be
* initialized by BTS model at startup*/
int trx_p_max_out_mdBm;
/* intended current total system output power */
int p_total_tgt_mdBm;
/* actual current total system output power, filled in by tx_power code */
int p_total_cur_mdBm;
/* current temporary attenuation due to thermal management,
* set by thermal management code via control interface */
int thermal_attenuation_mdB;
/* external gain (+) or attenuation (-) added by the user, configured
* by the user via VTY */
int user_gain_mdB;
/* calibration table of internal PA */
struct power_amp pa;
/* calibration table of user PA */
struct power_amp user_pa;
/* power ramping related data */
struct {
/* maximum initial Pout including all PAs */
int max_initial_pout_mdBm;
/* temporary attenuation due to power ramping */
int attenuation_mdB;
unsigned int step_size_mdB;
unsigned int step_interval_sec;
struct osmo_timer_list step_timer;
} ramp;
};
int get_p_max_out_mdBm(struct gsm_bts_trx *trx);
int get_p_nominal_mdBm(struct gsm_bts_trx *trx);
int get_p_target_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie);
int get_p_target_mdBm_lchan(struct gsm_lchan *lchan);
int get_p_trxout_target_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie);
int get_p_trxout_target_mdBm_lchan(struct gsm_lchan *lchan);
int get_p_trxout_actual_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie);
int get_p_trxout_actual_mdBm_lchan(struct gsm_lchan *lchan);
int power_ramp_start(struct gsm_bts_trx *trx, int p_total_tgt_mdBm, int bypass);
void power_trx_change_compl(struct gsm_bts_trx *trx, int p_trxout_cur_mdBm);

View File

@@ -7,8 +7,6 @@
enum bts_vty_node { enum bts_vty_node {
BTS_NODE = _LAST_OSMOVTY_NODE + 1, BTS_NODE = _LAST_OSMOVTY_NODE + 1,
TRX_NODE, TRX_NODE,
TS_NODE,
LCHAN_NODE,
}; };
extern struct cmd_element ournode_exit_cmd; extern struct cmd_element ournode_exit_cmd;

View File

@@ -1,4 +1,4 @@
SUBDIRS = common #osmo-bts-bb SUBDIRS = common
if ENABLE_SYSMOBTS if ENABLE_SYSMOBTS
SUBDIRS += osmo-bts-sysmo SUBDIRS += osmo-bts-sysmo

View File

@@ -1,7 +1,11 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOTRAU_CFLAGS) AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOTRAU_LIBS) LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOTRAU_LIBS)
noinst_LIBRARIES = libbts.a noinst_LIBRARIES = libbts.a
libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \ libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \
rsl.c vty.c paging.c measurement.c rsl.c vty.c paging.c measurement.c amr.c lchan.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

View File

@@ -1,8 +1,7 @@
/* Minimalistic Abis/IP interface routines, soon to be replaced by /* Abis/IP interface routines utilizing libosmo-abis (Pablo) */
* libosmo-abis (Pablo) */
/* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu> /* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2011 by Harald Welte <laforge@gnumonks.org> * (C) 2011-2013 by Harald Welte <laforge@gnumonks.org>
* *
* All Rights Reserved * All Rights Reserved
* *
@@ -21,6 +20,8 @@
* *
*/ */
#include "btsconfig.h"
#include <stdio.h> #include <stdio.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
@@ -34,496 +35,205 @@
#include <osmocom/core/select.h> #include <osmocom/core/select.h>
#include <osmocom/core/timer.h> #include <osmocom/core/timer.h>
#include <osmocom/core/msgb.h> #include <osmocom/core/msgb.h>
#include <osmocom/gsm/protocol/ipaccess.h> #include <osmocom/core/signal.h>
#include <osmocom/core/macaddr.h>
#include <osmocom/abis/abis.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/abis/ipaccess.h>
#include <osmocom/gsm/ipa.h>
#include <osmo-bts/logging.h> #include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h> #include <osmo-bts/gsm_data.h>
#include <osmo-bts/abis.h>
#include <osmo-bts/bts.h> #include <osmo-bts/bts.h>
#include <osmo-bts/rsl.h> #include <osmo-bts/rsl.h>
#include <osmo-bts/oml.h> #include <osmo-bts/oml.h>
extern char *software_version; static struct gsm_bts *g_bts;
extern uint8_t abis_mac[6];
/*
* support
*/
#define ABIS_ALLOC_SIZE 900
/* send message to BSC */
int abis_tx(struct ipabis_link *link, struct msgb *msg)
{
if (link->state != LINK_STATE_CONNECT) {
LOGP(DABIS, LOGL_NOTICE, "Link down, dropping message.\n");
msgb_free(msg);
return -EIO;
}
msgb_enqueue(&link->tx_queue, msg);
link->bfd.when |= BSC_FD_WRITE;
return 0;
}
int abis_oml_sendmsg(struct msgb *msg) int abis_oml_sendmsg(struct msgb *msg)
{ {
struct gsm_bts *bts = msg->trx->bts; struct gsm_bts *bts = msg->trx->bts;
abis_push_ipa(msg, 0xff); /* osmo-bts uses msg->trx internally, but libosmo-abis uses
* the signalling link at msg->dst */
if (!bts->oml_link) { msg->dst = bts->oml_link;
msgb_free(msg); return abis_sendmsg(msg);
return 0;
}
return abis_tx((struct ipabis_link *) bts->oml_link, msg);
} }
int abis_rsl_sendmsg(struct msgb *msg) int abis_bts_rsl_sendmsg(struct msgb *msg)
{ {
struct gsm_bts_trx *trx = msg->trx; /* osmo-bts uses msg->trx internally, but libosmo-abis uses
* the signalling link at msg->dst */
abis_push_ipa(msg, 0); msg->dst = msg->trx->rsl_link;
return abis_sendmsg(msg);
return abis_tx((struct ipabis_link *) trx->rsl_link, msg);
} }
struct msgb *abis_msgb_alloc(int headroom) static struct e1inp_sign_link *sign_link_up(void *unit, struct e1inp_line *line,
enum e1inp_sign_type type)
{ {
struct msgb *nmsg; struct e1inp_sign_link *sign_link = NULL;
headroom += sizeof(struct ipaccess_head); switch (type) {
case E1INP_SIGN_OML:
nmsg = msgb_alloc_headroom(ABIS_ALLOC_SIZE + headroom, LOGP(DABIS, LOGL_INFO, "OML Signalling link up\n");
headroom, "Abis/IP"); e1inp_ts_config_sign(&line->ts[E1INP_SIGN_OML-1], line);
if (!nmsg) sign_link = g_bts->oml_link =
return NULL; e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML-1],
return nmsg; E1INP_SIGN_OML, NULL, 255, 0);
} sign_link->trx = g_bts->c0;
bts_link_estab(g_bts);
void abis_push_ipa(struct msgb *msg, uint8_t proto)
{
struct ipaccess_head *nhh;
msg->l2h = msg->data;
nhh = (struct ipaccess_head *) msgb_push(msg, sizeof(*nhh));
nhh->proto = proto;
nhh->len = htons(msgb_l2len(msg));
}
/*
* IPA related messages
*/
/* send ping/pong */
static int abis_tx_ipa_pingpong(struct ipabis_link *link, uint8_t pingpong)
{
struct msgb *nmsg;
nmsg = abis_msgb_alloc(0);
if (!nmsg)
return -ENOMEM;
*msgb_put(nmsg, 1) = pingpong;
abis_push_ipa(nmsg, IPAC_PROTO_IPACCESS);
return abis_tx(link, nmsg);
}
/* send ACK and ID RESP */
static int abis_rx_ipa_id_get(struct ipabis_link *link, uint8_t *data, int len)
{
struct gsm_bts *bts = link->bts;
struct msgb *nmsg, *nmsg2;
char str[64];
uint8_t *tag;
if (!link->bts)
bts = link->trx->bts;
LOGP(DABIS, LOGL_INFO, "Reply to ID_GET\n");
nmsg = abis_msgb_alloc(0);
if (!nmsg)
return -ENOMEM;
*msgb_put(nmsg, 1) = IPAC_MSGT_ID_RESP;
while (len) {
if (len < 2) {
LOGP(DABIS, LOGL_NOTICE,
"Short read of ipaccess tag\n");
msgb_free(nmsg);
return -EIO;
}
switch (data[1]) {
case IPAC_IDTAG_UNIT:
sprintf(str, "%u/%u/%u",
bts->ip_access.site_id,
bts->ip_access.bts_id,
(link->trx) ? link->trx->nr : 0);
break;
case IPAC_IDTAG_MACADDR:
sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
abis_mac[0], abis_mac[1], abis_mac[2],
abis_mac[3], abis_mac[4], abis_mac[5]);
break;
case IPAC_IDTAG_LOCATION1:
strcpy(str, "osmoBTS");
break;
case IPAC_IDTAG_LOCATION2:
strcpy(str, "osmoBTS");
break;
case IPAC_IDTAG_EQUIPVERS:
case IPAC_IDTAG_SWVERSION:
strcpy(str, software_version);
break;
case IPAC_IDTAG_UNITNAME:
sprintf(str, "osmoBTS-%02x-%02x-%02x-%02x-%02x-%02x",
abis_mac[0], abis_mac[1], abis_mac[2],
abis_mac[3], abis_mac[4], abis_mac[5]);
break;
case IPAC_IDTAG_SERNR:
strcpy(str, "");
break;
default:
LOGP(DABIS, LOGL_NOTICE,
"Unknown ipaccess tag 0x%02x\n", *data);
msgb_free(nmsg);
return -EINVAL;
}
LOGP(DABIS, LOGL_INFO, " tag %d: %s\n", data[1], str);
tag = msgb_put(nmsg, 3 + strlen(str) + 1);
tag[0] = 0x00;
tag[1] = 1 + strlen(str) + 1;
tag[2] = data[1];
memcpy(tag + 3, str, strlen(str) + 1);
data += 2;
len -= 2;
}
abis_push_ipa(nmsg, IPAC_PROTO_IPACCESS);
nmsg2 = abis_msgb_alloc(0);
if (!nmsg2) {
msgb_free(nmsg);
return -ENOMEM;
}
*msgb_put(nmsg2, 1) = IPAC_MSGT_ID_ACK;
abis_push_ipa(nmsg2, IPAC_PROTO_IPACCESS);
link->id_resp = 1;
abis_tx(link, nmsg2);
return abis_tx(link, nmsg);
}
static int abis_rx_ipaccess(struct ipabis_link *link, struct msgb *msg)
{
uint8_t *data = msgb_l2(msg);
int len = msgb_l2len(msg);
int ret = 0;
if (len < 1) {
LOGP(DABIS, LOGL_NOTICE, "Short read of ipaccess message\n");
msgb_free(msg);
return EIO;
}
switch (*data) {
case IPAC_MSGT_PONG:
#if 0
#warning HACK
rsl_tx_chan_rqd(link->bts->trx[0]);
#endif
LOGP(DABIS, LOGL_DEBUG, "PONG\n");
link->pong = 1;
break; break;
case IPAC_MSGT_PING: case E1INP_SIGN_RSL:
LOGP(DABIS, LOGL_DEBUG, "reply to ping request\n"); LOGP(DABIS, LOGL_INFO, "RSL Signalling link up\n");
ret = abis_tx_ipa_pingpong(link, IPAC_MSGT_PONG); e1inp_ts_config_sign(&line->ts[E1INP_SIGN_RSL-1], line);
break; sign_link = g_bts->c0->rsl_link =
case IPAC_MSGT_ID_GET: e1inp_sign_link_create(&line->ts[E1INP_SIGN_RSL-1],
ret = abis_rx_ipa_id_get(link, data + 1, len - 1); E1INP_SIGN_RSL, NULL, 0, 0);
break; /* FIXME: This assumes there is only one TRX! */
case IPAC_MSGT_ID_ACK: sign_link->trx = g_bts->c0;
LOGP(DABIS, LOGL_DEBUG, "ID ACK\n"); trx_link_estab(sign_link->trx);
if (link->id_resp && link->bts)
ret = bts_link_estab(link->bts);
if (link->id_resp && link->trx)
ret = trx_link_estab(link->trx);
link->id_resp = 0;
break; break;
default: default:
LOGP(DABIS, LOGL_NOTICE, break;
"Unknown ipaccess message type 0x%02x\n", *data);
ret = -EINVAL;
} }
msgb_free(msg); return sign_link;
return ret;
} }
/* static void sign_link_down(struct e1inp_line *line)
* A-Bis over IP implementation
*/
/* receive message from BSC */
static int abis_rx(struct ipabis_link *link, struct msgb *msg)
{ {
struct ipaccess_head *hh = (struct ipaccess_head *) msg->data; LOGP(DABIS, LOGL_ERROR, "Signalling link down\n");
int ret = 0;
switch (hh->proto) { if (g_bts->c0->rsl_link) {
case IPAC_PROTO_RSL: e1inp_sign_link_destroy(g_bts->c0->rsl_link);
if (!link->trx) { g_bts->c0->rsl_link = NULL;
LOGP(DABIS, LOGL_NOTICE, trx_link_estab(g_bts->c0);
"Received RSL message not on RSL link\n"); }
msgb_free(msg);
ret = EIO; if (g_bts->oml_link)
break; e1inp_sign_link_destroy(g_bts->oml_link);
} g_bts->oml_link = NULL;
msg->trx = link->trx;
ret = down_rsl(link->trx, msg); bts_shutdown(g_bts, "Abis close");
}
/* callback for incoming mesages from A-bis/IP */
static int sign_link_cb(struct msgb *msg)
{
struct e1inp_sign_link *link = msg->dst;
/* osmo-bts code assumes msg->trx is set, but libosmo-abis works
* with the sign_link stored in msg->dst, so we have to convert
* here */
msg->trx = link->trx;
switch (link->type) {
case E1INP_SIGN_OML:
down_oml(link->trx->bts, msg);
break; break;
case IPAC_PROTO_IPACCESS: case E1INP_SIGN_RSL:
ret = abis_rx_ipaccess(link, msg); down_rsl(link->trx, msg);
break;
case IPAC_PROTO_SCCP:
LOGP(DABIS, LOGL_INFO, "Received SCCP message\n");
msgb_free(msg);
break;
case IPAC_PROTO_OML:
if (!link->bts) {
LOGP(DABIS, LOGL_NOTICE,
"Received OML message not on OML link\n");
msgb_free(msg);
ret = EIO;
break;
}
msg->trx = link->bts->c0;
ret = down_oml(link->bts, msg);
break; break;
default: default:
LOGP(DABIS, LOGL_NOTICE, "Unknown Protocol %d\n", hh->proto);
msgb_free(msg); msgb_free(msg);
ret = EINVAL;
}
return ret;
}
static void abis_timeout(void *arg)
{
struct ipabis_link *link = arg;
int ret;
switch (link->state) {
case LINK_STATE_RETRYING:
ret = abis_open(link, link->ip);
if (ret <= 0)
osmo_timer_schedule(&link->timer, OML_RETRY_TIMER, 0);
break;
case LINK_STATE_CONNECT:
if (link->ping && !link->pong) {
LOGP(DABIS, LOGL_NOTICE,
"No reply to PING. Link is lost\n");
abis_close(link);
ret = abis_open(link, link->ip);
if (ret <= 0) {
osmo_timer_schedule(&link->timer,
OML_RETRY_TIMER, 0);
link->state = LINK_STATE_RETRYING;
}
break;
}
link->ping = 1;
link->pong = 0;
LOGP(DABIS, LOGL_DEBUG, "PING\n");
abis_tx_ipa_pingpong(link, IPAC_MSGT_PING);
osmo_timer_schedule(&link->timer, OML_PING_TIMER, 0);
break; break;
} }
}
static int abis_sock_cb(struct osmo_fd *bfd, unsigned int what)
{
struct ipabis_link *link = bfd->data;
struct ipaccess_head *hh;
struct msgb *msg;
int ret = 0;
if ((what & BSC_FD_WRITE) && link->state == LINK_STATE_CONNECTING) {
if (link->bts) {
if (osmo_timer_pending(&link->timer))
osmo_timer_del(&link->timer);
// osmo_timer_schedule(&link->timer, OML_PING_TIMER, 0);
#warning HACK
osmo_timer_schedule(&link->timer, 3, 0);
link->ping = link->pong = 0;
}
LOGP(DABIS, LOGL_INFO, "Abis socket now connected.\n");
link->state = LINK_STATE_CONNECT;
}
//printf("what %d\n", what);
if ((what & BSC_FD_READ)) {
if (!link->rx_msg) {
link->rx_msg = msgb_alloc(ABIS_ALLOC_SIZE, "Abis/IP");
if (!link->rx_msg)
return -ENOMEM;
}
msg = link->rx_msg;
hh = (struct ipaccess_head *) msg->data;
if (msg->len < sizeof(*hh)) {
ret = recv(link->bfd.fd, msg->data, sizeof(*hh), 0);
if (ret <= 0) {
goto close;
}
msgb_put(msg, ret);
if (msg->len < sizeof(*hh))
return 0;
msg->l2h = msg->data + sizeof(*hh);
if (ntohs(hh->len) > msgb_tailroom(msg)) {
LOGP(DABIS, LOGL_DEBUG, "Received packet from "
"Abis socket too large.\n");
goto close;
}
}
ret = recv(link->bfd.fd, msg->tail,
ntohs(hh->len) + sizeof(*hh) - msg->len, 0);
if (ret == 0)
goto close;
if (ret < 0 && errno != EAGAIN)
goto close;
msgb_put(msg, ret);
if (ntohs(hh->len) + sizeof(*hh) > msg->len)
return 0;
link->rx_msg = NULL;
LOGP(DABIS, LOGL_DEBUG, "Received messages from Abis socket.\n");
ret = abis_rx(link, msg);
}
if ((what & BSC_FD_WRITE)) {
msg = msgb_dequeue(&link->tx_queue);
if (msg) {
LOGP(DABIS, LOGL_DEBUG, "Sending messages to Abis socket.\n");
ret = send(link->bfd.fd, msg->data, msg->len, 0);
msgb_free(msg);
if (ret < 0)
goto close;
} else
link->bfd.when &= ~BSC_FD_WRITE;
}
if ((what & BSC_FD_EXCEPT)) {
LOGP(DABIS, LOGL_NOTICE, "Abis socket received exception\n");
goto close;
}
return ret;
close:
abis_close(link);
/* RSL link will just close and BSC is notified */
if (link->trx) {
LOGP(DABIS, LOGL_NOTICE, "Connection to BSC failed\n");
return trx_link_estab(link->trx);
}
LOGP(DABIS, LOGL_NOTICE, "Connection to BSC failed, retrying in %d "
"seconds.\n", OML_RETRY_TIMER);
osmo_timer_schedule(&link->timer, OML_RETRY_TIMER, 0);
link->state = LINK_STATE_RETRYING;
return 0; return 0;
} }
int abis_open(struct ipabis_link *link, uint32_t ip) uint32_t get_signlink_remote_ip(struct e1inp_sign_link *link)
{ {
unsigned int on = 1; int fd = link->ts->driver.ipaccess.fd.fd;
struct sockaddr_in addr; struct sockaddr_in sin;
int sock; socklen_t slen = sizeof(sin);
int ret; int rc;
rc = getpeername(fd, (struct sockaddr *)&sin, &slen);
if (rc < 0) {
LOGP(DOML, LOGL_ERROR, "Cannot determine remote IP Addr: %s\n",
strerror(errno));
return 0;
}
/* we assume that the soket is AF_INET. As Abis/IP contains
* lots of hard-coded IPv4 addresses, this safe */
OSMO_ASSERT(sin.sin_family == AF_INET);
return ntohl(sin.sin_addr.s_addr);
}
static int inp_s_cbfn(unsigned int subsys, unsigned int signal,
void *hdlr_data, void *signal_data)
{
if (subsys != SS_L_INPUT)
return 0;
DEBUGP(DABIS, "Input Signal %u received\n", signal);
return 0;
}
static struct ipaccess_unit bts_dev_info = {
.unit_name = "sysmoBTS",
.equipvers = "", /* FIXME: read this from hw */
.swversion = PACKAGE_VERSION,
.location1 = "",
.location2 = "",
.serno = "",
};
static struct e1inp_line_ops line_ops = {
.cfg = {
.ipa = {
.role = E1INP_LINE_R_BTS,
.dev = &bts_dev_info,
},
},
.sign_link_up = sign_link_up,
.sign_link_down = sign_link_down,
.sign_link = sign_link_cb,
};
void abis_init(struct gsm_bts *bts)
{
g_bts = bts;
oml_init(); oml_init();
e1inp_vty_init();
libosmo_abis_init(NULL);
if (link->bfd.fd > 0) osmo_signal_register_handler(SS_L_INPUT, &inp_s_cbfn, bts);
return -EBUSY;
INIT_LLIST_HEAD(&link->tx_queue);
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
return sock;
ret = ioctl(sock, FIONBIO, (unsigned char *)&on);
if (ret < 0) {
close(sock);
return ret;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
if (link->bts)
addr.sin_port = htons(IPA_TCP_PORT_OML);
else
addr.sin_port = htons(IPA_TCP_PORT_RSL);
addr.sin_addr.s_addr = htonl(ip);
ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0 && errno != EINPROGRESS) {
close(sock);
return ret;
}
link->bfd.data = link;
link->bfd.when = BSC_FD_READ | BSC_FD_WRITE | BSC_FD_EXCEPT;
link->bfd.cb = abis_sock_cb;
link->bfd.fd = sock;
link->state = LINK_STATE_CONNECTING;
link->ip = ip;
link->timer.cb = abis_timeout;
link->timer.data = link;
osmo_fd_register(&link->bfd);
LOGP(DABIS, LOGL_INFO, "Abis socket trying to reach BSC.\n");
return sock;
} }
void abis_close(struct ipabis_link *link) struct e1inp_line *abis_open(struct gsm_bts *bts, char *dst_host,
char *model_name)
{ {
struct msgb *msg; struct e1inp_line *line;
if (link->bfd.fd <= 0) /* patch in various data from VTY and othe sources */
return; line_ops.cfg.ipa.addr = dst_host;
osmo_get_macaddr(bts_dev_info.mac_addr, "eth0");
bts_dev_info.site_id = bts->ip_access.site_id;
bts_dev_info.bts_id = bts->ip_access.bts_id;
bts_dev_info.unit_name = model_name;
if (bts->description)
bts_dev_info.unit_name = bts->description;
bts_dev_info.location2 = model_name;
LOGP(DABIS, LOGL_NOTICE, "Abis socket closed.\n"); line = e1inp_line_find(0);
if (!line)
line = e1inp_line_create(0, "ipa");
if (!line)
return NULL;
e1inp_line_bind_ops(line, &line_ops);
if (link->rx_msg) { /* This will open the OML connection now */
msgb_free(link->rx_msg); if (e1inp_line_update(line) < 0)
link->rx_msg = NULL; return NULL;
}
while ((msg = msgb_dequeue(&link->tx_queue))) return line;
msgb_free(msg);
osmo_fd_unregister(&link->bfd);
close(link->bfd.fd);
link->bfd.fd = -1; /* -1 or 0 indicates: 'close' */
link->state = LINK_STATE_IDLE;
if (osmo_timer_pending(&link->timer))
osmo_timer_del(&link->timer);
/* for now, we simply terminate the program and re-spawn */
if (link->bts)
bts_shutdown(link->bts, "Abis close / OML");
else if (link->trx)
bts_shutdown(link->trx->bts, "Abis close / RSL");
else {
LOGP(DABIS, LOGL_FATAL, "Unable to connect to BSC\n");
exit(43);
}
} }

117
src/common/amr.c Normal file
View File

@@ -0,0 +1,117 @@
#include <stdint.h>
#include <errno.h>
#include <osmocom/core/logging.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/amr.h>
void amr_log_mr_conf(int ss, int logl, const char *pfx,
struct amr_multirate_conf *amr_mrc)
{
int i;
LOGP(ss, logl, "%s AMR MR Conf: num_modes=%u",
pfx, amr_mrc->num_modes);
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);
LOGPC(ss, logl, "\n");
}
/* parse a GSM 04.08 MultiRate Config IE (10.5.2.21aa) in a more
* comfortable internal data structure */
int amr_parse_mr_conf(struct amr_multirate_conf *amr_mrc,
const uint8_t *mr_conf, unsigned int len)
{
uint8_t mr_version = mr_conf[0] >> 5;
uint8_t num_codecs = 0;
int i, j = 0;
if (mr_version != 1) {
LOGP(DRSL, LOGL_ERROR, "AMR Multirate Version %u unknonw\n",
mr_version);
goto ret_einval;
}
/* check number of active codecs */
for (i = 0; i < 8; i++) {
if (mr_conf[1] & (1 << i))
num_codecs++;
}
/* check for minimum length */
if (num_codecs == 0 ||
(num_codecs == 1 && len < 2) ||
(num_codecs == 2 && len < 4) ||
(num_codecs == 3 && len < 5) ||
(num_codecs == 4 && len < 6) ||
(num_codecs > 4)) {
LOGP(DRSL, LOGL_ERROR, "AMR Multirate with %u modes len=%u "
"not possible\n", num_codecs, len);
goto ret_einval;
}
/* copy the first two octets of the IE */
amr_mrc->gsm48_ie[0] = mr_conf[0];
amr_mrc->gsm48_ie[1] = mr_conf[1];
amr_mrc->num_modes = num_codecs;
for (i = 0; i < 8; i++) {
if (mr_conf[1] & (1 << i)) {
amr_mrc->mode[j++].mode = i;
}
}
if (num_codecs >= 2) {
amr_mrc->mode[0].threshold = mr_conf[1] & 0x3F;
amr_mrc->mode[0].hysteresis = mr_conf[2] >> 4;
}
if (num_codecs >= 3) {
amr_mrc->mode[1].threshold =
((mr_conf[2] & 0xF) << 2) | (mr_conf[3] >> 6);
amr_mrc->mode[1].hysteresis = (mr_conf[3] >> 2) & 0x7;
}
if (num_codecs >= 4) {
amr_mrc->mode[3].threshold =
((mr_conf[3] & 0x3) << 4) | (mr_conf[4] >> 4);
amr_mrc->mode[3].hysteresis = mr_conf[4] & 0xF;
}
return num_codecs;
ret_einval:
return -EINVAL;
}
/*! \brief determine AMR initial codec mode for given logical channel
* \returns integer between 0..3 for AMR codce mode 1..4 */
unsigned int amr_get_initial_mode(struct gsm_lchan *lchan)
{
struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr;
if (lchan->mr_conf.icmi) {
/* initial mode given, coding in TS 05.09 3.4.1 */
return lchan->mr_conf.smod;
} else {
/* implicit rule according to TS 05.09 Chapter 3.4.3 */
switch (amr_mrc->num_modes) {
case 2:
case 3:
/* return the most robust */
return 0;
case 4:
/* return the second-most robust */
return 1;
case 1:
default:
/* return the only mode we have */
return 0;
}
}
}

View File

@@ -42,6 +42,7 @@
#include <osmo-bts/bts_model.h> #include <osmo-bts/bts_model.h>
#include <osmo-bts/rsl.h> #include <osmo-bts/rsl.h>
#include <osmo-bts/oml.h> #include <osmo-bts/oml.h>
#include <osmo-bts/signal.h>
struct gsm_network bts_gsmnet = { struct gsm_network bts_gsmnet = {
@@ -51,27 +52,80 @@ struct gsm_network bts_gsmnet = {
void *tall_bts_ctx; void *tall_bts_ctx;
/* Table 3.1 TS 04.08: Values of parameter S */
static const uint8_t tx_integer[] = {
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 25, 32, 50,
};
static const uint8_t s_values[][2] = {
{ 55, 41 }, { 76, 52 }, { 109, 58 }, { 163, 86 }, { 217, 115 },
};
static int bts_signal_cbfn(unsigned int subsys, unsigned int signal,
void *hdlr_data, void *signal_data)
{
if (subsys == SS_GLOBAL && signal == S_NEW_SYSINFO) {
struct gsm_bts *bts = signal_data;
bts_update_agch_max_queue_length(bts);
}
return 0;
}
int bts_init(struct gsm_bts *bts) int bts_init(struct gsm_bts *bts)
{ {
struct gsm_bts_role_bts *btsb; struct gsm_bts_role_bts *btsb;
struct gsm_bts_trx *trx; struct gsm_bts_trx *trx;
int rc; int rc;
static int initialized = 0;
/* add to list of BTSs */
llist_add_tail(&bts->list, &bts_gsmnet.bts_list);
bts->band = GSM_BAND_1800; bts->band = GSM_BAND_1800;
bts->role = btsb = talloc_zero(bts, struct gsm_bts_role_bts); bts->role = btsb = talloc_zero(bts, struct gsm_bts_role_bts);
INIT_LLIST_HEAD(&btsb->agch_queue); INIT_LLIST_HEAD(&btsb->agch_queue);
btsb->agch_queue_length = 0;
/* FIXME: make those parameters configurable */ /* enable management with default levels,
* raise threshold to GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DISABLE to
* disable this feature.
*/
btsb->agch_queue_low_level = GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT;
btsb->agch_queue_high_level = GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT;
btsb->agch_queue_thresh_level = GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT;
/* configurable via VTY */
btsb->paging_state = paging_init(btsb, 200, 0); btsb->paging_state = paging_init(btsb, 200, 0);
/* configurable via OML */
btsb->load.ccch.load_ind_period = 112;
load_timer_start(bts);
btsb->rtp_jitter_buf_ms = 100;
btsb->max_ta = 63;
btsb->ny1 = 4;
btsb->t3105_ms = 300;
/* default RADIO_LINK_TIMEOUT */
btsb->radio_link_timeout = 32;
/* Start with the site manager */
oml_mo_state_init(&bts->site_mgr.mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
/* set BTS to dependency */ /* set BTS to dependency */
oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_DEPENDENCY); oml_mo_state_init(&bts->mo, -1, NM_AVSTATE_DEPENDENCY);
oml_mo_state_init(&bts->gprs.nse.mo, -1, NM_AVSTATE_DEPENDENCY);
oml_mo_state_init(&bts->gprs.cell.mo, -1, NM_AVSTATE_DEPENDENCY);
oml_mo_state_init(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_DEPENDENCY);
oml_mo_state_init(&bts->gprs.nsvc[1].mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
/* initialize bts data structure */ /* initialize bts data structure */
llist_for_each_entry(trx, &bts->trx_list, list) { llist_for_each_entry(trx, &bts->trx_list, list) {
struct trx_power_params *tpp = &trx->power_params;
int i; int i;
for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
struct gsm_bts_trx_ts *ts = &trx->ts[i]; struct gsm_bts_trx_ts *ts = &trx->ts[i];
int k; int k;
@@ -81,18 +135,29 @@ int bts_init(struct gsm_bts *bts)
INIT_LLIST_HEAD(&lchan->dl_tch_queue); INIT_LLIST_HEAD(&lchan->dl_tch_queue);
} }
} }
/* Default values for the power adjustments */
tpp->ramp.max_initial_pout_mdBm = to_mdB(23);
tpp->ramp.step_size_mdB = to_mdB(2);
tpp->ramp.step_interval_sec = 1;
} }
osmo_rtp_init(tall_bts_ctx); osmo_rtp_init(tall_bts_ctx);
rc = bts_model_init(bts); rc = bts_model_init(bts);
if (rc < 0) if (rc < 0) {
llist_del(&bts->list);
return rc; return rc;
}
/* add to list of BTSs */
llist_add_tail(&bts->list, &bts_gsmnet.bts_list);
bts_gsmnet.num_bts++; bts_gsmnet.num_bts++;
if (!initialized) {
osmo_signal_register_handler(SS_GLOBAL, bts_signal_cbfn, NULL);
initialized = 1;
}
INIT_LLIST_HEAD(&btsb->smscb_state.queue);
return rc; return rc;
} }
@@ -110,238 +175,25 @@ void bts_shutdown(struct gsm_bts *bts, const char *reason)
{ {
struct gsm_bts_trx *trx; struct gsm_bts_trx *trx;
if (osmo_timer_pending(&shutdown_timer)) {
LOGP(DOML, LOGL_NOTICE,
"BTS is already being shutdown.\n");
return;
}
LOGP(DOML, LOGL_NOTICE, "Shutting down BTS %u, Reason %s\n", LOGP(DOML, LOGL_NOTICE, "Shutting down BTS %u, Reason %s\n",
bts->nr, reason); bts->nr, reason);
llist_for_each_entry(trx, &bts->trx_list, list) llist_for_each_entry(trx, &bts->trx_list, list) {
bts_model_trx_deact_rf(trx); bts_model_trx_deact_rf(trx);
bts_model_trx_close(trx);
}
/* shedule a timer to make sure select loop logic can run again /* shedule a timer to make sure select loop logic can run again
* to dispatch any pending primitives */ * to dispatch any pending primitives */
osmo_timer_schedule(&shutdown_timer, 3, 0); osmo_timer_schedule(&shutdown_timer, 3, 0);
} }
#if 0
struct osmobts_lchan *lchan_by_channelnr(struct osmobts_trx *trx,
uint8_t channelnr)
{
uint8_t ts = channelnr & ~RSL_CHAN_NR_MASK;
uint8_t type = channelnr & RSL_CHAN_NR_MASK;
uint8_t sub = 0;
struct osmobts_slot *slot = trx->slot[ts];
if ((type & 0xf0) == RSL_CHAN_Lm_ACCHs) {
sub = (channelnr >> 3) & 1;
type = RSL_CHAN_Lm_ACCHs;
} else
if ((type & 0xe0) == RSL_CHAN_SDCCH4_ACCH) {
sub = (channelnr >> 3) & 3;
type = RSL_CHAN_SDCCH4_ACCH;
} else
if ((type & 0xc0) == RSL_CHAN_SDCCH8_ACCH) {
sub = (channelnr >> 3) & 7;
type = RSL_CHAN_SDCCH8_ACCH;
} else
if (type == RSL_CHAN_BCCH
|| type == RSL_CHAN_RACH
|| type == RSL_CHAN_PCH_AGCH) {
if (!slot->has_bcch) {
LOGP(DSUM, LOGL_NOTICE, "Slot %d has no BCCH\n", ts);
return NULL;
}
return slot->lchan[0];
} else {
LOGP(DSUM, LOGL_NOTICE, "Unknown channel type 0x%02x\n", type);
return NULL;
}
if (slot->acch_type == type)
return slot->lchan[sub];
}
LOGP(DSUM, LOGL_NOTICE, "No slot found with channel type 0x%02x\n", type);
return NULL;
}
struct osmocom_bts *create_bts(uint8_t num_trx, char *id)
{
struct osmocom_bts *bts;
struct osmobts_trx *trx;
int i, j;
LOGP(DSUM, LOGL_INFO, "Creating BTS\n");
bts = talloc_zero(l23_ctx, struct osmocom_bts);
if (!bts)
return NULL;
bts->link.bts = bts;
bts->id = id;
for (i = 0; i < num_trx; i++) {
LOGP(DSUM, LOGL_INFO, "Creating TRX %d\n", i);
trx = talloc_zero(l23_ctx, struct osmobts_trx);
if (!trx)
return NULL;
trx->bts = bts;
trx->trx_nr = i;
INIT_LLIST_HEAD(&trx->ms_list);
for (j = 0; j < 8; j++) {
trx->slot[j].trx = trx;
trx->slot[j].slot_nr = j;
trx->slot[j].chan_comb = 0xff; /* not set */
}
trx->link.trx = trx;
bts->trx[i] = trx;
}
bts->num_trx = num_trx;
return bts;
}
int create_ms(struct osmobts_trx *trx, int maskc, uint8_t *maskv_tx,
uint8_t *maskv_rx)
{
struct osmobts_ms *tx_ms, *rx_ms;
int i, j;
int rc;
static char sock_path[] = "/tmp/osmocom_l2.1";
for (i = 0; i < maskc; i++) {
/* create MS */
tx_ms = talloc_zero(l23_ctx, struct osmobts_ms);
printf("%p\n", tx_ms);
if (!tx_ms)
return -ENOMEM;
tx_ms->trx = trx;
rc = layer2_open(&tx_ms->ms, sock_path);
strcpy(tx_ms->ms.name, strchr(sock_path, '.') + 1);
sock_path[strlen(sock_path) - 1]++;
if (rc < 0) {
talloc_free(tx_ms);
return rc;
}
llist_add_tail(&tx_ms->entry, &trx->ms_list);
rx_ms = talloc_zero(l23_ctx, struct osmobts_ms);
rx_ms->trx = trx;
if (!rx_ms)
return -ENOMEM;
rc = layer2_open(&rx_ms->ms, sock_path);
strcpy(rx_ms->ms.name, strchr(sock_path, '.') + 1);
sock_path[strlen(sock_path) - 1]++;
if (rc < 0) {
talloc_free(rx_ms);
return rc;
}
llist_add_tail(&rx_ms->entry, &trx->ms_list);
/* assign to SLOT */
for (j = 0; j < 8; j++) {
if ((maskv_tx[i] & (1 << j)))
trx->slot[j].tx_ms = tx_ms;
if ((maskv_rx[i] & (1 << j)))
trx->slot[j].rx_ms = rx_ms;
}
}
return i;
}
void destroy_lchan(struct osmobts_lchan *lchan)
{
LOGP(DSUM, LOGL_INFO, "Destroying logical channel. (trx=%d ts=%d ss=%d)\n", lchan->slot->trx->trx_nr, lchan->slot->slot_nr, lchan->lchan_nr);
lapdm_exit(&lchan->l2_entity.lapdm_acch);
lapdm_exit(&lchan->l2_entity.lapdm_acch);
if (lchan->rtp.socket_created)
rtp_close_socket(&lchan->rtp);
talloc_free(lchan);
}
/* create and destroy lchan */
void bts_setup_slot(struct osmobts_slot *slot, uint8_t comb)
{
int i, ii;
struct osmobts_lchan *lchan;
uint8_t cbits;
if (slot->chan_comb == comb)
return;
/* destroy old */
for (i = 0; i < 8; i++) {
if (slot->lchan[i])
destroy_lchan(slot->lchan[i]);
slot->lchan[i] = NULL;
}
switch(comb) {
case 0xff:
return;
case NM_CHANC_TCHFull:
cbits = 0x01;
ii = 1;
break;
case NM_CHANC_TCHHalf:
cbits = 0x02;
ii = 2;
break;
case NM_CHANC_BCCHComb:
cbits = 0x04;
ii = 4;
break;
case NM_CHANC_SDCCH:
cbits = 0x08;
ii = 8;
break;
default:
cbits = 0x10;
ii = 0;
}
if (!slot->tx_ms) {
LOGP(DSUM, LOGL_ERROR, "Slot is not available\n");
return;
}
for (i = 0; i < ii; i++) {
LOGP(DSUM, LOGL_INFO, "Creating logical channel. (trx=%d ts=%d ss=%d)\n", slot->trx->trx_nr, slot->slot_nr, i);
lchan = talloc_zero(l23_ctx, struct osmobts_lchan);
lchan->slot = slot;
lchan->lchan_nr = i;
lchan->chan_nr = ((cbits | i) << 3) | slot->slot_nr;
lapdm_init(&lchan->l2_entity.lapdm_dcch, &lchan->l2_entity, &slot->tx_ms->ms);
lapdm_init(&lchan->l2_entity.lapdm_acch, &lchan->l2_entity, &slot->tx_ms->ms);
lapdm_set_bts();
osmol2_register_handler(&lchan->l2_entity, &rsl_tx_rll);
slot->lchan[i] = lchan;
}
}
static void destroy_ms(struct osmobts_ms *ms)
{
layer2_close(&ms->ms);
llist_del(&ms->entry);
talloc_free(ms);
}
void destroy_bts(struct osmocom_bts *bts)
{
int i;
struct osmobts_ms *ms;
for (i = 0; i < bts->num_trx; i++) {
abis_close(&bts->trx[i]->link);
while(!llist_empty(&bts->trx[i]->ms_list)) {
ms = llist_entry(bts->trx[i]->ms_list.next,
struct osmobts_ms, entry);
destroy_ms(ms);
}
if (osmo_timer_pending(&bts->trx[i]->si.timer))
osmo_timer_del(&bts->trx[i]->si.timer);
talloc_free(bts->trx[i]);
bts->trx[i] = NULL;
}
abis_close(&bts->link);
talloc_free(bts);
}
#endif
/* main link is established, send status report */ /* main link is established, send status report */
int bts_link_estab(struct gsm_bts *bts) int bts_link_estab(struct gsm_bts *bts)
{ {
@@ -349,10 +201,16 @@ int bts_link_estab(struct gsm_bts *bts)
LOGP(DSUM, LOGL_INFO, "Main link established, sending Status'.\n"); LOGP(DSUM, LOGL_INFO, "Main link established, sending Status'.\n");
/* BTS and SITE MGR are EAABLED, BTS is DEPENDENCY */ /* BTS and SITE MGR are EANBLED, BTS is DEPENDENCY */
oml_tx_state_changed(&bts->site_mgr.mo); oml_tx_state_changed(&bts->site_mgr.mo);
oml_tx_state_changed(&bts->mo); oml_tx_state_changed(&bts->mo);
/* those should all be in DEPENDENCY */
oml_tx_state_changed(&bts->gprs.nse.mo);
oml_tx_state_changed(&bts->gprs.cell.mo);
oml_tx_state_changed(&bts->gprs.nsvc[0].mo);
oml_tx_state_changed(&bts->gprs.nsvc[1].mo);
/* All other objects start off-line until the BTS Model code says otherwise */ /* All other objects start off-line until the BTS Model code says otherwise */
for (i = 0; i < bts->num_trx; i++) { for (i = 0; i < bts->num_trx; i++) {
struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, i); struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, i);
@@ -367,56 +225,26 @@ int bts_link_estab(struct gsm_bts *bts)
} }
} }
return 0; return bts_model_oml_estab(bts);
} }
/* RSL link is established, send status report */ /* RSL link is established, send status report */
int trx_link_estab(struct gsm_bts_trx *trx) int trx_link_estab(struct gsm_bts_trx *trx)
{ {
struct ipabis_link *link = (struct ipabis_link *) trx->rsl_link; struct e1inp_sign_link *link = trx->rsl_link;
uint8_t radio_state = (link->state == LINK_STATE_CONNECT) ? NM_OPSTATE_ENABLED : NM_OPSTATE_DISABLED; uint8_t radio_state = link ? NM_OPSTATE_ENABLED : NM_OPSTATE_DISABLED;
LOGP(DSUM, LOGL_INFO, "RSL link (TRX %02x) state changed to %s, sending Status'.\n", LOGP(DSUM, LOGL_INFO, "RSL link (TRX %02x) state changed to %s, sending Status'.\n",
trx->nr, (link->state == LINK_STATE_CONNECT) ? "up" : "down"); trx->nr, link ? "up" : "down");
oml_mo_state_chg(&trx->mo, radio_state, NM_AVSTATE_OK); oml_mo_state_chg(&trx->mo, radio_state, NM_AVSTATE_OK);
if (link->state == LINK_STATE_CONNECT) if (link)
rsl_tx_rf_res(trx); rsl_tx_rf_res(trx);
return 0; return 0;
} }
void bts_new_si(void *arg)
{
struct osmobts_trx *trx = arg;
#if 0
if (osmo_timer_pending(&trx->si.timer))
return;
i = 0;
while(i < BTS_SI_NUM) {
if ((trx->si.flags[i] & BTS_SI_NEW))
break;
i++;
}
if (i == BTS_SI_NUM)
return;
if ((trx->si.flags[i] & BTS_SI_USE))
LOGP(DSUM, LOGL_INFO, "Setting SYSTEM INFORMATION %s.\n", bts_si_name[i]);
else
LOGP(DSUM, LOGL_INFO, "Removing SYSTEM INFORMATION %s.\n", bts_si_name[i]);
trx->si.flags[i] &= ~BTS_SI_NEW;
/* distribute */
printf("TODO: send SI update to L1\n");
/* delay until next SI */
trx->si.timer.cb = bts_new_si;
trx->si.timer.data = trx;
osmo_timer_schedule(&trx->si.timer, 0, 200000);
#endif
}
int lchan_init_lapdm(struct gsm_lchan *lchan) int lchan_init_lapdm(struct gsm_lchan *lchan)
{ {
struct lapdm_channel *lc = &lchan->lapdm_ch; struct lapdm_channel *lc = &lchan->lapdm_ch;
@@ -429,26 +257,352 @@ int lchan_init_lapdm(struct gsm_lchan *lchan)
return 0; return 0;
} }
#define CCCH_RACH_RATIO_COMBINED256 (256*1/9)
#define CCCH_RACH_RATIO_SEPARATE256 (256*10/55)
int bts_agch_max_queue_length(int T, int bcch_conf)
{
int S, ccch_rach_ratio256, i;
int T_group = 0;
int is_ccch_comb = 0;
if (bcch_conf == RSL_BCCH_CCCH_CONF_1_C)
is_ccch_comb = 1;
/*
* The calculation is based on the ratio of the number RACH slots and
* CCCH blocks per time:
* Lmax = (T + 2*S) / R_RACH * R_CCCH
* where
* T3126_min = (T + 2*S) / R_RACH, as defined in GSM 04.08, 11.1.1
* R_RACH is the RACH slot rate (e.g. RACHs per multiframe)
* R_CCCH is the CCCH block rate (same time base like R_RACH)
* S and T are defined in GSM 04.08, 3.3.1.1.2
* The ratio is mainly influenced by the downlink only channels
* (BCCH, FCCH, SCH, CBCH) that can not be used for CCCH.
* An estimation with an error of < 10% is used:
* ~ 1/9 if CCCH is combined with SDCCH, and
* ~ 1/5.5 otherwise.
*/
ccch_rach_ratio256 = is_ccch_comb ?
CCCH_RACH_RATIO_COMBINED256 :
CCCH_RACH_RATIO_SEPARATE256;
for (i = 0; i < ARRAY_SIZE(tx_integer); i++) {
if (tx_integer[i] == T) {
T_group = i % 5;
break;
}
}
S = s_values[T_group][is_ccch_comb];
return (T + 2 * S) * ccch_rach_ratio256 / 256;
}
void bts_update_agch_max_queue_length(struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
struct gsm48_system_information_type_3 *si3;
int old_max_length = btsb->agch_max_queue_length;
if (!(bts->si_valid & (1<<SYSINFO_TYPE_3)))
return;
si3 = GSM_BTS_SI(bts, SYSINFO_TYPE_3);
btsb->agch_max_queue_length =
bts_agch_max_queue_length(si3->rach_control.tx_integer,
si3->control_channel_desc.ccch_conf);
if (btsb->agch_max_queue_length != old_max_length)
LOGP(DRSL, LOGL_INFO, "Updated AGCH max queue length to %d\n",
btsb->agch_max_queue_length);
}
#define REQ_REFS_PER_IMM_ASS_REJ 4
static int store_imm_ass_rej_refs(struct gsm48_imm_ass_rej *rej,
struct gsm48_req_ref *req_refs,
uint8_t *wait_inds,
int count)
{
switch (count) {
case 0:
/* TODO: Warning ? */
return 0;
default:
count = 4;
rej->req_ref4 = req_refs[3];
rej->wait_ind4 = wait_inds[3];
/* fall through */
case 3:
rej->req_ref3 = req_refs[2];
rej->wait_ind3 = wait_inds[2];
/* fall through */
case 2:
rej->req_ref2 = req_refs[1];
rej->wait_ind2 = wait_inds[1];
/* fall through */
case 1:
rej->req_ref1 = req_refs[0];
rej->wait_ind1 = wait_inds[0];
break;
}
switch (count) {
case 1:
rej->req_ref2 = req_refs[0];
rej->wait_ind2 = wait_inds[0];
/* fall through */
case 2:
rej->req_ref3 = req_refs[0];
rej->wait_ind3 = wait_inds[0];
/* fall through */
case 3:
rej->req_ref4 = req_refs[0];
rej->wait_ind4 = wait_inds[0];
/* fall through */
default:
break;
}
return count;
}
static int extract_imm_ass_rej_refs(struct gsm48_imm_ass_rej *rej,
struct gsm48_req_ref *req_refs,
uint8_t *wait_inds)
{
int count = 0;
req_refs[count] = rej->req_ref1;
wait_inds[count] = rej->wait_ind1;
count++;
if (memcmp(&rej->req_ref1, &rej->req_ref2, sizeof(rej->req_ref2))) {
req_refs[count] = rej->req_ref2;
wait_inds[count] = rej->wait_ind2;
count++;
}
if (memcmp(&rej->req_ref1, &rej->req_ref3, sizeof(rej->req_ref3)) &&
memcmp(&rej->req_ref2, &rej->req_ref3, sizeof(rej->req_ref3))) {
req_refs[count] = rej->req_ref3;
wait_inds[count] = rej->wait_ind3;
count++;
}
if (memcmp(&rej->req_ref1, &rej->req_ref4, sizeof(rej->req_ref4)) &&
memcmp(&rej->req_ref2, &rej->req_ref4, sizeof(rej->req_ref4)) &&
memcmp(&rej->req_ref3, &rej->req_ref4, sizeof(rej->req_ref4))) {
req_refs[count] = rej->req_ref4;
wait_inds[count] = rej->wait_ind4;
count++;
}
return count;
}
static int try_merge_imm_ass_rej(struct gsm48_imm_ass_rej *old_rej,
struct gsm48_imm_ass_rej *new_rej)
{
struct gsm48_req_ref req_refs[2 * REQ_REFS_PER_IMM_ASS_REJ];
uint8_t wait_inds[2 * REQ_REFS_PER_IMM_ASS_REJ];
int count = 0;
int stored = 0;
if (new_rej->msg_type != GSM48_MT_RR_IMM_ASS_REJ)
return 0;
if (old_rej->msg_type != GSM48_MT_RR_IMM_ASS_REJ)
return 0;
/* GSM 08.58, 5.7
* -> The BTS may combine serveral IMM.ASS.REJ messages
* -> Identical request refs in one message may be squeezed
*
* GSM 04.08, 9.1.20.2
* -> Request ref and wait ind are duplicated to fill the message
*/
/* Extract all entries */
count = extract_imm_ass_rej_refs(old_rej,
&req_refs[count], &wait_inds[count]);
if (count == REQ_REFS_PER_IMM_ASS_REJ)
return 0;
count += extract_imm_ass_rej_refs(new_rej,
&req_refs[count], &wait_inds[count]);
/* Store entries into old message */
stored = store_imm_ass_rej_refs(old_rej,
&req_refs[stored], &wait_inds[stored],
count);
count -= stored;
if (count == 0)
return 1;
/* Store remaining entries into new message */
stored += store_imm_ass_rej_refs(new_rej,
&req_refs[stored], &wait_inds[stored],
count);
return 0;
}
int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg) int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg)
{ {
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);; struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
int hard_limit = 1000;
struct gsm48_imm_ass_rej *imm_ass_cmd = msgb_l3(msg);
/* FIXME: implement max queue length */ if (btsb->agch_queue_length > hard_limit) {
llist_add_tail(&msg->list, &btsb->agch_queue); LOGP(DSUM, LOGL_ERROR,
"AGCH: too many messages in queue, "
"refusing message type 0x%02x, length = %d/%d\n",
((struct gsm48_imm_ass *)msgb_l3(msg))->msg_type,
btsb->agch_queue_length, btsb->agch_max_queue_length);
btsb->agch_queue_rejected_msgs++;
return -ENOMEM;
}
if (btsb->agch_queue_length > 0) {
struct msgb *last_msg =
llist_entry(btsb->agch_queue.prev, struct msgb, list);
struct gsm48_imm_ass_rej *last_imm_ass_rej = msgb_l3(last_msg);
if (try_merge_imm_ass_rej(last_imm_ass_rej, imm_ass_cmd)) {
btsb->agch_queue_merged_msgs++;
msgb_free(msg);
return 0;
}
}
msgb_enqueue(&btsb->agch_queue, msg);
btsb->agch_queue_length++;
return 0; return 0;
} }
struct msgb *bts_agch_dequeue(struct gsm_bts *bts) struct msgb *bts_agch_dequeue(struct gsm_bts *bts)
{ {
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);; struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
struct msgb *msg; struct msgb *msg = msgb_dequeue(&btsb->agch_queue);
if (!msg)
if (llist_empty(&btsb->agch_queue))
return NULL; return NULL;
msg = llist_entry(btsb->agch_queue.next, struct msgb, list); btsb->agch_queue_length--;
llist_del(&msg->list);
return msg; return msg;
} }
/*
* Remove lower prio messages if the queue has grown too long.
*
* \return 0 iff the number of messages in the queue would fit into the AGCH
* reserved part of the CCCH.
*/
static void compact_agch_queue(struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
struct msgb *msg, *msg2;
int max_len, slope, offs;
int level_low = btsb->agch_queue_low_level;
int level_high = btsb->agch_queue_high_level;
int level_thres = btsb->agch_queue_thresh_level;
max_len = btsb->agch_max_queue_length;
if (max_len == 0)
max_len = 1;
if (btsb->agch_queue_length < max_len * level_thres / 100)
return;
/* p^
* 1+ /'''''
* | /
* | /
* 0+---/--+----+--> Q length
* low high max_len
*/
offs = max_len * level_low / 100;
if (level_high > level_low)
slope = 0x10000 * 100 / (level_high - level_low);
else
slope = 0x10000 * max_len; /* p_drop >= 1 if len > offs */
llist_for_each_entry_safe(msg, msg2, &btsb->agch_queue, list) {
struct gsm48_imm_ass *imm_ass_cmd = msgb_l3(msg);
int p_drop;
if (imm_ass_cmd->msg_type != GSM48_MT_RR_IMM_ASS_REJ)
return;
/* IMMEDIATE ASSIGN REJECT */
p_drop = (btsb->agch_queue_length - offs) * slope / max_len;
if ((random() & 0xffff) >= p_drop)
return;
llist_del(&msg->list);
btsb->agch_queue_length--;
msgb_free(msg);
btsb->agch_queue_dropped_msgs++;
}
return;
}
int bts_ccch_copy_msg(struct gsm_bts *bts, uint8_t *out_buf, struct gsm_time *gt,
int is_ag_res)
{
struct msgb *msg = NULL;
struct gsm_bts_role_bts *btsb = bts->role;
int rc = 0;
int is_empty = 1;
/* Do queue house keeping.
* This needs to be done every time a CCCH message is requested, since
* the queue max length is calculated based on the CCCH block rate and
* PCH messages also reduce the drain of the AGCH queue.
*/
compact_agch_queue(bts);
/* Check for paging messages first if this is PCH */
if (!is_ag_res)
rc = paging_gen_msg(btsb->paging_state, out_buf, gt, &is_empty);
/* Check whether the block may be overwritten */
if (!is_empty)
return rc;
msg = bts_agch_dequeue(bts);
if (!msg)
return rc;
/* Copy AGCH message */
memcpy(out_buf, msgb_l3(msg), msgb_l3len(msg));
rc = msgb_l3len(msg);
msgb_free(msg);
if (is_ag_res)
btsb->agch_queue_agch_msgs++;
else
btsb->agch_queue_pch_msgs++;
return rc;
}
int bts_supports_cipher(struct gsm_bts_role_bts *bts, int rsl_cipher)
{
int sup;
if (rsl_cipher < 1 || rsl_cipher > 8)
return -ENOTSUP;
/* No encryption is always supported */
if (rsl_cipher == 1)
return 1;
sup = (1 << (rsl_cipher - 2)) & bts->support.ciphers;
return sup > 0;
}

View File

@@ -0,0 +1,78 @@
/* Control Interface for osmo-bts */
/* (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 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 <fcntl.h>
#include <osmocom/ctrl/control_cmd.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/tx_power.h>
CTRL_CMD_DEFINE(therm_att, "thermal-attenuation");
static int get_therm_att(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts_trx *trx = cmd->node;
struct trx_power_params *tpp = &trx->power_params;
cmd->reply = talloc_asprintf(cmd, "%d", tpp->thermal_attenuation_mdB);
return CTRL_CMD_REPLY;
}
static int set_therm_att(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts_trx *trx = cmd->node;
struct trx_power_params *tpp = &trx->power_params;
int val = atoi(cmd->value);
printf("set_therm_att(trx=%p, tpp=%p)\n", trx, tpp);
tpp->thermal_attenuation_mdB = val;
power_ramp_start(trx, tpp->p_total_cur_mdBm, 0);
return get_therm_att(cmd, data);
}
static int verify_therm_att(struct ctrl_cmd *cmd, const char *value, void *data)
{
int val = atoi(value);
/* permit between 0 to 40 dB attenuation */
if (val < 0 || val > to_mdB(40))
return 1;
return 0;
}
int bts_ctrl_cmds_install(struct gsm_bts *bts)
{
int rc = 0;
rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_therm_att);
return rc;
}

View File

@@ -0,0 +1,104 @@
/* Control Interface for osmo-bts */
/* (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 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 <errno.h>
#include <osmocom/vty/command.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/ctrl/ports.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
extern vector ctrl_node_vec;
/*! \brief control interface lookup function for bsc/bts gsm_data
* \param[in] data Private data passed to controlif_setup()
* \param[in] vline Vector of the line holding the command string
* \param[out] node_type type (CTRL_NODE_) that was determined
* \param[out] node_data private dta of node that was determined
* \param i Current index into vline, up to which it is parsed
*/
static int bts_ctrl_node_lookup(void *data, vector vline, int *node_type,
void **node_data, int *i)
{
struct gsm_bts *bts = data;
struct gsm_bts_trx *trx = NULL;
struct gsm_bts_trx_ts *ts = NULL;
char *token = vector_slot(vline, *i);
long num;
/* TODO: We need to make sure that the following chars are digits
* and/or use strtol to check if number conversion was successful
* Right now something like net.bts_stats will not work */
if (!strcmp(token, "trx")) {
if (*node_type != CTRL_NODE_ROOT || !*node_data)
goto err_missing;
bts = *node_data;
(*i)++;
if (!ctrl_parse_get_num(vline, *i, &num))
goto err_index;
trx = gsm_bts_trx_num(bts, num);
if (!trx)
goto err_missing;
*node_data = trx;
*node_type = CTRL_NODE_TRX;
} else if (!strcmp(token, "ts")) {
if (*node_type != CTRL_NODE_TRX || !*node_data)
goto err_missing;
trx = *node_data;
(*i)++;
if (!ctrl_parse_get_num(vline, *i, &num))
goto err_index;
if ((num >= 0) && (num < TRX_NR_TS))
ts = &trx->ts[num];
if (!ts)
goto err_missing;
*node_data = ts;
*node_type = CTRL_NODE_TS;
} else
return 0;
return 1;
err_missing:
return -ENODEV;
err_index:
return -ERANGE;
}
struct ctrl_handle *bts_controlif_setup(struct gsm_bts *bts)
{
struct ctrl_handle *hdl;
int rc = 0;
hdl = ctrl_interface_setup(bts, OSMO_CTRL_PORT_BTS, bts_ctrl_node_lookup);
if (!hdl)
return NULL;
rc = bts_ctrl_cmds_install(bts);
if (rc) {
/* FIXME: close control interface */
return NULL;
}
return hdl;
}

202
src/common/cbch.c Normal file
View File

@@ -0,0 +1,202 @@
/* Cell Broadcast routines */
/* (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 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 <errno.h>
#include <osmocom/core/linuxlist.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 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;
};
/* 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 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;
}
DEBUGP(DLSMS, "Current SMS-CB %s: ",
osmo_hexdump_nospc(msg->msg, sizeof(msg->msg)));
/* 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;
/* copy data and increment index */
memcpy(out, &msg->msg[msg->next_seg * SMS_CB_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;
else
block_type->lb = 0;
if (block_type->lb == 1) {
/* remove/release the message memory */
talloc_free(btsb->smscb_state.cur_msg);
btsb->smscb_state.cur_msg = NULL;
}
return block_type->lb;
}
static const uint8_t last_block_rsl2um[4] = {
[RSL_CB_CMD_LASTBLOCK_4] = 4,
[RSL_CB_CMD_LASTBLOCK_1] = 1,
[RSL_CB_CMD_LASTBLOCK_2] = 2,
[RSL_CB_CMD_LASTBLOCK_3] = 3,
};
/* incoming SMS broadcast command from RSL */
int bts_process_smscb_cmd(struct gsm_bts *bts,
struct rsl_ie_cb_cmd_type cmd_type,
uint8_t msg_len, const uint8_t *msg)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
struct smscb_msg *scm;
if (msg_len > sizeof(scm->msg)) {
LOGP(DLSMS, LOGL_ERROR,
"Cannot process SMSCB of %u bytes (max %u)\n",
msg_len, sizeof(scm->msg));
return -EINVAL;
}
scm = talloc_zero_size(bts, sizeof(*scm));
/* initialize entire message with default padding */
memset(scm->msg, GSM_MACBLOCK_PADDING, sizeof(scm->msg));
/* next segment is first segment */
scm->next_seg = 0;
switch (cmd_type.command) {
case RSL_CB_CMD_TYPE_NORMAL:
case RSL_CB_CMD_TYPE_SCHEDULE:
case RSL_CB_CMD_TYPE_NULL:
scm->num_segs = last_block_rsl2um[cmd_type.last_block&3];
memcpy(scm->msg, msg, msg_len);
/* def_bcast is ignored */
break;
case RSL_CB_CMD_TYPE_DEFAULT:
/* use def_bcast, ignore command */
/* def_bcast == 0: normal mess */
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;
}
static struct smscb_msg *select_next_smscb(struct gsm_bts *bts)
{
struct smscb_msg *msg;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
if (llist_empty(&btsb->smscb_state.queue))
return NULL;
msg = llist_entry(btsb->smscb_state.queue.next,
struct smscb_msg, list);
llist_del(&msg->list);
return msg;
}
/* call-back from bts model specific code when it wants to obtain a CBCH
* block for a given gsm_time. outbuf must have 23 bytes of space. */
int bts_cbch_get(struct gsm_bts *bts, uint8_t *outbuf, struct gsm_time *g_time)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
uint32_t fn = gsm_gsmtime2fn(g_time);
/* According to 05.02 Section 6.5.4 */
uint32_t tb = (fn / 51) % 8;
int rc = 0;
/* The multiframes used for the basic cell broadcast channel
* shall be those in * which TB = 0,1,2 and 3. The multiframes
* used for the extended cell broadcast channel shall be those
* in which TB = 4, 5, 6 and 7 */
/* The SMSCB header shall be sent in the multiframe in which TB
* = 0 for the basic, and TB = 4 for the extended cell
* broadcast channel. */
switch (tb) {
case 0:
/* select a new SMSCB message */
btsb->smscb_state.cur_msg = select_next_smscb(bts);
rc = get_smscb_block(bts, outbuf);
break;
case 1: case 2: case 3:
rc = get_smscb_block(bts, outbuf);
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);
break;
}
return rc;
}

111
src/common/etws_p1.c Normal file
View File

@@ -0,0 +1,111 @@
/* 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;
}

157
src/common/handover.c Normal file
View File

@@ -0,0 +1,157 @@
/* Paging message encoding + queue management */
/* (C) 2012-2013 by Harald Welte <laforge@gnumonks.org>
* Andreas Eversberg <jolly@eversberg.eu>
* (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 <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/rsl.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/handover.h>
/* Transmit a handover related PHYS INFO on given lchan */
static int ho_tx_phys_info(struct gsm_lchan *lchan)
{
struct msgb *msg = msgb_alloc_headroom(1024, 128, "PHYS INFO");
struct gsm48_hdr *gh;
if (!msg)
return -ENOMEM;
LOGP(DHO, LOGL_INFO,
"%s Sending PHYSICAL INFORMATION to MS.\n",
gsm_lchan_name(lchan));
/* Build RSL UNITDATA REQUEST message with 04.08 PHYS INFO */
msg->l3h = msg->data;
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_HANDO_INFO;
msgb_put_u8(msg, lchan->rqd_ta);
rsl_rll_push_l3(msg, RSL_MT_UNIT_DATA_REQ, gsm_lchan2chan_nr(lchan),
0x00, 0);
lapdm_rslms_recvmsg(msg, &lchan->lapdm_ch);
return 0;
}
/* timer call-back for T3105 (handover PHYS INFO re-transmit) */
static void ho_t3105_cb(void *data)
{
struct gsm_lchan *lchan = data;
struct gsm_bts *bts = lchan->ts->trx->bts;
struct gsm_bts_role_bts *btsb = bts->role;
LOGP(DHO, LOGL_INFO, "%s T3105 timeout (%d resends left)\n",
gsm_lchan_name(lchan), btsb->ny1 - lchan->ho.phys_info_count);
if (lchan->state != LCHAN_S_ACTIVE) {
LOGP(DHO, LOGL_NOTICE,
"%s is in not active. It is in state %s. Ignoring\n",
gsm_lchan_name(lchan), gsm_lchans_name(lchan->state));
return;
}
if (lchan->ho.phys_info_count >= btsb->ny1) {
/* HO Abort */
LOGP(DHO, LOGL_NOTICE, "%s NY1 reached, sending CONNection "
"FAILure to BSC.\n", gsm_lchan_name(lchan));
rsl_tx_conn_fail(lchan, RSL_ERR_HANDOVER_ACC_FAIL);
return;
}
ho_tx_phys_info(lchan);
lchan->ho.phys_info_count++;
osmo_timer_schedule(&lchan->ho.t3105, 0, btsb->t3105_ms * 1000);
}
/* received random access on dedicated channel */
void handover_rach(struct gsm_lchan *lchan, uint8_t ra, uint8_t acc_delay)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
struct gsm_bts_role_bts *btsb = bts->role;
/* Ignore invalid handover ref */
if (lchan->ho.ref != ra) {
LOGP(DHO, LOGL_INFO, "%s RACH on dedicated channel received, but "
"ra=0x%02x != expected ref=0x%02x. (This is no bug)\n",
gsm_lchan_name(lchan), ra, lchan->ho.ref);
return;
}
LOGP(DHO, LOGL_NOTICE,
"%s RACH on dedicated channel received with TA=%u\n",
gsm_lchan_name(lchan), acc_delay);
/* Set timing advance */
lchan->rqd_ta = acc_delay;
/* Stop handover detection, wait for valid frame */
lchan->ho.active = HANDOVER_WAIT_FRAME;
if (bts_model_rsl_chan_mod(lchan) != 0) {
LOGP(DHO, LOGL_ERROR,
"%s failed to modify channel after handover\n",
gsm_lchan_name(lchan));
rsl_tx_conn_fail(lchan, RSL_ERR_HANDOVER_ACC_FAIL);
return;
}
/* Send HANDover DETect to BSC */
rsl_tx_hando_det(lchan, &lchan->rqd_ta);
/* Send PHYS INFO */
lchan->ho.phys_info_count = 1;
ho_tx_phys_info(lchan);
/* Start T3105 */
LOGP(DHO, LOGL_DEBUG,
"%s Starting T3105 with %u ms\n",
gsm_lchan_name(lchan), btsb->t3105_ms);
lchan->ho.t3105.cb = ho_t3105_cb;
lchan->ho.t3105.data = lchan;
osmo_timer_schedule(&lchan->ho.t3105, 0, btsb->t3105_ms * 1000);
}
/* received frist valid data frame on dedicated channel */
void handover_frame(struct gsm_lchan *lchan)
{
LOGP(DHO, LOGL_INFO,
"%s First valid frame detected\n", gsm_lchan_name(lchan));
handover_reset(lchan);
}
/* release handover state */
void handover_reset(struct gsm_lchan *lchan)
{
/* Stop T3105 */
osmo_timer_del(&lchan->ho.t3105);
/* Handover process is done */
lchan->ho.active = HANDOVER_NONE;
}

27
src/common/lchan.c Normal file
View File

@@ -0,0 +1,27 @@
/* OsmoBTS lchan interface */
/* (C) 2012 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 <osmo-bts/gsm_data.h>
void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state)
{
lchan->state = state;
}

View File

@@ -19,12 +19,19 @@
* *
*/ */
#include <rsl.h> #include <stdint.h>
#include <osmocom/core/timer.h> #include <osmocom/core/timer.h>
#include <osmocom/core/msgb.h>
static void reset_load_counters(void) #include <osmo-bts/gsm_data.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/paging.h>
static void reset_load_counters(struct gsm_bts *bts)
{ {
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
/* re-set the counters */ /* re-set the counters */
btsb->load.ccch.pch_used = btsb->load.ccch.pch_total = 0; btsb->load.ccch.pch_used = btsb->load.ccch.pch_total = 0;
} }
@@ -32,38 +39,62 @@ static void reset_load_counters(void)
static void load_timer_cb(void *data) static void load_timer_cb(void *data)
{ {
struct gsm_bts *bts = data; struct gsm_bts *bts = data;
struct gsm_bts_role_bts *btsb = FIXME; struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
unsigned int pch_percent; unsigned int pch_percent, rach_percent;
/* compute percentages */ /* compute percentages */
pch_percent = (btsb->load.ccch.pch_used * 100) / btsb->load.ccch.pch_total; if (btsb->load.ccch.pch_total == 0)
pch_percent = 0;
else
pch_percent = (btsb->load.ccch.pch_used * 100) /
btsb->load.ccch.pch_total;
if (pch_percent >= btsb->load.ccch.load_ind_thresh) { if (pch_percent >= btsb->load.ccch.load_ind_thresh) {
/* send RSL load indication message to BSC */ /* send RSL load indication message to BSC */
uint16_t paging_buffer_space = FIXME; uint16_t buffer_space = paging_buffer_space(btsb->paging_state);
rsl_tx_ccch_load_ind_pch(bts, paging_buffer_space); rsl_tx_ccch_load_ind_pch(bts, buffer_space);
} else {
/* This is an extenstion of TS 08.58. We don't only
* send load indications if the load is above threshold,
* but we also explicitly indicate that we are below
* threshold by using the magic value 0xffff */
rsl_tx_ccch_load_ind_pch(bts, 0xffff);
} }
reset_load_counters(); if (btsb->load.rach.total == 0)
rach_percent = 0;
else
rach_percent = (btsb->load.rach.busy * 100) /
btsb->load.rach.total;
if (rach_percent >= btsb->load.ccch.load_ind_thresh) {
/* send RSL load indication message to BSC */
rsl_tx_ccch_load_ind_rach(bts, btsb->load.rach.total,
btsb->load.rach.busy,
btsb->load.rach.access);
}
reset_load_counters(bts);
/* re-schedule the timer */ /* re-schedule the timer */
osmo_timer_schedule(&btsb->load.ccch.timer, osmo_timer_schedule(&btsb->load.ccch.timer,
btsb->load.ccch.load_ind_period, 0); btsb->load.ccch.load_ind_period, 0);
} }
static void load_timer_start(struct gsm_bts *bts) void load_timer_start(struct gsm_bts *bts)
{ {
struct gsm_bts_role_bts *btsb = FIXME; struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
btsb->load.ccch.timer.data = bts; btsb->load.ccch.timer.data = bts;
reset_load_counters(); btsb->load.ccch.timer.cb = load_timer_cb;
reset_load_counters(bts);
osmo_timer_schedule(&btsb->load.ccch.timer, osmo_timer_schedule(&btsb->load.ccch.timer,
btsb->load.ccch.load_ind_period, 0); btsb->load.ccch.load_ind_period, 0);
return 0
} }
static void load_timer_stop(struct gsm_bts *bts) void load_timer_stop(struct gsm_bts *bts)
{ {
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
osmo_timer_del(&btsb->load.ccch.timer); osmo_timer_del(&btsb->load.ccch.timer);
} }

View File

@@ -95,6 +95,18 @@ static struct log_info_cat bts_log_info_cat[] = {
.loglevel = LOGL_NOTICE, .loglevel = LOGL_NOTICE,
.enabled = 1, .enabled = 1,
}, },
[DPCU] = {
.name = "DPCU",
.description = "PCU interface",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DHO] = {
.name = "DHO",
.description = "Handover",
.color = "\033[0;37m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
#if 0 #if 0
[DNS] = { [DNS] = {
.name = "DNS", .name = "DNS",

View File

@@ -70,11 +70,13 @@ static int is_meas_complete(enum gsm_phys_chan_config pchan, unsigned int ts,
rc = 1; rc = 1;
break; break;
case GSM_PCHAN_SDCCH8_SACCH8C: case GSM_PCHAN_SDCCH8_SACCH8C:
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
fn_mod = fn % 102; fn_mod = fn % 102;
if (fn_mod == 11) if (fn_mod == 11)
rc = 1; rc = 1;
break; break;
case GSM_PCHAN_CCCH_SDCCH4: case GSM_PCHAN_CCCH_SDCCH4:
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
fn_mod = fn % 102; fn_mod = fn % 102;
if (fn_mod == 36) if (fn_mod == 36)
rc = 1; rc = 1;
@@ -92,6 +94,11 @@ int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm)
DEBUGP(DMEAS, "%s adding measurement, num_ul_meas=%d\n", DEBUGP(DMEAS, "%s adding measurement, num_ul_meas=%d\n",
gsm_lchan_name(lchan), lchan->meas.num_ul_meas); gsm_lchan_name(lchan), lchan->meas.num_ul_meas);
if (lchan->state != LCHAN_S_ACTIVE) {
LOGP(DMEAS, LOGL_NOTICE, "%s measurement during state: %s\n",
gsm_lchan_name(lchan), gsm_lchans_name(lchan->state));
}
if (lchan->meas.num_ul_meas >= ARRAY_SIZE(lchan->meas.uplink)) { if (lchan->meas.num_ul_meas >= ARRAY_SIZE(lchan->meas.uplink)) {
LOGP(DMEAS, LOGL_NOTICE, "%s no space for uplink measurement\n", LOGP(DMEAS, LOGL_NOTICE, "%s no space for uplink measurement\n",
gsm_lchan_name(lchan)); gsm_lchan_name(lchan));
@@ -125,8 +132,9 @@ static uint8_t ber10k_to_rxqual(uint32_t ber10k)
return 7; return 7;
} }
int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn) static int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
{ {
struct gsm_meas_rep_unidir *mru;
uint32_t ber_full_sum = 0; uint32_t ber_full_sum = 0;
uint32_t irssi_full_sum = 0; uint32_t irssi_full_sum = 0;
uint32_t ber_sub_sum = 0; uint32_t ber_sub_sum = 0;
@@ -178,10 +186,11 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
irssi_sub_sum); irssi_sub_sum);
/* store results */ /* store results */
lchan->meas.res.rxlev_full = dbm2rxlev((int)irssi_full_sum * -1); mru = &lchan->meas.ul_res;
lchan->meas.res.rxlev_sub = dbm2rxlev((int)irssi_sub_sum * -1); mru->full.rx_lev = dbm2rxlev((int)irssi_full_sum * -1);
lchan->meas.res.rxqual_full = ber10k_to_rxqual(ber_full_sum); mru->sub.rx_lev = dbm2rxlev((int)irssi_sub_sum * -1);
lchan->meas.res.rxqual_sub = ber10k_to_rxqual(ber_sub_sum); mru->full.rx_qual = ber10k_to_rxqual(ber_full_sum);
mru->sub.rx_qual = ber10k_to_rxqual(ber_sub_sum);
lchan->meas.flags |= LC_UL_M_F_RES_VALID; lchan->meas.flags |= LC_UL_M_F_RES_VALID;
lchan->meas.num_ul_meas = 0; lchan->meas.num_ul_meas = 0;
@@ -194,19 +203,34 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
/* build the 3 byte RSL uplinke measurement IE content */ /* build the 3 byte RSL uplinke measurement IE content */
int lchan_build_rsl_ul_meas(struct gsm_lchan *lchan, uint8_t *buf) int lchan_build_rsl_ul_meas(struct gsm_lchan *lchan, uint8_t *buf)
{ {
buf[0] = (lchan->meas.res.rxlev_full & 0x3f); /* FIXME: DTXu support */ struct gsm_meas_rep_unidir *mru = &lchan->meas.ul_res;
buf[1] = (lchan->meas.res.rxlev_sub & 0x3f); buf[0] = (mru->full.rx_lev & 0x3f); /* FIXME: DTXu support */
buf[2] = ((lchan->meas.res.rxqual_full & 7) << 3) | buf[1] = (mru->sub.rx_lev & 0x3f);
(lchan->meas.res.rxqual_sub & 7); buf[2] = ((mru->full.rx_qual & 7) << 3) | (mru->sub.rx_qual & 7);
return 3; return 3;
} }
int ts_meas_check_compute(struct gsm_bts_trx_ts *ts, uint32_t fn) /* Copied from OpenBSC and enlarged to _GSM_PCHAN_MAX */
static const uint8_t subslots_per_pchan[_GSM_PCHAN_MAX] = {
[GSM_PCHAN_NONE] = 0,
[GSM_PCHAN_CCCH] = 0,
[GSM_PCHAN_CCCH_SDCCH4] = 4,
[GSM_PCHAN_CCCH_SDCCH4_CBCH] = 4,
[GSM_PCHAN_TCH_F] = 1,
[GSM_PCHAN_TCH_H] = 2,
[GSM_PCHAN_SDCCH8_SACCH8C] = 8,
[GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 8,
/* FIXME: what about dynamic TCH_F_TCH_H ? */
[GSM_PCHAN_TCH_F_PDCH] = 1,
};
static int ts_meas_check_compute(struct gsm_bts_trx_ts *ts, uint32_t fn)
{ {
int i; int i;
const int num_subslots = subslots_per_pchan[ts->pchan];
for (i = 0; i < ARRAY_SIZE(ts->lchan); i++) { for (i = 0; i < num_subslots; ++i) {
struct gsm_lchan *lchan = &ts->lchan[i]; struct gsm_lchan *lchan = &ts->lchan[i];
if (lchan->state != LCHAN_S_ACTIVE) if (lchan->state != LCHAN_S_ACTIVE)
@@ -216,6 +240,7 @@ int ts_meas_check_compute(struct gsm_bts_trx_ts *ts, uint32_t fn)
case GSM_LCHAN_SDCCH: case GSM_LCHAN_SDCCH:
case GSM_LCHAN_TCH_F: case GSM_LCHAN_TCH_F:
case GSM_LCHAN_TCH_H: case GSM_LCHAN_TCH_H:
case GSM_LCHAN_PDTCH:
lchan_meas_check_compute(lchan, fn); lchan_meas_check_compute(lchan, fn);
break; break;
default: default:

175
src/common/msg_utils.c Normal file
View File

@@ -0,0 +1,175 @@
/* (C) 2014 by sysmocom s.f.m.c. GmbH
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmo-bts/msg_utils.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/oml.h>
#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmocom/gsm/protocol/gsm_12_21.h>
#include <osmocom/gsm/abis_nm.h>
#include <osmocom/core/msgb.h>
#include <arpa/inet.h>
static int check_fom(struct abis_om_hdr *omh, size_t len)
{
if (omh->length != len) {
LOGP(DL1C, LOGL_ERROR, "Incorrect OM hdr length value %d %zu\n",
omh->length, len);
return -1;
}
if (len < sizeof(struct abis_om_fom_hdr)) {
LOGP(DL1C, LOGL_ERROR, "FOM header insufficient space %zu %zu\n",
len, sizeof(struct abis_om_fom_hdr));
return -1;
}
return 0;
}
static int check_manuf(struct msgb *msg, struct abis_om_hdr *omh, size_t msg_size)
{
int type;
size_t size;
if (msg_size < 1) {
LOGP(DL1C, LOGL_ERROR, "No ManId Length Indicator %zu\n",
msg_size);
return -1;
}
if (omh->data[0] >= msg_size - 1) {
LOGP(DL1C, LOGL_ERROR,
"Insufficient message space for this ManId Length %d %zu\n",
omh->data[0], msg_size - 1);
return -1;
}
if (omh->data[0] == sizeof(abis_nm_ipa_magic) &&
strncmp(abis_nm_ipa_magic, (const char *)omh->data + 1,
sizeof(abis_nm_ipa_magic)) == 0) {
type = OML_MSG_TYPE_IPA;
size = sizeof(abis_nm_ipa_magic) + 1;
} else if (omh->data[0] == sizeof(abis_nm_osmo_magic) &&
strncmp(abis_nm_osmo_magic, (const char *) omh->data + 1,
sizeof(abis_nm_osmo_magic)) == 0) {
type = OML_MSG_TYPE_OSMO;
size = sizeof(abis_nm_osmo_magic) + 1;
} else {
LOGP(DL1C, LOGL_ERROR, "Manuf Label Unknown\n");
return -1;
}
/* we have verified that the vendor string fits */
msg->l3h = omh->data + size;
if (check_fom(omh, msgb_l3len(msg)) != 0)
return -1;
return type;
}
/**
* Return 0 in case the IPA structure is okay and in this
* case the l2h will be set to the beginning of the data.
*/
int msg_verify_ipa_structure(struct msgb *msg)
{
struct ipaccess_head *hh;
if (msgb_l1len(msg) < sizeof(struct ipaccess_head)) {
LOGP(DL1C, LOGL_ERROR,
"Ipa header insufficient space %d %d\n",
msgb_l1len(msg), sizeof(struct ipaccess_head));
return -1;
}
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",
ntohs(hh->len), msgb_l1len(msg) - sizeof(struct ipaccess_head));
return -1;
}
msg->l2h = hh->data;
return 0;
}
/**
* \brief Verify the structure of the OML message and set l3h
*
* This function verifies that the data in \param in msg is a proper
* OML message. This code assumes that msg->l2h points to the
* beginning of the OML message. In the successful case the msg->l3h
* will be set and will point to the FOM header. The value is undefined
* in all other cases.
*
* \param msg The message to analyze starting from msg->l2h.
* \return In case the structure is correct a positive number will be
* returned and msg->l3h will point to the FOM. The number is a
* classification of the vendor type of the message.
*/
int msg_verify_oml_structure(struct msgb *msg)
{
struct abis_om_hdr *omh;
if (msgb_l2len(msg) < sizeof(*omh)) {
LOGP(DL1C, LOGL_ERROR, "Om header insufficient space %d %d\n",
msgb_l2len(msg), sizeof(*omh));
return -1;
}
omh = (struct abis_om_hdr *) msg->l2h;
if (omh->mdisc != ABIS_OM_MDISC_FOM &&
omh->mdisc != ABIS_OM_MDISC_MANUF) {
LOGP(DL1C, LOGL_ERROR, "Incorrect om mdisc value %x\n",
omh->mdisc);
return -1;
}
if (omh->placement != ABIS_OM_PLACEMENT_ONLY) {
LOGP(DL1C, LOGL_ERROR, "Incorrect om placement value %x %x\n",
omh->placement, ABIS_OM_PLACEMENT_ONLY);
return -1;
}
if (omh->sequence != 0) {
LOGP(DL1C, LOGL_ERROR, "Incorrect om sequence value %d\n",
omh->sequence);
return -1;
}
if (omh->mdisc == ABIS_OM_MDISC_MANUF)
return check_manuf(msg, omh, msgb_l2len(msg) - sizeof(*omh));
msg->l3h = omh->data;
if (check_fom(omh, msgb_l3len(msg)) != 0)
return -1;
return OML_MSG_TYPE_ETSI;
}

View File

@@ -1,7 +1,7 @@
/* GSM TS 12.21 O&M / OML, BTS side */ /* GSM TS 12.21 O&M / OML, BTS side */
/* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu> /* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2011 by Harald Welte <laforge@gnumonks.org> * (C) 2011-2013 by Harald Welte <laforge@gnumonks.org>
* *
* All Rights Reserved * All Rights Reserved
* *
@@ -31,12 +31,16 @@
#include <osmocom/core/talloc.h> #include <osmocom/core/talloc.h>
#include <osmocom/gsm/protocol/gsm_12_21.h> #include <osmocom/gsm/protocol/gsm_12_21.h>
#include <osmocom/gsm/abis_nm.h> #include <osmocom/gsm/abis_nm.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/abis/ipaccess.h>
#include <osmo-bts/logging.h> #include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h> #include <osmo-bts/gsm_data.h>
#include <osmo-bts/abis.h> #include <osmo-bts/abis.h>
#include <osmo-bts/oml.h> #include <osmo-bts/oml.h>
#include <osmo-bts/bts_model.h> #include <osmo-bts/bts_model.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/signal.h>
/* FIXME: move this to libosmocore */ /* FIXME: move this to libosmocore */
static struct tlv_definition abis_nm_att_tlvdef_ipa = { static struct tlv_definition abis_nm_att_tlvdef_ipa = {
@@ -87,8 +91,7 @@ static struct tlv_definition abis_nm_att_tlvdef_ipa = {
}, },
}; };
/* ip.access nanoBTS specific commands */ static int oml_ipa_set_attr(struct gsm_bts *bts, struct msgb *msg);
static const char ipaccess_magic[] = "com.ipaccess";
/* /*
* support * support
@@ -160,9 +163,9 @@ int oml_send_msg(struct msgb *msg, int is_manuf)
if (is_manuf) { if (is_manuf) {
/* length byte, string + 0 termination */ /* length byte, string + 0 termination */
uint8_t *manuf = msgb_push(msg, 1 + sizeof(ipaccess_magic)); uint8_t *manuf = msgb_push(msg, 1 + sizeof(abis_nm_ipa_magic));
manuf[0] = strlen(ipaccess_magic)+1; manuf[0] = strlen(abis_nm_ipa_magic)+1;
memcpy(manuf+1, ipaccess_magic, strlen(ipaccess_magic)); memcpy(manuf+1, abis_nm_ipa_magic, strlen(abis_nm_ipa_magic));
} }
/* Push the main OML header and send it off */ /* Push the main OML header and send it off */
@@ -226,6 +229,13 @@ int oml_tx_state_changed(struct gsm_abis_mo *mo)
return oml_mo_send_msg(mo, nmsg, NM_MT_STATECHG_EVENT_REP); return oml_mo_send_msg(mo, nmsg, NM_MT_STATECHG_EVENT_REP);
} }
/* First initialization of MO, does _not_ generate state changes */
void oml_mo_state_init(struct gsm_abis_mo *mo, int op_state, int avail_state)
{
mo->nm_state.availability = avail_state;
mo->nm_state.operational = op_state;
}
int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state) int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state)
{ {
int rc = 0; int rc = 0;
@@ -245,6 +255,7 @@ int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state)
abis_nm_opstate_name(mo->nm_state.operational), abis_nm_opstate_name(mo->nm_state.operational),
abis_nm_opstate_name(op_state)); abis_nm_opstate_name(op_state));
mo->nm_state.operational = op_state; mo->nm_state.operational = op_state;
osmo_signal_dispatch(SS_GLOBAL, S_NEW_OP_STATE, NULL);
} }
/* send state change report */ /* send state change report */
@@ -273,6 +284,24 @@ int oml_mo_fom_ack_nack(struct gsm_abis_mo *mo, uint8_t orig_msg_type,
return oml_mo_send_msg(mo, msg, new_msg_type); return oml_mo_send_msg(mo, msg, new_msg_type);
} }
int oml_mo_statechg_ack(struct gsm_abis_mo *mo)
{
struct msgb *msg;
msg = oml_msgb_alloc();
if (!msg)
return -ENOMEM;
msgb_tv_put(msg, NM_ATT_ADM_STATE, mo->nm_state.administrative);
return oml_mo_send_msg(mo, msg, NM_MT_CHG_ADM_STATE_ACK);
}
int oml_mo_statechg_nack(struct gsm_abis_mo *mo, uint8_t nack_cause)
{
return oml_mo_fom_ack_nack(mo, NM_MT_CHG_ADM_STATE, nack_cause);
}
int oml_mo_opstart_ack(struct gsm_abis_mo *mo) int oml_mo_opstart_ack(struct gsm_abis_mo *mo)
{ {
return oml_mo_fom_ack_nack(mo, NM_MT_OPSTART, 0); return oml_mo_fom_ack_nack(mo, NM_MT_OPSTART, 0);
@@ -287,10 +316,14 @@ int oml_fom_ack_nack(struct msgb *old_msg, uint8_t cause)
{ {
struct abis_om_hdr *old_oh = msgb_l2(old_msg); struct abis_om_hdr *old_oh = msgb_l2(old_msg);
struct abis_om_fom_hdr *old_foh = msgb_l3(old_msg); struct abis_om_fom_hdr *old_foh = msgb_l3(old_msg);
struct msgb *msg = oml_msgb_alloc(); struct msgb *msg;
struct abis_om_fom_hdr *foh; struct abis_om_fom_hdr *foh;
int is_manuf = 0; int is_manuf = 0;
msg = oml_msgb_alloc();
if (!msg)
return -ENOMEM;
/* make sure to respond with MANUF if request was MANUF */ /* make sure to respond with MANUF if request was MANUF */
if (old_oh->mdisc == ABIS_OM_MDISC_MANUF) if (old_oh->mdisc == ABIS_OM_MDISC_MANUF)
is_manuf = 1; is_manuf = 1;
@@ -325,7 +358,6 @@ int oml_fom_ack_nack(struct msgb *old_msg, uint8_t cause)
int oml_mo_tx_sw_act_rep(struct gsm_abis_mo *mo) int oml_mo_tx_sw_act_rep(struct gsm_abis_mo *mo)
{ {
struct msgb *nmsg; struct msgb *nmsg;
struct abis_om_fom_hdr *nofh;
LOGP(DOML, LOGL_INFO, "%s Tx SW ACT REP\n", gsm_abis_mo_name(mo)); LOGP(DOML, LOGL_INFO, "%s Tx SW ACT REP\n", gsm_abis_mo_name(mo));
@@ -333,8 +365,7 @@ int oml_mo_tx_sw_act_rep(struct gsm_abis_mo *mo)
if (!nmsg) if (!nmsg)
return -ENOMEM; return -ENOMEM;
nofh = (struct abis_om_fom_hdr *) msgb_put(nmsg, sizeof(*nofh)); msgb_put(nmsg, sizeof(struct abis_om_fom_hdr));
return oml_mo_send_msg(mo, nmsg, NM_MT_SW_ACTIVATED_REP); return oml_mo_send_msg(mo, nmsg, NM_MT_SW_ACTIVATED_REP);
} }
@@ -378,8 +409,13 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
/* Test for globally unsupported stuff here */ /* Test for globally unsupported stuff here */
if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) { if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) {
const uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN); const uint16_t *value = (const uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN);
uint16_t arfcn = ntohs(*value); uint16_t arfcn = ntohs(tlvp_val16_unal(&tp, NM_ATT_BCCH_ARFCN));
LOGP(DOML, LOGL_NOTICE, "MSG: %s\n", osmo_hexdump(msgb_l3(msg), msgb_l3len(msg)));
LOGP(DOML, LOGL_NOTICE, "L3=%p, VAL=%p, DIF=%tu\n", msgb_l3(msg), value,
(void *)value - (void *) msgb_l3(msg));
if (arfcn > 1024) { if (arfcn > 1024) {
LOGP(DOML, LOGL_NOTICE, "Given ARFCN %d is not supported.\n", arfcn); LOGP(DOML, LOGL_NOTICE, "Given ARFCN %d is not supported.\n", arfcn);
return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL); return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL);
@@ -398,8 +434,7 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
rc = bts_model_check_oml(bts, foh->msg_type, bts->mo.nm_attr, tp_merged, bts); rc = bts_model_check_oml(bts, foh->msg_type, bts->mo.nm_attr, tp_merged, bts);
if (rc < 0) { if (rc < 0) {
talloc_free(tp_merged); talloc_free(tp_merged);
/* FIXME: send nack? */ return oml_fom_ack_nack(msg, -rc);
return rc;
} }
/* Success: replace old BTS attributes with new */ /* Success: replace old BTS attributes with new */
@@ -414,13 +449,27 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
for (i = 0; i < 6; i++) { for (i = 0; i < 6; i++) {
int16_t boundary = *payload; int16_t boundary = *payload;
btsb->interference.boundary[i] = -1 * boundary; btsb->interference.boundary[i] = -1 * boundary;
}
} }
/* 9.4.24 Intave Parameter */ /* 9.4.24 Intave Parameter */
if (TLVP_PRESENT(&tp, NM_ATT_INTAVE_PARAM)) if (TLVP_PRESENT(&tp, NM_ATT_INTAVE_PARAM))
btsb->interference.intave = *TLVP_VAL(&tp, NM_ATT_INTAVE_PARAM); btsb->interference.intave = *TLVP_VAL(&tp, NM_ATT_INTAVE_PARAM);
/* 9.4.14 Connection Failure Criterion */ /* 9.4.14 Connection Failure Criterion */
/* ... can be 'operator dependent' and needs to be parsed by bts driver */ if (TLVP_PRESENT(&tp, NM_ATT_CONN_FAIL_CRIT)) {
const uint8_t *val = TLVP_VAL(&tp, NM_ATT_CONN_FAIL_CRIT);
if (TLVP_LEN(&tp, NM_ATT_CONN_FAIL_CRIT) < 2
|| val[0] != 0x01 || val[1] < 4 || val[1] > 64) {
LOGP(DOML, LOGL_NOTICE, "Given Conn. Failure Criterion "
"not supported. Please use critetion 0x01 with "
"RADIO_LINK_TIMEOUT value of 4..64\n");
return oml_fom_ack_nack(msg, NM_NACK_PARAM_RANGE);
}
btsb->radio_link_timeout = val[1];
}
/* if val[0] != 0x01: can be 'operator dependent' and needs to
* be parsed by bts driver */
/* 9.4.53 T200 */ /* 9.4.53 T200 */
if (TLVP_PRESENT(&tp, NM_ATT_T200)) { if (TLVP_PRESENT(&tp, NM_ATT_T200)) {
@@ -430,10 +479,8 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
} }
/* 9.4.31 Maximum Timing Advance */ /* 9.4.31 Maximum Timing Advance */
if (TLVP_PRESENT(&tp, NM_ATT_MAX_TA)) { if (TLVP_PRESENT(&tp, NM_ATT_MAX_TA))
uint16_t *fn = (uint16_t *) TLVP_VAL(&tp, NM_ATT_MAX_TA); btsb->max_ta = *TLVP_VAL(&tp, NM_ATT_MAX_TA);
btsb->max_ta = ntohs(*fn);
}
/* 9.4.39 Overload Period */ /* 9.4.39 Overload Period */
if (TLVP_PRESENT(&tp, NM_ATT_OVERL_PERIOD)) if (TLVP_PRESENT(&tp, NM_ATT_OVERL_PERIOD))
@@ -454,30 +501,36 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
} }
/* 9.4.45 RACH Load Averaging Slots */ /* 9.4.45 RACH Load Averaging Slots */
if (TLVP_PRESENT(&tp, NM_ATT_LDAVG_SLOTS)) if (TLVP_PRESENT(&tp, NM_ATT_LDAVG_SLOTS)) {
payload = TLVP_VAL(&tp, NM_ATT_LDAVG_SLOTS); btsb->load.rach.averaging_slots =
btsb->load.rach.averaging_slots = ntohs(*(uint16_t *)payload); ntohs(tlvp_val16_unal(&tp, NM_ATT_LDAVG_SLOTS));
} }
/* 9.4.10 BTS Air Timer */ /* 9.4.10 BTS Air Timer */
if (TLVP_PRESENT(&tp, NM_ATT_BTS_AIR_TIMER)) if (TLVP_PRESENT(&tp, NM_ATT_BTS_AIR_TIMER)) {
btsb->t3105_ms = *TLVP_VAL(&tp, NM_ATT_BTS_AIR_TIMER) * 10; uint8_t t3105 = *TLVP_VAL(&tp, NM_ATT_BTS_AIR_TIMER);
if (t3105 == 0) {
LOGP(DOML, LOGL_NOTICE,
"T3105 must have a value != 0.\n");
return oml_fom_ack_nack(msg, NM_NACK_PARAM_RANGE);
}
btsb->t3105_ms = t3105 * 10;
}
/* 9.4.37 NY1 */ /* 9.4.37 NY1 */
if (TLVP_PRESENT(&tp, NM_ATT_NY1)) if (TLVP_PRESENT(&tp, NM_ATT_NY1))
btsb->ny1 = *TLVP_VAL(&tp, NM_ATT_NY1); btsb->ny1 = *TLVP_VAL(&tp, NM_ATT_NY1);
/* 9.4.8 BCCH ARFCN */ /* 9.4.8 BCCH ARFCN */
if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) { if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN))
const uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN); bts->c0->arfcn = ntohs(tlvp_val16_unal(&tp, NM_ATT_BCCH_ARFCN));
bts->c0->arfcn = ntohs(*value);
}
/* 9.4.9 BSIC */ /* 9.4.9 BSIC */
if (TLVP_PRESENT(&tp, NM_ATT_BSIC)) if (TLVP_PRESENT(&tp, NM_ATT_BSIC))
bts->bsic = *TLVP_VAL(&tp, NM_ATT_BSIC); bts->bsic = *TLVP_VAL(&tp, NM_ATT_BSIC);
/* call into BTS driver to apply new attributes to hardware */ /* call into BTS driver to apply new attributes to hardware */
return bts_model_apply_oml(bts, msg, tp_merged, bts); return bts_model_apply_oml(bts, msg, tp_merged, NM_OC_BTS, bts);
} }
/* 8.6.2 Set Radio Attributes has been received */ /* 8.6.2 Set Radio Attributes has been received */
@@ -502,8 +555,7 @@ static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg)
rc = bts_model_check_oml(trx->bts, foh->msg_type, trx->mo.nm_attr, tp_merged, trx); rc = bts_model_check_oml(trx->bts, foh->msg_type, trx->mo.nm_attr, tp_merged, trx);
if (rc < 0) { if (rc < 0) {
talloc_free(tp_merged); talloc_free(tp_merged);
/* FIXME: send NACK */ return oml_fom_ack_nack(msg, -rc);
return rc;
} }
/* Success: replace old BTS attributes with new */ /* Success: replace old BTS attributes with new */
@@ -514,18 +566,22 @@ static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg)
/* 9.4.47 RF Max Power Reduction */ /* 9.4.47 RF Max Power Reduction */
if (TLVP_PRESENT(&tp, NM_ATT_RF_MAXPOWR_R)) { if (TLVP_PRESENT(&tp, NM_ATT_RF_MAXPOWR_R)) {
trx->max_power_red = *TLVP_VAL(&tp, NM_ATT_RF_MAXPOWR_R); trx->max_power_red = *TLVP_VAL(&tp, NM_ATT_RF_MAXPOWR_R) * 2;
LOGP(DOML, LOGL_INFO, "Set RF Max Power Reduction = %d\n", trx->max_power_red); LOGP(DOML, LOGL_INFO, "Set RF Max Power Reduction = %d dBm\n",
trx->max_power_red);
} }
/* 9.4.5 ARFCN List */ /* 9.4.5 ARFCN List */
#if 0 #if 0
if (TLVP_PRESENT(&tp, NM_ATT_ARFCN_LIST)) { if (TLVP_PRESENT(&tp, NM_ATT_ARFCN_LIST)) {
uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_ARFCN_LIST); 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 length = TLVP_LEN(&tp, NM_ATT_ARFCN_LIST);
uint16_t arfcn; uint16_t arfcn;
int i; int i;
for (i = 0; i < length; i++) { for (i = 0; i < length; i++) {
arfcn = ntohs(*value++); memcpy(&_value, value, 2);
arfcn = ntohs(_value);
value += 2;
if (arfcn > 1024) if (arfcn > 1024)
return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL); return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL);
trx->arfcn_list[i] = arfcn; trx->arfcn_list[i] = arfcn;
@@ -536,7 +592,7 @@ static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg)
trx->arfcn_num = 0; trx->arfcn_num = 0;
#endif #endif
/* call into BTS driver to apply new attributes to hardware */ /* call into BTS driver to apply new attributes to hardware */
return bts_model_apply_oml(trx->bts, msg, tp_merged, trx); return bts_model_apply_oml(trx->bts, msg, tp_merged, NM_OC_RADIO_CARRIER, trx);
} }
static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts) static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts)
@@ -545,10 +601,17 @@ static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts)
unsigned int i; unsigned int i;
switch (ts->pchan) { switch (ts->pchan) {
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
/* fallthrough */
case GSM_PCHAN_CCCH_SDCCH4: case GSM_PCHAN_CCCH_SDCCH4:
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
lchan = &ts->lchan[i]; lchan = &ts->lchan[i];
lchan->type = GSM_LCHAN_SDCCH; if (ts->pchan == GSM_PCHAN_CCCH_SDCCH4_CBCH
&& i == 2) {
lchan->type = GSM_LCHAN_CBCH;
} else {
lchan->type = GSM_LCHAN_SDCCH;
}
} }
/* fallthrough */ /* fallthrough */
case GSM_PCHAN_CCCH: case GSM_PCHAN_CCCH:
@@ -565,12 +628,23 @@ static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts)
lchan->type = GSM_LCHAN_TCH_H; lchan->type = GSM_LCHAN_TCH_H;
} }
break; break;
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
/* fallthrough */
case GSM_PCHAN_SDCCH8_SACCH8C: case GSM_PCHAN_SDCCH8_SACCH8C:
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
lchan = &ts->lchan[i]; lchan = &ts->lchan[i];
lchan->type = GSM_LCHAN_SDCCH; if (ts->pchan == GSM_PCHAN_SDCCH8_SACCH8C_CBCH
&& i == 2) {
lchan->type = GSM_LCHAN_CBCH;
} else {
lchan->type = GSM_LCHAN_SDCCH;
}
} }
break; break;
case GSM_PCHAN_PDCH:
lchan = &ts->lchan[0];
lchan->type = GSM_LCHAN_PDTCH;
break;
default: default:
/* FIXME */ /* FIXME */
break; break;
@@ -607,17 +681,21 @@ static int oml_rx_set_chan_attr(struct gsm_bts_trx_ts *ts, struct msgb *msg)
} }
/* merge existing BTS attributes with new attributes */ /* merge existing BTS attributes with new attributes */
tp_merged = tlvp_copy(bts->mo.nm_attr, bts); tp_merged = tlvp_copy(ts->mo.nm_attr, bts);
tlvp_merge(tp_merged, &tp); tlvp_merge(tp_merged, &tp);
/* Call into BTS driver to check attribute values */ /* Call into BTS driver to check attribute values */
rc = bts_model_check_oml(bts, foh->msg_type, ts->mo.nm_attr, tp_merged, ts); rc = bts_model_check_oml(bts, foh->msg_type, ts->mo.nm_attr, tp_merged, ts);
if (rc < 0) { if (rc < 0) {
talloc_free(&tp_merged); talloc_free(tp_merged);
/* FIXME: Send NACK */ /* Send NACK */
return rc; return oml_fom_ack_nack(msg, -rc);
} }
/* Success: replace old BTS attributes with new */
talloc_free(ts->mo.nm_attr);
ts->mo.nm_attr = tp_merged;
/* 9.4.13 Channel Combination */ /* 9.4.13 Channel Combination */
if (TLVP_PRESENT(&tp, NM_ATT_CHAN_COMB)) { if (TLVP_PRESENT(&tp, NM_ATT_CHAN_COMB)) {
uint8_t comb = *TLVP_VAL(&tp, NM_ATT_CHAN_COMB); uint8_t comb = *TLVP_VAL(&tp, NM_ATT_CHAN_COMB);
@@ -638,7 +716,7 @@ static int oml_rx_set_chan_attr(struct gsm_bts_trx_ts *ts, struct msgb *msg)
gsm_abis_mo_name(&ts->mo), ts->tsc); gsm_abis_mo_name(&ts->mo), ts->tsc);
/* call into BTS driver to apply new attributes to hardware */ /* call into BTS driver to apply new attributes to hardware */
return bts_model_apply_oml(bts, msg, tp_merged, ts); return bts_model_apply_oml(bts, msg, tp_merged, NM_OC_CHANNEL, ts);
} }
/* 8.9.2 Opstart has been received */ /* 8.9.2 Opstart has been received */
@@ -699,11 +777,10 @@ static int oml_rx_chg_adm_state(struct gsm_bts *bts, struct msgb *msg)
return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN); return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN);
/* Step 2: Do some global dependency/consistency checking */ /* Step 2: Do some global dependency/consistency checking */
if (mo->nm_state.administrative == adm_state) { if (mo->nm_state.administrative == adm_state)
DEBUGP(DOML, "... automatic ACK, ADM state already was %s\n", LOGP(DOML, LOGL_NOTICE,
get_value_string(abis_nm_adm_state_names, adm_state)); "ADM state already was %s\n",
return oml_fom_ack_nack(msg, 0); get_value_string(abis_nm_adm_state_names, adm_state));
}
/* Step 3: Ask BTS driver to apply the state chg */ /* Step 3: Ask BTS driver to apply the state chg */
return bts_model_chg_adm_state(bts, mo, obj, adm_state); return bts_model_chg_adm_state(bts, mo, obj, adm_state);
@@ -749,6 +826,9 @@ static int down_fom(struct gsm_bts *bts, struct msgb *msg)
case NM_MT_CHG_ADM_STATE: case NM_MT_CHG_ADM_STATE:
ret = oml_rx_chg_adm_state(bts, msg); ret = oml_rx_chg_adm_state(bts, msg);
break; break;
case NM_MT_IPACC_SET_ATTR:
ret = oml_ipa_set_attr(bts, msg);
break;
default: default:
LOGP(DOML, LOGL_INFO, "unknown Formatted O&M msg_type 0x%02x\n", LOGP(DOML, LOGL_INFO, "unknown Formatted O&M msg_type 0x%02x\n",
foh->msg_type); foh->msg_type);
@@ -762,25 +842,194 @@ static int down_fom(struct gsm_bts *bts, struct msgb *msg)
* manufacturer related messages * manufacturer related messages
*/ */
#ifndef TLVP_PRES_LEN /* old libosmocore */
#define TLVP_PRES_LEN(tp, tag, min_len) \
(TLVP_PRESENT(tp, tag) && TLVP_LEN(tp, tag) >= min_len)
#endif
static int oml_ipa_mo_set_attr_nse(void *obj, struct tlv_parsed *tp)
{
struct gsm_bts *bts = container_of(obj, struct gsm_bts, gprs.nse);
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NSEI, 2))
bts->gprs.nse.nsei =
ntohs(tlvp_val16_unal(tp, NM_ATT_IPACC_NSEI));
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NS_CFG, 7)) {
memcpy(&bts->gprs.nse.timer,
TLVP_VAL(tp, NM_ATT_IPACC_NS_CFG), 7);
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_BSSGP_CFG, 11)) {
memcpy(&bts->gprs.cell.timer,
TLVP_VAL(tp, NM_ATT_IPACC_BSSGP_CFG), 11);
}
osmo_signal_dispatch(SS_GLOBAL, S_NEW_NSE_ATTR, bts);
return 0;
}
static int oml_ipa_mo_set_attr_cell(void *obj, struct tlv_parsed *tp)
{
struct gsm_bts *bts = container_of(obj, struct gsm_bts, gprs.cell);
struct gprs_rlc_cfg *rlcc = &bts->gprs.cell.rlc_cfg;
const uint8_t *cur;
uint16_t _cur_s;
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RAC, 1))
bts->gprs.rac = *TLVP_VAL(tp, NM_ATT_IPACC_RAC);
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_GPRS_PAGING_CFG, 2)) {
cur = TLVP_VAL(tp, NM_ATT_IPACC_GPRS_PAGING_CFG);
rlcc->paging.repeat_time = cur[0] * 50;
rlcc->paging.repeat_count = cur[1];
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_BVCI, 2))
bts->gprs.cell.bvci =
htons(tlvp_val16_unal(tp, NM_ATT_IPACC_BVCI));
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG, 9)) {
cur = TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG);
rlcc->parameter[RLC_T3142] = cur[0];
rlcc->parameter[RLC_T3169] = cur[1];
rlcc->parameter[RLC_T3191] = cur[2];
rlcc->parameter[RLC_T3193] = cur[3];
rlcc->parameter[RLC_T3195] = cur[4];
rlcc->parameter[RLC_N3101] = cur[5];
rlcc->parameter[RLC_N3103] = cur[6];
rlcc->parameter[RLC_N3105] = cur[7];
rlcc->parameter[CV_COUNTDOWN] = cur[8];
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_CODING_SCHEMES, 2)) {
int i;
rlcc->cs_mask = 0;
cur = TLVP_VAL(tp, NM_ATT_IPACC_CODING_SCHEMES);
for (i = 0; i < 4; i++) {
if (cur[0] & (1 << i))
rlcc->cs_mask |= (1 << (GPRS_CS1+i));
}
if (cur[0] & 0x80)
rlcc->cs_mask |= (1 << GPRS_MCS9);
for (i = 0; i < 8; i++) {
if (cur[1] & (1 << i))
rlcc->cs_mask |= (1 << (GPRS_MCS1+i));
}
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG_2, 5)) {
cur = TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG_2);
memcpy(&_cur_s, cur, 2);
rlcc->parameter[T_DL_TBF_EXT] = ntohs(_cur_s) * 10;
cur += 2;
memcpy(&_cur_s, cur, 2);
rlcc->parameter[T_UL_TBF_EXT] = ntohs(_cur_s) * 10;
cur += 2;
rlcc->initial_cs = *cur;
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG_3, 1)) {
rlcc->initial_mcs = *TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG_3);
}
osmo_signal_dispatch(SS_GLOBAL, S_NEW_CELL_ATTR, bts);
return 0;
}
static int oml_ipa_mo_set_attr_nsvc(struct gsm_bts_gprs_nsvc *nsvc,
struct tlv_parsed *tp)
{
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NSVCI, 2))
nsvc->nsvci = ntohs(tlvp_val16_unal(tp, NM_ATT_IPACC_NSVCI));
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NS_LINK_CFG, 8)) {
const uint8_t *cur = TLVP_VAL(tp, NM_ATT_IPACC_NS_LINK_CFG);
uint16_t _cur_s;
uint32_t _cur_l;
memcpy(&_cur_s, cur, 2);
nsvc->remote_port = ntohs(_cur_s);
cur += 2;
memcpy(&_cur_l, cur, 4);
nsvc->remote_ip = ntohl(_cur_l);
cur += 4;
memcpy(&_cur_s, cur, 2);
nsvc->local_port = ntohs(_cur_s);
}
osmo_signal_dispatch(SS_GLOBAL, S_NEW_NSVC_ATTR, nsvc);
return 0;
}
static int oml_ipa_mo_set_attr(struct gsm_bts *bts, struct gsm_abis_mo *mo,
void *obj, struct tlv_parsed *tp)
{
int rc;
switch (mo->obj_class) {
case NM_OC_GPRS_NSE:
rc = oml_ipa_mo_set_attr_nse(obj, tp);
break;
case NM_OC_GPRS_CELL:
rc = oml_ipa_mo_set_attr_cell(obj, tp);
break;
case NM_OC_GPRS_NSVC:
rc = oml_ipa_mo_set_attr_nsvc(obj, tp);
break;
default:
rc = NM_NACK_OBJINST_UNKN;
}
return rc;
}
static int oml_ipa_set_attr(struct gsm_bts *bts, struct msgb *msg)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
struct gsm_abis_mo *mo;
struct tlv_parsed tp;
void *obj;
int rc;
abis_nm_debugp_foh(DOML, foh);
DEBUGPC(DOML, "Rx IPA SET ATTR\n");
rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh));
if (rc < 0)
return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT);
/* Resolve MO by obj_class/obj_inst */
mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst);
if (!mo || !obj)
return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN);
rc = oml_ipa_mo_set_attr(bts, mo, obj, &tp);
return oml_fom_ack_nack(msg, rc);
}
static int rx_oml_ipa_rsl_connect(struct gsm_bts_trx *trx, struct msgb *msg, static int rx_oml_ipa_rsl_connect(struct gsm_bts_trx *trx, struct msgb *msg,
struct tlv_parsed *tp) struct tlv_parsed *tp)
{ {
struct ipabis_link *oml_link = (struct ipabis_link *) trx->bts->oml_link; struct e1inp_sign_link *oml_link = trx->bts->oml_link;
uint16_t port = IPA_TCP_PORT_RSL; uint16_t port = IPA_TCP_PORT_RSL;
uint32_t ip = oml_link->ip; uint32_t ip = get_signlink_remote_ip(oml_link);
struct in_addr in; struct in_addr in;
int rc; int rc;
uint8_t stream_id = 0; uint8_t stream_id = 0;
if (TLVP_PRESENT(tp, NM_ATT_IPACC_DST_IP)) { if (TLVP_PRESENT(tp, NM_ATT_IPACC_DST_IP)) {
const uint8_t *ptr = TLVP_VAL(tp, NM_ATT_IPACC_DST_IP); ip = ntohl(tlvp_val32_unal(tp, NM_ATT_IPACC_DST_IP));
ip = ntohl(*(uint32_t *)ptr);
} }
if (TLVP_PRESENT(tp, NM_ATT_IPACC_DST_IP_PORT)) { if (TLVP_PRESENT(tp, NM_ATT_IPACC_DST_IP_PORT)) {
const uint8_t *ptr = TLVP_VAL(tp, NM_ATT_IPACC_DST_IP_PORT); port = ntohs(tlvp_val16_unal(tp, NM_ATT_IPACC_DST_IP_PORT));
port = ntohs(*(uint16_t *)ptr);
} }
if (TLVP_PRESENT(tp, NM_ATT_IPACC_STREAM_ID)) { if (TLVP_PRESENT(tp, NM_ATT_IPACC_STREAM_ID)) {
stream_id = *TLVP_VAL(tp, NM_ATT_IPACC_STREAM_ID); stream_id = *TLVP_VAL(tp, NM_ATT_IPACC_STREAM_ID);
@@ -790,14 +1039,7 @@ static int rx_oml_ipa_rsl_connect(struct gsm_bts_trx *trx, struct msgb *msg,
LOGP(DOML, LOGL_INFO, "Rx IPA RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n", LOGP(DOML, LOGL_INFO, "Rx IPA RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n",
inet_ntoa(in), port, stream_id); inet_ntoa(in), port, stream_id);
if (!trx->rsl_link) { rc = e1inp_ipa_bts_rsl_connect(oml_link->ts->line, inet_ntoa(in), port);
struct ipabis_link *rsl_link = talloc_zero(trx, struct ipabis_link);
rsl_link->trx = trx;
trx->rsl_link = rsl_link;
}
/* FIXME: we cannot even use a non-standard port here */
rc = abis_open(trx->rsl_link, ip);
if (rc < 0) { if (rc < 0) {
LOGP(DOML, LOGL_ERROR, "Error in abis_open(RSL): %d\n", rc); LOGP(DOML, LOGL_ERROR, "Error in abis_open(RSL): %d\n", rc);
return oml_fom_ack_nack(msg, NM_NACK_CANT_PERFORM); return oml_fom_ack_nack(msg, NM_NACK_CANT_PERFORM);
@@ -820,7 +1062,7 @@ static int down_mom(struct gsm_bts *bts, struct msgb *msg)
return -EIO; return -EIO;
} }
if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) { if (strncmp((char *)&oh->data[1], abis_nm_ipa_magic, idstrlen)) {
LOGP(DOML, LOGL_ERROR, "Manufacturer OML message != ipaccess not supported\n"); LOGP(DOML, LOGL_ERROR, "Manufacturer OML message != ipaccess not supported\n");
return -EINVAL; return -EINVAL;
} }
@@ -847,6 +1089,9 @@ static int down_mom(struct gsm_bts *bts, struct msgb *msg)
trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
ret = rx_oml_ipa_rsl_connect(trx, msg, &tp); ret = rx_oml_ipa_rsl_connect(trx, msg, &tp);
break; break;
case NM_MT_IPACC_SET_ATTR:
ret = oml_ipa_set_attr(bts, msg);
break;
default: default:
LOGP(DOML, LOGL_INFO, "Manufacturer Formatted O&M msg_type 0x%02x\n", LOGP(DOML, LOGL_INFO, "Manufacturer Formatted O&M msg_type 0x%02x\n",
foh->msg_type); foh->msg_type);

View File

@@ -1,6 +1,6 @@
/* Paging message encoding + queue management */ /* Paging message encoding + queue management */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org> /* (C) 2011-2012 by Harald Welte <laforge@gnumonks.org>
* *
* All Rights Reserved * All Rights Reserved
* *
@@ -41,18 +41,37 @@
#include <osmo-bts/logging.h> #include <osmo-bts/logging.h>
#include <osmo-bts/paging.h> #include <osmo-bts/paging.h>
#include <osmo-bts/signal.h> #include <osmo-bts/signal.h>
#include <osmo-bts/pcu_if.h>
#define MAX_PAGING_BLOCKS_CCCH 9 #define MAX_PAGING_BLOCKS_CCCH 9
#define MAX_BS_PA_MFRMS 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
};
struct paging_record { struct paging_record {
struct llist_head list; struct llist_head list;
time_t expiration_time; enum paging_record_type type;
uint8_t chan_needed; union {
uint8_t identity_lv[9]; struct {
time_t expiration_time;
uint8_t chan_needed;
uint8_t identity_lv[9];
} paging;
struct {
uint8_t msg[GSM_MACBLOCK_LEN];
} imm_ass;
} u;
}; };
struct paging_state { struct paging_state {
struct gsm_bts_role_bts *btsb;
/* parameters taken / interpreted from BCCH/CCCH configuration */ /* parameters taken / interpreted from BCCH/CCCH configuration */
struct gsm48_control_channel_descr chan_desc; struct gsm48_control_channel_descr chan_desc;
@@ -65,6 +84,26 @@ struct paging_state {
struct llist_head paging_queue[MAX_PAGING_BLOCKS_CCCH*MAX_BS_PA_MFRMS]; struct llist_head paging_queue[MAX_PAGING_BLOCKS_CCCH*MAX_BS_PA_MFRMS];
}; };
unsigned int paging_get_lifetime(struct paging_state *ps)
{
return ps->paging_lifetime;
}
unsigned int paging_get_queue_max(struct paging_state *ps)
{
return ps->num_paging_max;
}
void paging_set_lifetime(struct paging_state *ps, unsigned int lifetime)
{
ps->paging_lifetime = lifetime;
}
void paging_set_queue_max(struct paging_state *ps, unsigned int queue_max)
{
ps->num_paging_max = queue_max;
}
static int tmsi_mi_to_uint(uint32_t *out, const uint8_t *tmsi_lv) static int tmsi_mi_to_uint(uint32_t *out, const uint8_t *tmsi_lv)
{ {
if (tmsi_lv[0] < 5) if (tmsi_lv[0] < 5)
@@ -128,6 +167,13 @@ static int get_pag_subch_nr(struct paging_state *ps, struct gsm_time *gt)
return pag_idx + mfrm_part; return pag_idx + mfrm_part;
} }
int paging_buffer_space(struct paging_state *ps)
{
if (ps->num_paging >= ps->num_paging_max)
return 0;
else
return ps->num_paging_max - ps->num_paging;
}
/* Add an identity to the paging queue */ /* Add an identity to the paging queue */
int paging_add_identity(struct paging_state *ps, uint8_t paging_group, int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
@@ -144,10 +190,14 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
/* Check if we already have this identity */ /* Check if we already have this identity */
llist_for_each_entry(pr, group_q, list) { llist_for_each_entry(pr, group_q, list) {
if (identity_lv[0] == pr->identity_lv[0] && if (pr->type != PAGING_RECORD_PAGING)
!memcmp(identity_lv+1, pr->identity_lv+1, identity_lv[0])) { continue;
if (identity_lv[0] == pr->u.paging.identity_lv[0] &&
!memcmp(identity_lv+1, pr->u.paging.identity_lv+1,
identity_lv[0])) {
LOGP(DPAG, LOGL_INFO, "Ignoring duplicate paging\n"); LOGP(DPAG, LOGL_INFO, "Ignoring duplicate paging\n");
pr->expiration_time = time(NULL) + ps->paging_lifetime; pr->u.paging.expiration_time =
time(NULL) + ps->paging_lifetime;
return -EEXIST; return -EEXIST;
} }
} }
@@ -155,8 +205,9 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
pr = talloc_zero(ps, struct paging_record); pr = talloc_zero(ps, struct paging_record);
if (!pr) if (!pr)
return -ENOMEM; return -ENOMEM;
pr->type = PAGING_RECORD_PAGING;
if (*identity_lv + 1 > sizeof(pr->identity_lv)) { if (*identity_lv + 1 > sizeof(pr->u.paging.identity_lv)) {
talloc_free(pr); talloc_free(pr);
return -E2BIG; return -E2BIG;
} }
@@ -164,9 +215,9 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
LOGP(DPAG, LOGL_INFO, "Add paging to queue (group=%u, queue_len=%u)\n", LOGP(DPAG, LOGL_INFO, "Add paging to queue (group=%u, queue_len=%u)\n",
paging_group, ps->num_paging+1); paging_group, ps->num_paging+1);
pr->expiration_time = time(NULL) + ps->paging_lifetime; pr->u.paging.expiration_time = time(NULL) + ps->paging_lifetime;
pr->chan_needed = chan_needed; pr->u.paging.chan_needed = chan_needed;
memcpy(&pr->identity_lv, identity_lv, identity_lv[0]+1); memcpy(&pr->u.paging.identity_lv, identity_lv, identity_lv[0]+1);
/* enqueue the new identity to the HEAD of the queue, /* enqueue the new identity to the HEAD of the queue,
* to ensure it will be paged quickly at least once. */ * to ensure it will be paged quickly at least once. */
@@ -176,9 +227,52 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
return 0; return 0;
} }
/* Add an IMM.ASS message to the paging queue */
int paging_add_imm_ass(struct paging_state *ps, const uint8_t *data,
uint8_t len)
{
struct llist_head *group_q;
struct paging_record *pr;
uint16_t imsi, paging_group;
if (len != GSM_MACBLOCK_LEN + 3) {
LOGP(DPAG, LOGL_ERROR, "IMM.ASS invalid length %d\n", len);
return -EINVAL;
}
len -= 3;
imsi = 100 * ((*(data++)) - '0');
imsi += 10 * ((*(data++)) - '0');
imsi += (*(data++)) - '0';
paging_group = gsm0502_calc_paging_group(&ps->chan_desc, imsi);
group_q = &ps->paging_queue[paging_group];
pr = talloc_zero(ps, struct paging_record);
if (!pr)
return -ENOMEM;
pr->type = PAGING_RECORD_IMM_ASS;
LOGP(DPAG, LOGL_INFO, "Add IMM.ASS to queue (group=%u)\n",
paging_group);
memcpy(pr->u.imm_ass.msg, data, GSM_MACBLOCK_LEN);
/* enqueue the new message to the HEAD of the queue */
llist_add(&pr->list, group_q);
return 0;
}
#define L2_PLEN(len) (((len - 1) << 2) | 0x01) #define L2_PLEN(len) (((len - 1) << 2) | 0x01)
static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv, 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,
uint8_t chan1, const uint8_t *identity2_lv, uint8_t chan1, const uint8_t *identity2_lv,
uint8_t chan2) uint8_t chan2)
{ {
@@ -195,7 +289,20 @@ static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv,
cur = lv_put(pt1->data, identity1_lv[0], identity1_lv+1); cur = lv_put(pt1->data, identity1_lv[0], identity1_lv+1);
if (identity2_lv) if (identity2_lv)
cur = lv_put(cur, identity2_lv[0], identity2_lv+1); 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); pt1->l2_plen = L2_PLEN(cur - out_buf);
return cur - out_buf; return cur - out_buf;
@@ -252,8 +359,6 @@ static int fill_paging_type_3(uint8_t *out_buf, const uint8_t *tmsi1_lv,
return cur - out_buf; return cur - out_buf;
} }
static const uint8_t empty_id_lv[] = { 0x01, 0xF0 };
static struct paging_record *dequeue_pr(struct llist_head *group_q) static struct paging_record *dequeue_pr(struct llist_head *group_q)
{ {
struct paging_record *pr; struct paging_record *pr;
@@ -266,7 +371,7 @@ static struct paging_record *dequeue_pr(struct llist_head *group_q)
static int pr_is_imsi(struct paging_record *pr) static int pr_is_imsi(struct paging_record *pr)
{ {
if ((pr->identity_lv[1] & 7) == GSM_MI_TYPE_IMSI) if ((pr->u.paging.identity_lv[1] & 7) == GSM_MI_TYPE_IMSI)
return 1; return 1;
else else
return 0; return 0;
@@ -293,28 +398,52 @@ static void sort_pr_tmsi_imsi(struct paging_record *pr[], unsigned int n)
} }
/* generate paging message for given gsm time */ /* generate paging message for given gsm time */
int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt) int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt,
int *is_empty)
{ {
unsigned int group = get_pag_subch_nr(ps, gt); struct llist_head *group_q;
struct llist_head *group_q = &ps->paging_queue[group]; int group;
int len; int len;
*is_empty = 0;
ps->btsb->load.ccch.pch_total += 1;
group = get_pag_subch_nr(ps, gt);
if (group < 0) {
LOGP(DPAG, LOGL_ERROR,
"Paging called for GSM wrong time: FN %d/%d/%d/%d.\n",
gt->fn, gt->t1, gt->t2, gt->t3);
return -1;
}
group_q = &ps->paging_queue[group];
/* There is nobody to be paged, send Type1 with two empty ID */ /* There is nobody to be paged, send Type1 with two empty ID */
if (llist_empty(group_q)) { if (llist_empty(group_q)) {
//DEBUGP(DPAG, "Tx PAGING TYPE 1 (empty)\n"); //DEBUGP(DPAG, "Tx PAGING TYPE 1 (empty)\n");
len = fill_paging_type_1(out_buf, empty_id_lv, 0, len = fill_paging_type_1(group, out_buf, empty_id_lv, 0,
NULL, 0); NULL, 0);
*is_empty = 1;
} else { } else {
struct paging_record *pr[4]; struct paging_record *pr[4];
unsigned int num_pr = 0; unsigned int num_pr = 0, imm_ass = 0;
time_t now = time(NULL); time_t now = time(NULL);
unsigned int i, num_imsi = 0; unsigned int i, num_imsi = 0;
ps->btsb->load.ccch.pch_used += 1;
/* get (if we have) up to four paging records */ /* get (if we have) up to four paging records */
for (i = 0; i < ARRAY_SIZE(pr); i++) { for (i = 0; i < ARRAY_SIZE(pr); i++) {
if (llist_empty(group_q)) if (llist_empty(group_q))
break; break;
pr[i] = dequeue_pr(group_q); pr[i] = dequeue_pr(group_q);
/* check for IMM.ASS */
if (pr[i]->type == PAGING_RECORD_IMM_ASS) {
imm_ass = 1;
break;
}
num_pr++; num_pr++;
/* count how many IMSIs are among them */ /* count how many IMSIs are among them */
@@ -322,27 +451,43 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
num_imsi++; num_imsi++;
} }
/* if we have an IMMEDIATE ASSIGNMENT */
if (imm_ass) {
/* re-add paging records */
for (i = 0; i < num_pr; i++)
llist_add(&pr[i]->list, group_q);
/* get message and free record */
memcpy(out_buf, pr[num_pr]->u.imm_ass.msg,
GSM_MACBLOCK_LEN);
pcu_tx_pch_data_cnf(gt->fn, pr[num_pr]->u.imm_ass.msg,
GSM_MACBLOCK_LEN);
talloc_free(pr[num_pr]);
return GSM_MACBLOCK_LEN;
}
/* make sure the TMSIs are ahead of the IMSIs in the array */ /* make sure the TMSIs are ahead of the IMSIs in the array */
sort_pr_tmsi_imsi(pr, num_pr); sort_pr_tmsi_imsi(pr, num_pr);
if (num_pr == 4 && num_imsi == 0) { if (num_pr == 4 && num_imsi == 0) {
/* No IMSI: easy case, can use TYPE 3 */ /* No IMSI: easy case, can use TYPE 3 */
DEBUGP(DPAG, "Tx PAGING TYPE 3 (4 TMSI)\n"); DEBUGP(DPAG, "Tx PAGING TYPE 3 (4 TMSI)\n");
len = fill_paging_type_3(out_buf, pr[0]->identity_lv, len = fill_paging_type_3(out_buf,
pr[0]->chan_needed, pr[0]->u.paging.identity_lv,
pr[1]->identity_lv, pr[0]->u.paging.chan_needed,
pr[1]->chan_needed, pr[1]->u.paging.identity_lv,
pr[2]->identity_lv, pr[1]->u.paging.chan_needed,
pr[3]->identity_lv); pr[2]->u.paging.identity_lv,
pr[3]->u.paging.identity_lv);
} else if (num_pr >= 3 && num_imsi <= 1) { } else if (num_pr >= 3 && num_imsi <= 1) {
/* 3 or 4, of which only up to 1 is IMSI */ /* 3 or 4, of which only up to 1 is IMSI */
DEBUGP(DPAG, "Tx PAGING TYPE 2 (2 TMSI,1 xMSI)\n"); DEBUGP(DPAG, "Tx PAGING TYPE 2 (2 TMSI,1 xMSI)\n");
len = fill_paging_type_2(out_buf, len = fill_paging_type_2(out_buf,
pr[0]->identity_lv, pr[0]->u.paging.identity_lv,
pr[0]->chan_needed, pr[0]->u.paging.chan_needed,
pr[1]->identity_lv, pr[1]->u.paging.identity_lv,
pr[1]->chan_needed, pr[1]->u.paging.chan_needed,
pr[2]->identity_lv); pr[2]->u.paging.identity_lv);
if (num_pr == 4) { if (num_pr == 4) {
/* re-add #4 for next time */ /* re-add #4 for next time */
llist_add(&pr[3]->list, group_q); llist_add(&pr[3]->list, group_q);
@@ -350,16 +495,19 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
} }
} else if (num_pr == 1) { } else if (num_pr == 1) {
DEBUGP(DPAG, "Tx PAGING TYPE 1 (1 xMSI,1 empty)\n"); DEBUGP(DPAG, "Tx PAGING TYPE 1 (1 xMSI,1 empty)\n");
len = fill_paging_type_1(out_buf, pr[0]->identity_lv, len = fill_paging_type_1(group, out_buf,
pr[0]->chan_needed, NULL, 0); pr[0]->u.paging.identity_lv,
pr[0]->u.paging.chan_needed,
NULL, 0);
} else { } else {
/* 2 (any type) or /* 2 (any type) or
* 3 or 4, of which only 2 will be sent */ * 3 or 4, of which only 2 will be sent */
DEBUGP(DPAG, "Tx PAGING TYPE 1 (2 xMSI)\n"); DEBUGP(DPAG, "Tx PAGING TYPE 1 (2 xMSI)\n");
len = fill_paging_type_1(out_buf, pr[0]->identity_lv, len = fill_paging_type_1(group, out_buf,
pr[0]->chan_needed, pr[0]->u.paging.identity_lv,
pr[1]->identity_lv, pr[0]->u.paging.chan_needed,
pr[1]->chan_needed); pr[1]->u.paging.identity_lv,
pr[1]->u.paging.chan_needed);
if (num_pr >= 3) { if (num_pr >= 3) {
/* re-add #4 for next time */ /* re-add #4 for next time */
llist_add(&pr[2]->list, group_q); llist_add(&pr[2]->list, group_q);
@@ -378,7 +526,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
continue; continue;
/* check if we can expire the paging record, /* check if we can expire the paging record,
* or if we need to re-queue it */ * or if we need to re-queue it */
if (pr[i]->expiration_time >= now) { if (pr[i]->u.paging.expiration_time <= now) {
talloc_free(pr[i]); talloc_free(pr[i]);
ps->num_paging--; ps->num_paging--;
LOGP(DPAG, LOGL_INFO, "Removed paging record, queue_len=%u\n", LOGP(DPAG, LOGL_INFO, "Removed paging record, queue_len=%u\n",
@@ -395,7 +543,7 @@ int paging_si_update(struct paging_state *ps, struct gsm48_control_channel_descr
{ {
LOGP(DPAG, LOGL_INFO, "Paging SI update\n"); LOGP(DPAG, LOGL_INFO, "Paging SI update\n");
memcpy(&ps->chan_desc, chan_desc, sizeof(chan_desc)); ps->chan_desc = *chan_desc;
/* FIXME: do we need to re-sort the old paging_records? */ /* FIXME: do we need to re-sort the old paging_records? */
@@ -411,6 +559,12 @@ static int paging_signal_cbfn(unsigned int subsys, unsigned int signal, void *hd
struct paging_state *ps = btsb->paging_state; struct paging_state *ps = btsb->paging_state;
struct gsm48_system_information_type_3 *si3 = (void *) bts->si_buf[SYSINFO_TYPE_3]; struct gsm48_system_information_type_3 *si3 = (void *) bts->si_buf[SYSINFO_TYPE_3];
#warning "TODO: Remove this when setting u8NbrOfAgch is implemented properly"
if (si3->control_channel_desc.bs_ag_blks_res != 1)
LOGP(DPAG, LOGL_ERROR,
"Paging: BS_AG_BLKS_RES = %d != 1 not fully supported\n",
si3->control_channel_desc.bs_ag_blks_res);
paging_si_update(ps, &si3->control_channel_desc); paging_si_update(ps, &si3->control_channel_desc);
} }
return 0; return 0;
@@ -418,16 +572,18 @@ static int paging_signal_cbfn(unsigned int subsys, unsigned int signal, void *hd
static int initialized = 0; static int initialized = 0;
struct paging_state *paging_init(void *ctx, unsigned int num_paging_max, struct paging_state *paging_init(struct gsm_bts_role_bts *btsb,
unsigned int num_paging_max,
unsigned int paging_lifetime) unsigned int paging_lifetime)
{ {
struct paging_state *ps; struct paging_state *ps;
unsigned int i; unsigned int i;
ps = talloc_zero(ctx, struct paging_state); ps = talloc_zero(btsb, struct paging_state);
if (!ps) if (!ps)
return NULL; return NULL;
ps->btsb = btsb;
ps->paging_lifetime = paging_lifetime; ps->paging_lifetime = paging_lifetime;
ps->num_paging_max = num_paging_max; ps->num_paging_max = num_paging_max;
@@ -441,6 +597,14 @@ struct paging_state *paging_init(void *ctx, unsigned int num_paging_max,
return ps; return ps;
} }
void paging_config(struct paging_state *ps,
unsigned int num_paging_max,
unsigned int paging_lifetime)
{
ps->num_paging_max = num_paging_max;
ps->paging_lifetime = paging_lifetime;
}
void paging_reset(struct paging_state *ps) void paging_reset(struct paging_state *ps)
{ {
int i; int i;
@@ -460,3 +624,18 @@ void paging_reset(struct paging_state *ps)
ps->num_paging = 0; ps->num_paging = 0;
} }
/**
* \brief Helper for the unit tests
*/
int paging_group_queue_empty(struct paging_state *ps, uint8_t grp)
{
if (grp >= ARRAY_SIZE(ps->paging_queue))
return 1;
return llist_empty(&ps->paging_queue[grp]);
}
int paging_queue_length(struct paging_state *ps)
{
return ps->num_paging;
}

926
src/common/pcu_sock.c Normal file
View File

@@ -0,0 +1,926 @@
/* pcu_sock.c: Connect from PCU via unix domain socket */
/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2009-2012 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2012 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 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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/pcuif_proto.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/signal.h>
#include <osmo-bts/bts_model.h>
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx);
extern struct gsm_network bts_gsmnet;
extern int pcu_direct;
static int avail_lai = 0, avail_nse = 0, avail_cell = 0, avail_nsvc[2] = {0, 0};
static const char *sapi_string[] = {
[PCU_IF_SAPI_RACH] = "RACH",
[PCU_IF_SAPI_AGCH] = "AGCH",
[PCU_IF_SAPI_PCH] = "PCH",
[PCU_IF_SAPI_BCCH] = "BCCH",
[PCU_IF_SAPI_PDTCH] = "PDTCH",
[PCU_IF_SAPI_PRACH] = "PRACH",
[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);
static struct gsm_bts_trx *trx_by_nr(struct gsm_bts *bts, uint8_t trx_nr)
{
struct gsm_bts_trx *trx;
llist_for_each_entry(trx, &bts->trx_list, list) {
if (trx->nr == trx_nr)
return trx;
}
return NULL;
}
/*
* PCU messages
*/
struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx");
if (!msg)
return NULL;
msgb_put(msg, sizeof(struct gsm_pcu_if));
pcu_prim = (struct gsm_pcu_if *) msg->data;
pcu_prim->msg_type = msg_type;
pcu_prim->bts_nr = bts_nr;
return msg;
}
int pcu_tx_info_ind(void)
{
struct gsm_network *net = &bts_gsmnet;
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_info_ind *info_ind;
struct gsm_bts *bts;
struct gprs_rlc_cfg *rlcc;
struct gsm_bts_gprs_nsvc *nsvc;
struct gsm_bts_trx *trx;
struct gsm_bts_trx_ts *ts;
int i, j;
LOGP(DPCU, LOGL_INFO, "Sending info\n");
/* FIXME: allow multiple BTS */
bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
rlcc = &bts->gprs.cell.rlc_cfg;
msg = pcu_msgb_alloc(PCU_IF_MSG_INFO_IND, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
info_ind = &pcu_prim->u.info_ind;
info_ind->version = PCU_IF_VERSION;
if (avail_lai && avail_nse && avail_cell && avail_nsvc[0]) {
info_ind->flags |= PCU_IF_FLAG_ACTIVE;
LOGP(DPCU, LOGL_INFO, "BTS is up\n");
} else
LOGP(DPCU, LOGL_INFO, "BTS is down\n");
if (pcu_direct)
info_ind->flags |= PCU_IF_FLAG_SYSMO;
/* RAI */
info_ind->mcc = net->mcc;
info_ind->mnc = net->mnc;
info_ind->lac = bts->location_area_code;
info_ind->rac = bts->gprs.rac;
/* NSE */
info_ind->nsei = bts->gprs.nse.nsei;
memcpy(info_ind->nse_timer, bts->gprs.nse.timer, 7);
memcpy(info_ind->cell_timer, bts->gprs.cell.timer, 11);
/* cell attributes */
info_ind->cell_id = bts->cell_identity;
info_ind->repeat_time = rlcc->paging.repeat_time;
info_ind->repeat_count = rlcc->paging.repeat_count;
info_ind->bvci = bts->gprs.cell.bvci;
info_ind->t3142 = rlcc->parameter[RLC_T3142];
info_ind->t3169 = rlcc->parameter[RLC_T3169];
info_ind->t3191 = rlcc->parameter[RLC_T3191];
info_ind->t3193_10ms = rlcc->parameter[RLC_T3193];
info_ind->t3195 = rlcc->parameter[RLC_T3195];
info_ind->n3101 = rlcc->parameter[RLC_N3101];
info_ind->n3103 = rlcc->parameter[RLC_N3103];
info_ind->n3105 = rlcc->parameter[RLC_N3105];
info_ind->cv_countdown = rlcc->parameter[CV_COUNTDOWN];
if (rlcc->cs_mask & (1 << GPRS_CS1))
info_ind->flags |= PCU_IF_FLAG_CS1;
if (rlcc->cs_mask & (1 << GPRS_CS2))
info_ind->flags |= PCU_IF_FLAG_CS2;
if (rlcc->cs_mask & (1 << GPRS_CS3))
info_ind->flags |= PCU_IF_FLAG_CS3;
if (rlcc->cs_mask & (1 << GPRS_CS4))
info_ind->flags |= PCU_IF_FLAG_CS4;
if (rlcc->cs_mask & (1 << GPRS_MCS1))
info_ind->flags |= PCU_IF_FLAG_MCS1;
if (rlcc->cs_mask & (1 << GPRS_MCS2))
info_ind->flags |= PCU_IF_FLAG_MCS2;
if (rlcc->cs_mask & (1 << GPRS_MCS3))
info_ind->flags |= PCU_IF_FLAG_MCS3;
if (rlcc->cs_mask & (1 << GPRS_MCS4))
info_ind->flags |= PCU_IF_FLAG_MCS4;
if (rlcc->cs_mask & (1 << GPRS_MCS5))
info_ind->flags |= PCU_IF_FLAG_MCS5;
if (rlcc->cs_mask & (1 << GPRS_MCS6))
info_ind->flags |= PCU_IF_FLAG_MCS6;
if (rlcc->cs_mask & (1 << GPRS_MCS7))
info_ind->flags |= PCU_IF_FLAG_MCS7;
if (rlcc->cs_mask & (1 << GPRS_MCS8))
info_ind->flags |= PCU_IF_FLAG_MCS8;
if (rlcc->cs_mask & (1 << GPRS_MCS9))
info_ind->flags |= PCU_IF_FLAG_MCS9;
#warning "isn't dl_tbf_ext wrong?: * 10 and no ntohs"
info_ind->dl_tbf_ext = rlcc->parameter[T_DL_TBF_EXT];
#warning "isn't ul_tbf_ext wrong?: * 10 and no ntohs"
info_ind->ul_tbf_ext = rlcc->parameter[T_UL_TBF_EXT];
info_ind->initial_cs = rlcc->initial_cs;
info_ind->initial_mcs = rlcc->initial_mcs;
/* NSVC */
for (i = 0; i < 2; i++) {
nsvc = &bts->gprs.nsvc[i];
info_ind->nsvci[i] = nsvc->nsvci;
info_ind->local_port[i] = nsvc->local_port;
info_ind->remote_port[i] = nsvc->remote_port;
info_ind->remote_ip[i] = nsvc->remote_ip;
}
for (i = 0; i < 8; i++) {
trx = trx_by_nr(bts, i);
if (!trx)
break;
info_ind->trx[i].pdch_mask = 0;
info_ind->trx[i].arfcn = trx->arfcn;
info_ind->trx[i].hlayer1 = trx_get_hlayer1(trx);
for (j = 0; j < 8; j++) {
ts = &trx->ts[j];
if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
&& ts->pchan == GSM_PCHAN_PDCH) {
info_ind->trx[i].pdch_mask |= (1 << j);
info_ind->trx[i].tsc[j] =
(ts->tsc >= 0) ? ts->tsc : bts->tsc;
LOGP(DPCU, LOGL_INFO, "trx=%d ts=%d: "
"available (tsc=%d arfcn=%d)\n",
trx->nr, ts->nr,
info_ind->trx[i].tsc[j],
info_ind->trx[i].arfcn);
}
}
}
return pcu_sock_send(net, msg);
}
static int pcu_if_signal_cb(unsigned int subsys, unsigned int signal,
void *hdlr_data, void *signal_data)
{
struct gsm_network *net = &bts_gsmnet;
struct gsm_bts_gprs_nsvc *nsvc;
struct gsm_bts *bts;
struct gsm48_system_information_type_3 *si3;
int id;
if (subsys != SS_GLOBAL)
return -EINVAL;
switch(signal) {
case S_NEW_SYSINFO:
bts = signal_data;
if (!(bts->si_valid & (1 << SYSINFO_TYPE_3)))
break;
si3 = (struct gsm48_system_information_type_3 *)
bts->si_buf[SYSINFO_TYPE_3];
net->mcc = ((si3->lai.digits[0] & 0x0f) << 8)
| (si3->lai.digits[0] & 0xf0)
| (si3->lai.digits[1] & 0x0f);
net->mnc = ((si3->lai.digits[2] & 0x0f) << 8)
| (si3->lai.digits[2] & 0xf0)
| ((si3->lai.digits[1] & 0xf0) >> 4);
if ((net->mnc & 0x00f) == 0x00f)
net->mnc >>= 4;
bts->location_area_code = ntohs(si3->lai.lac);
bts->cell_identity = si3->cell_identity;
avail_lai = 1;
break;
case S_NEW_NSE_ATTR:
bts = signal_data;
avail_nse = 1;
break;
case S_NEW_CELL_ATTR:
bts = signal_data;
avail_cell = 1;
break;
case S_NEW_NSVC_ATTR:
nsvc = signal_data;
id = nsvc->id;
if (id < 0 || id > 1)
return -EINVAL;
avail_nsvc[id] = 1;
break;
case S_NEW_OP_STATE:
break;
default:
return -EINVAL;
}
/* If all infos have been received, of if one info is updated after
* all infos have been received, transmit info update. */
if (avail_lai && avail_nse && avail_cell && avail_nsvc[0])
pcu_tx_info_ind();
return 0;
}
int pcu_tx_rts_req(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_rts_req *rts_req;
struct gsm_bts *bts = ts->trx->bts;
LOGP(DPCU, LOGL_DEBUG, "Sending rts request: is_ptcch=%d arfcn=%d "
"block=%d\n", is_ptcch, arfcn, block_nr);
msg = pcu_msgb_alloc(PCU_IF_MSG_RTS_REQ, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
rts_req = &pcu_prim->u.rts_req;
rts_req->sapi = (is_ptcch) ? PCU_IF_SAPI_PTCCH : PCU_IF_SAPI_PDTCH;
rts_req->fn = fn;
rts_req->arfcn = arfcn;
rts_req->trx_nr = ts->trx->nr;
rts_req->ts_nr = ts->nr;
rts_req->block_nr = block_nr;
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len,
int8_t rssi)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_data *data_ind;
struct gsm_bts *bts = ts->trx->bts;
LOGP(DPCU, LOGL_DEBUG, "Sending data indication: is_ptcch=%d arfcn=%d "
"block=%d data=%s\n", is_ptcch, arfcn, block_nr,
osmo_hexdump(data, len));
msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_IND, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
data_ind = &pcu_prim->u.data_ind;
data_ind->sapi = (is_ptcch) ? PCU_IF_SAPI_PTCCH : PCU_IF_SAPI_PDTCH;
data_ind->fn = fn;
data_ind->arfcn = arfcn;
data_ind->trx_nr = ts->trx->nr;
data_ind->ts_nr = ts->nr;
data_ind->block_nr = block_nr;
data_ind->rssi = rssi;
memcpy(data_ind->data, data, len);
data_ind->len = len;
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint8_t ra, uint32_t fn)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_rach_ind *rach_ind;
LOGP(DPCU, LOGL_INFO, "Sending RACH indication: qta=%d, ra=%d, "
"fn=%d\n", qta, ra, fn);
msg = pcu_msgb_alloc(PCU_IF_MSG_RACH_IND, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
rach_ind = &pcu_prim->u.rach_ind;
rach_ind->sapi = PCU_IF_SAPI_RACH;
rach_ind->ra = ra;
rach_ind->qta = qta;
rach_ind->fn = fn;
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_time_ind(uint32_t fn)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_time_ind *time_ind;
uint8_t fn13 = fn % 13;
/* omit frame numbers not starting at a MAC block */
if (fn13 != 0 && fn13 != 4 && fn13 != 8)
return 0;
msg = pcu_msgb_alloc(PCU_IF_MSG_TIME_IND, 0);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
time_ind = &pcu_prim->u.time_ind;
time_ind->fn = fn;
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_pag_req(const uint8_t *identity_lv, uint8_t chan_needed)
{
struct pcu_sock_state *state = bts_gsmnet.pcu_state;
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_pag_req *pag_req;
/* check if identity does not fit: length > sizeof(lv) - 1 */
if (identity_lv[0] >= sizeof(pag_req->identity_lv)) {
LOGP(DPCU, LOGL_ERROR, "Paging identity too large (%d)\n",
identity_lv[0]);
return -EINVAL;
}
/* socket not created */
if (!state) {
LOGP(DPCU, LOGL_DEBUG, "PCU socket not created, ignoring "
"paging message\n");
return 0;
}
msg = pcu_msgb_alloc(PCU_IF_MSG_PAG_REQ, 0);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
pag_req = &pcu_prim->u.pag_req;
pag_req->chan_needed = chan_needed;
memcpy(pag_req->identity_lv, identity_lv, identity_lv[0] + 1);
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_pch_data_cnf(uint32_t fn, uint8_t *data, uint8_t len)
{
struct gsm_network *net = &bts_gsmnet;
struct gsm_bts *bts;
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_data *data_cnf;
/* FIXME: allow multiple BTS */
bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
LOGP(DPCU, LOGL_INFO, "Sending PCH confirm\n");
msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_CNF, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
data_cnf = &pcu_prim->u.data_cnf;
data_cnf->sapi = PCU_IF_SAPI_PCH;
data_cnf->fn = fn;
memcpy(data_cnf->data, data, len);
data_cnf->len = len;
return pcu_sock_send(&bts_gsmnet, msg);
}
static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
struct gsm_pcu_if_data *data_req)
{
uint8_t is_ptcch;
struct gsm_bts_trx *trx;
struct gsm_bts_trx_ts *ts;
struct msgb *msg;
int rc = 0;
LOGP(DPCU, LOGL_DEBUG, "Data request received: sapi=%s arfcn=%d "
"block=%d data=%s\n", sapi_string[data_req->sapi],
data_req->arfcn, data_req->block_nr,
osmo_hexdump(data_req->data, data_req->len));
switch (data_req->sapi) {
case PCU_IF_SAPI_BCCH:
if (data_req->len == 23) {
bts->si_valid |= (1 << SYSINFO_TYPE_13);
memcpy(bts->si_buf[SYSINFO_TYPE_13], data_req->data,
data_req->len);
} else {
bts->si_valid &= ~(1 << SYSINFO_TYPE_13);
}
osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts);
break;
case PCU_IF_SAPI_PCH:
if (msg_type == PCU_IF_MSG_PAG_REQ) {
/* FIXME: Add function to schedule paging request.
* This might not be required, if PCU_IF_MSG_DATA_REQ
* is used instead. */
} else {
struct gsm_bts_role_bts *btsb = bts->role;
paging_add_imm_ass(btsb->paging_state, data_req->data,
data_req->len);
}
break;
case PCU_IF_SAPI_AGCH:
msg = msgb_alloc(data_req->len, "pcu_agch");
if (!msg) {
rc = -ENOMEM;
break;
}
msg->l3h = msgb_put(msg, data_req->len);
memcpy(msg->l3h, data_req->data, data_req->len);
if (bts_agch_enqueue(bts, msg) < 0) {
msgb_free(msg);
rc = -EIO;
}
break;
case PCU_IF_SAPI_PDTCH:
case PCU_IF_SAPI_PTCCH:
trx = trx_by_nr(bts, data_req->trx_nr);
if (!trx) {
LOGP(DPCU, LOGL_ERROR, "Received PCU data request with "
"not existing TRX %d\n", data_req->trx_nr);
rc = -EINVAL;
break;
}
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,
data_req->block_nr, data_req->data, data_req->len);
break;
default:
LOGP(DPCU, LOGL_ERROR, "Received PCU data request with "
"unsupported sapi %d\n", data_req->sapi);
rc = -EINVAL;
}
return rc;
}
static int pcu_rx_act_req(struct gsm_bts *bts,
struct gsm_pcu_if_act_req *act_req)
{
struct gsm_bts_trx *trx;
struct gsm_lchan *lchan;
LOGP(DPCU, LOGL_INFO, "%s request received: TRX=%d TX=%d\n",
(act_req->activate) ? "Activate" : "Deactivate",
act_req->trx_nr, act_req->ts_nr);
trx = trx_by_nr(bts, act_req->trx_nr);
if (!trx || act_req->ts_nr >= 8)
return -EINVAL;
lchan = trx->ts[act_req->ts_nr].lchan;
lchan->rel_act_kind = LCHAN_REL_ACT_PCU;
if (lchan->type != GSM_LCHAN_PDTCH) {
LOGP(DPCU, LOGL_ERROR, "Lchan is not of type PDCH, but %d.\n",
lchan->type);
return -EINVAL;
}
if (act_req->activate)
bts_model_rsl_chan_act(lchan, NULL);
else
bts_model_rsl_chan_rel(lchan);
return 0;
}
static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
struct gsm_pcu_if *pcu_prim)
{
int rc = 0;
struct gsm_bts *bts;
/* FIXME: allow multiple BTS */
bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
switch (msg_type) {
case PCU_IF_MSG_DATA_REQ:
case PCU_IF_MSG_PAG_REQ:
rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req);
break;
case PCU_IF_MSG_ACT_REQ:
rc = pcu_rx_act_req(bts, &pcu_prim->u.act_req);
break;
default:
LOGP(DPCU, LOGL_ERROR, "Received unknwon PCU msg type %d\n",
msg_type);
rc = -EINVAL;
}
return rc;
}
/*
* PCU socket interface
*/
struct pcu_sock_state {
struct gsm_network *net;
struct osmo_fd listen_bfd; /* fd for listen socket */
struct osmo_fd conn_bfd; /* fd for connection to lcr */
struct llist_head upqueue; /* queue for sending messages */
};
static int pcu_sock_send(struct gsm_network *net, struct msgb *msg)
{
struct pcu_sock_state *state = net->pcu_state;
struct osmo_fd *conn_bfd;
struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msg->data;
if (!state) {
if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND)
LOGP(DPCU, LOGL_INFO, "PCU socket not created, "
"dropping message\n");
msgb_free(msg);
return -EINVAL;
}
conn_bfd = &state->conn_bfd;
if (conn_bfd->fd <= 0) {
if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND)
LOGP(DPCU, LOGL_NOTICE, "PCU socket not connected, "
"dropping message\n");
msgb_free(msg);
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
conn_bfd->when |= BSC_FD_WRITE;
return 0;
}
static void pcu_sock_close(struct pcu_sock_state *state)
{
struct osmo_fd *bfd = &state->conn_bfd;
struct gsm_bts *bts;
struct gsm_bts_trx *trx;
struct gsm_bts_trx_ts *ts;
int i, j;
/* FIXME: allow multiple BTS */
bts = llist_entry(state->net->bts_list.next, struct gsm_bts, list);
LOGP(DPCU, LOGL_NOTICE, "PCU socket has LOST connection\n");
close(bfd->fd);
bfd->fd = -1;
osmo_fd_unregister(bfd);
/* re-enable the generation of ACCEPT for new connections */
state->listen_bfd.when |= BSC_FD_READ;
#if 0
/* remove si13, ... */
bts->si_valid &= ~(1 << SYSINFO_TYPE_13);
osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts);
#endif
/* release PDCH */
for (i = 0; i < 8; i++) {
trx = trx_by_nr(bts, i);
if (!trx)
break;
for (j = 0; j < 8; j++) {
ts = &trx->ts[j];
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);
}
}
}
/* flush the queue */
while (!llist_empty(&state->upqueue)) {
struct msgb *msg = msgb_dequeue(&state->upqueue);
msgb_free(msg);
}
}
static int pcu_sock_read(struct osmo_fd *bfd)
{
struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
struct gsm_pcu_if *pcu_prim;
struct msgb *msg;
int rc;
msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->tail;
rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
if (rc == 0)
goto close;
if (rc < 0) {
if (errno == EAGAIN)
return 0;
goto close;
}
rc = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim);
/* as we always synchronously process the message in pcu_rx() and
* its callbacks, we can free the message here. */
msgb_free(msg);
return rc;
close:
msgb_free(msg);
pcu_sock_close(state);
return -1;
}
static int pcu_sock_write(struct osmo_fd *bfd)
{
struct pcu_sock_state *state = bfd->data;
int rc;
while (!llist_empty(&state->upqueue)) {
struct msgb *msg, *msg2;
struct gsm_pcu_if *pcu_prim;
/* peek at the beginning of the queue */
msg = llist_entry(state->upqueue.next, struct msgb, list);
pcu_prim = (struct gsm_pcu_if *)msg->data;
bfd->when &= ~BSC_FD_WRITE;
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
LOGP(DPCU, LOGL_ERROR, "message type (%d) with ZERO "
"bytes!\n", pcu_prim->msg_type);
goto dontsend;
}
/* try to send it over the socket */
rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
if (rc == 0)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
bfd->when |= BSC_FD_WRITE;
break;
}
goto close;
}
dontsend:
/* _after_ we send it, we can deueue */
msg2 = msgb_dequeue(&state->upqueue);
assert(msg == msg2);
msgb_free(msg);
}
return 0;
close:
pcu_sock_close(state);
return -1;
}
static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
{
int rc = 0;
if (flags & BSC_FD_READ)
rc = pcu_sock_read(bfd);
if (rc < 0)
return rc;
if (flags & BSC_FD_WRITE)
rc = pcu_sock_write(bfd);
return rc;
}
/* accept connection comming from PCU */
static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags)
{
struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
struct osmo_fd *conn_bfd = &state->conn_bfd;
struct sockaddr_un un_addr;
socklen_t len;
int rc;
len = sizeof(un_addr);
rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len);
if (rc < 0) {
LOGP(DPCU, LOGL_ERROR, "Failed to accept a new connection\n");
return -1;
}
if (conn_bfd->fd >= 0) {
LOGP(DPCU, LOGL_NOTICE, "PCU connects but we already have "
"another active connection ?!?\n");
/* We already have one PCU connected, this is all we support */
state->listen_bfd.when &= ~BSC_FD_READ;
close(rc);
return 0;
}
conn_bfd->fd = rc;
conn_bfd->when = BSC_FD_READ;
conn_bfd->cb = pcu_sock_cb;
conn_bfd->data = state;
if (osmo_fd_register(conn_bfd) != 0) {
LOGP(DPCU, LOGL_ERROR, "Failed to register new connection "
"fd\n");
close(conn_bfd->fd);
conn_bfd->fd = -1;
return -1;
}
LOGP(DPCU, LOGL_NOTICE, "PCU socket connected to external PCU\n");
/* send current info */
pcu_tx_info_ind();
return 0;
}
int pcu_sock_init(void)
{
struct pcu_sock_state *state;
struct osmo_fd *bfd;
int rc;
state = talloc_zero(NULL, struct pcu_sock_state);
if (!state)
return -ENOMEM;
INIT_LLIST_HEAD(&state->upqueue);
state->net = &bts_gsmnet;
state->conn_bfd.fd = -1;
bfd = &state->listen_bfd;
rc = osmo_unixsock_listen(bfd, SOCK_SEQPACKET, "/tmp/pcu_bts");
if (rc < 0) {
LOGP(DPCU, LOGL_ERROR, "Could not create unix socket: %s\n",
strerror(errno));
talloc_free(state);
return rc;
}
bfd->when = BSC_FD_READ;
bfd->cb = pcu_sock_accept;
bfd->data = state;
rc = osmo_fd_register(bfd);
if (rc < 0) {
LOGP(DPCU, LOGL_ERROR, "Could not register listen fd: %d\n",
rc);
close(bfd->fd);
talloc_free(state);
return rc;
}
osmo_signal_register_handler(SS_GLOBAL, pcu_if_signal_cb, NULL);
bts_gsmnet.pcu_state = state;
return 0;
}
void pcu_sock_exit(void)
{
struct pcu_sock_state *state = bts_gsmnet.pcu_state;
struct osmo_fd *bfd, *conn_bfd;
if (!state)
return;
osmo_signal_unregister_handler(SS_GLOBAL, pcu_if_signal_cb, NULL);
conn_bfd = &state->conn_bfd;
if (conn_bfd->fd > 0)
pcu_sock_close(state);
bfd = &state->listen_bfd;
close(bfd->fd);
osmo_fd_unregister(bfd);
talloc_free(state);
bts_gsmnet.pcu_state = NULL;
}
/* FIXME: move this to libosmocore */
int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path)
{
struct sockaddr_un local;
unsigned int namelen;
int rc;
bfd->fd = socket(AF_UNIX, type, 0);
if (bfd->fd < 0) {
fprintf(stderr, "Failed to create Unix Domain Socket.\n");
return -1;
}
local.sun_family = AF_UNIX;
strncpy(local.sun_path, path, sizeof(local.sun_path));
local.sun_path[sizeof(local.sun_path) - 1] = '\0';
unlink(local.sun_path);
/* we use the same magic that X11 uses in Xtranssock.c for
* calculating the proper length of the sockaddr */
#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
local.sun_len = strlen(local.sun_path);
#endif
#if defined(BSD44SOCKETS) || defined(SUN_LEN)
namelen = SUN_LEN(&local);
#else
namelen = strlen(local.sun_path) +
offsetof(struct sockaddr_un, sun_path);
#endif
rc = bind(bfd->fd, (struct sockaddr *) &local, namelen);
if (rc != 0) {
fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n",
local.sun_path);
close(bfd->fd);
bfd->fd = -1;
return -1;
}
if (listen(bfd->fd, 0) != 0) {
fprintf(stderr, "Failed to listen.\n");
close(bfd->fd);
bfd->fd = -1;
return -1;
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -70,8 +70,12 @@ char *bts_support_comb_name(uint8_t chan_comb)
return("BCCH"); return("BCCH");
if (chan_comb == NM_CHANC_BCCHComb) if (chan_comb == NM_CHANC_BCCHComb)
return("BCCH+SDCCH/4"); return("BCCH+SDCCH/4");
if (chan_comb == NM_CHANC_BCCH_CBCH)
return("BCCH+CBCH+SDCCH/4");
if (chan_comb == NM_CHANC_SDCCH) if (chan_comb == NM_CHANC_SDCCH)
return("SDCCH/8"); return("SDCCH/8");
if (chan_comb == NM_CHANC_SDCCH_CBCH)
return("SDCCH/8+CBCH");
if (chan_comb == NM_CHANC_TCHFull) if (chan_comb == NM_CHANC_TCHFull)
return("TCH/F"); return("TCH/F");
if (chan_comb == NM_CHANC_TCHHalf) if (chan_comb == NM_CHANC_TCHHalf)

View File

@@ -24,23 +24,103 @@
#include <osmo-bts/gsm_data.h> #include <osmo-bts/gsm_data.h>
#define BTS_HAS_SI(bts, sinum) ((bts)->si_valid & (1 << sinum))
/* Apply the rules from 05.02 6.3.1.3 Mapping of BCCH Data */
uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time) uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time)
{ {
/* Apply the rules from 05.02 6.3.1.3 Mapping of BCCH Data */ struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
unsigned int tc4_cnt = 0;
unsigned int tc4_sub[4];
/* System information type 2 bis or 2 ter messages are sent if
* needed, as determined by the system operator. If only one of
* them is needed, it is sent when TC = 5. If both are needed,
* 2bis is sent when TC = 5 and 2ter is sent at least once
* within any of 4 consecutive occurrences of TC = 4. */
/* System information type 2 quater is sent if needed, as
* determined by the system operator. If sent on BCCH Norm, it
* shall be sent when TC = 5 if neither of 2bis and 2ter are
* used, otherwise it shall be sent at least once within any of
* 4 consecutive occurrences of TC = 4. If sent on BCCH Ext, it
* is sent at least once within any of 4 consecutive occurrences
* of TC = 5. */
/* System Information type 9 is sent in those blocks with
* TC = 4 which are specified in system information type 3 as
* defined in 3GPP TS 04.08. */
/* System Information Type 13 need only be sent if GPRS support
* is indicated in one or more of System Information Type 3 or 4
* or 7 or 8 messages. These messages also indicate if the
* message is sent on the BCCH Norm or if the message is
* transmitted on the BCCH Ext. In the case that the message is
* sent on the BCCH Norm, it is sent at least once within any of
* 4 consecutive occurrences of TC = 4. */
/* We only implement BCCH Norm at this time */
switch (g_time->tc) { switch (g_time->tc) {
case 0: case 0:
/* System Information Type 1 need only be sent if
* frequency hopping is in use or when the NCH is
* present in a cell. If the MS finds another message
* when TC = 0, it can assume that System Information
* Type 1 is not in use. */
return GSM_BTS_SI(bts, SYSINFO_TYPE_1); return GSM_BTS_SI(bts, SYSINFO_TYPE_1);
case 1: case 1:
/* A SI 2 message will be sent at least every time TC = 1. */
return GSM_BTS_SI(bts, SYSINFO_TYPE_2); return GSM_BTS_SI(bts, SYSINFO_TYPE_2);
case 2: case 2:
return GSM_BTS_SI(bts, SYSINFO_TYPE_3); return GSM_BTS_SI(bts, SYSINFO_TYPE_3);
case 3: case 3:
return GSM_BTS_SI(bts, SYSINFO_TYPE_4); return GSM_BTS_SI(bts, SYSINFO_TYPE_4);
case 4: case 4:
/* 2ter, 2quater, 9, 13 */ /* iterate over 2ter, 2quater, 9, 13 */
break; /* determine how many SI we need to send on TC=4,
* and which of them we send when */
if (BTS_HAS_SI(bts, SYSINFO_TYPE_2ter)) {
tc4_sub[tc4_cnt] = SYSINFO_TYPE_2ter;
tc4_cnt += 1; /* 2bis */
}
if (BTS_HAS_SI(bts, SYSINFO_TYPE_2quater) &&
(BTS_HAS_SI(bts, SYSINFO_TYPE_2bis) ||
BTS_HAS_SI(bts, SYSINFO_TYPE_2bis))) {
tc4_sub[tc4_cnt] = SYSINFO_TYPE_2quater;
tc4_cnt += 1;
}
if (BTS_HAS_SI(bts, SYSINFO_TYPE_13)) {
tc4_sub[tc4_cnt] = SYSINFO_TYPE_13;
tc4_cnt += 1;
}
if (BTS_HAS_SI(bts, SYSINFO_TYPE_9)) {
/* FIXME: check SI3 scheduling info! */
tc4_sub[tc4_cnt] = SYSINFO_TYPE_9;
tc4_cnt += 1;
}
/* simply send SI2 if we have nothing else to send */
if (tc4_cnt == 0)
return GSM_BTS_SI(bts, SYSINFO_TYPE_2);
else {
/* increment static counter by one, modulo count */
btsb->si.tc4_ctr = (btsb->si.tc4_ctr + 1) % tc4_cnt;
return GSM_BTS_SI(bts, tc4_sub[btsb->si.tc4_ctr]);
}
case 5: case 5:
/* 2ter, 2quater */ /* 2bis, 2ter, 2quater */
if (BTS_HAS_SI(bts, SYSINFO_TYPE_2bis) &&
!BTS_HAS_SI(bts, SYSINFO_TYPE_2ter))
return GSM_BTS_SI(bts, SYSINFO_TYPE_2bis);
else if (BTS_HAS_SI(bts, SYSINFO_TYPE_2ter) &&
!BTS_HAS_SI(bts, SYSINFO_TYPE_2bis))
return GSM_BTS_SI(bts, SYSINFO_TYPE_2ter);
else if (BTS_HAS_SI(bts, SYSINFO_TYPE_2bis) &&
BTS_HAS_SI(bts, SYSINFO_TYPE_2ter))
return GSM_BTS_SI(bts, SYSINFO_TYPE_2bis);
else if (BTS_HAS_SI(bts, SYSINFO_TYPE_2quater) &&
!BTS_HAS_SI(bts, SYSINFO_TYPE_2bis) &&
!BTS_HAS_SI(bts, SYSINFO_TYPE_2ter))
return GSM_BTS_SI(bts, SYSINFO_TYPE_2quater);
break; break;
case 6: case 6:
return GSM_BTS_SI(bts, SYSINFO_TYPE_3); return GSM_BTS_SI(bts, SYSINFO_TYPE_3);
@@ -51,11 +131,11 @@ uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time)
return NULL; return NULL;
} }
uint8_t *lchan_sacch_get(struct gsm_lchan *lchan, struct gsm_time *g_time) uint8_t *lchan_sacch_get(struct gsm_lchan *lchan)
{ {
uint32_t tmp; uint32_t tmp;
for (tmp = lchan->si.last + 1; tmp != lchan->si.last; tmp = (tmp + 1) % 32) { for (tmp = lchan->si.last + 1; tmp != lchan->si.last; tmp = (tmp + 1) % _MAX_SYSINFO_TYPE) {
if (lchan->si.valid & (1 << tmp)) { if (lchan->si.valid & (1 << tmp)) {
lchan->si.last = tmp; lchan->si.last = tmp;
return lchan->si.buf[tmp]; return lchan->si.buf[tmp];

291
src/common/tx_power.c Normal file
View File

@@ -0,0 +1,291 @@
/* Transmit Power computation */
/* (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 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 <limits.h>
#include <errno.h>
#include <osmocom/core/utils.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/tx_power.h>
static int get_pa_drive_level_mdBm(const struct power_amp *pa,
int desired_p_out_mdBm, unsigned int arfcn)
{
if (arfcn >= ARRAY_SIZE(pa->calib.gain_mdB))
return INT_MIN;
/* FIXME: temperature compensation */
return desired_p_out_mdBm - pa->calib.gain_mdB[arfcn];
}
/* maximum output power of the system */
int get_p_max_out_mdBm(struct gsm_bts_trx *trx)
{
struct trx_power_params *tpp = &trx->power_params;
/* Add user gain, internal and external PA gain to TRX output power */
return tpp->trx_p_max_out_mdBm + tpp->user_gain_mdB +
tpp->pa.nominal_gain_mdB + tpp->user_pa.nominal_gain_mdB;
}
/* nominal output power, i.e. OML-reduced maximum output power */
int get_p_nominal_mdBm(struct gsm_bts_trx *trx)
{
/* P_max_out subtracted by OML maximum power reduction IE */
return get_p_max_out_mdBm(trx) - to_mdB(trx->max_power_red);
}
/* calculate the target total output power required, reduced by both
* OML and RSL, but ignoring the attenutation required for power ramping and
* thermal management */
int get_p_target_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie)
{
/* Pn subtracted by RSL BS Power IE (in 2 dB steps) */
return get_p_nominal_mdBm(trx) - to_mdB(bs_power_ie * 2);
}
int get_p_target_mdBm_lchan(struct gsm_lchan *lchan)
{
return get_p_target_mdBm(lchan->ts->trx, lchan->bs_power);
}
/* calculate the actual total output power required, taking into account the
* attenutation required for power ramping but not thermal management */
int get_p_actual_mdBm(struct gsm_bts_trx *trx, int p_target_mdBm)
{
struct trx_power_params *tpp = &trx->power_params;
/* P_target subtracted by ramp attenuation */
return p_target_mdBm - tpp->ramp.attenuation_mdB;
}
/* calculate the effective total output power required, taking into account the
* attenutation required for power ramping and thermal management */
int get_p_eff_mdBm(struct gsm_bts_trx *trx, int p_target_mdBm)
{
struct trx_power_params *tpp = &trx->power_params;
/* P_target subtracted by ramp attenuation */
return p_target_mdBm - tpp->ramp.attenuation_mdB - tpp->thermal_attenuation_mdB;
}
/* calculate effect TRX output power required, taking into account the
* attenuations required for power ramping and thermal management */
int get_p_trxout_eff_mdBm(struct gsm_bts_trx *trx, int p_target_mdBm)
{
struct trx_power_params *tpp = &trx->power_params;
int p_actual_mdBm, user_pa_drvlvl_mdBm, pa_drvlvl_mdBm;
unsigned int arfcn = trx->arfcn;
/* P_actual subtracted by any bulk gain added by the user */
p_actual_mdBm = get_p_eff_mdBm(trx, p_target_mdBm) - tpp->user_gain_mdB;
/* determine input drive level required at input to user PA */
user_pa_drvlvl_mdBm = get_pa_drive_level_mdBm(&tpp->user_pa, p_actual_mdBm, arfcn);
/* determine input drive level required at input to internal PA */
pa_drvlvl_mdBm = get_pa_drive_level_mdBm(&tpp->pa, user_pa_drvlvl_mdBm, arfcn);
/* internal PA input drive level is TRX output power */
return pa_drvlvl_mdBm;
}
/* calculate target TRX output power required, ignoring the
* attenuations required for power ramping but not thermal management */
int get_p_trxout_target_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie)
{
struct trx_power_params *tpp = &trx->power_params;
int p_target_mdBm, user_pa_drvlvl_mdBm, pa_drvlvl_mdBm;
unsigned int arfcn = trx->arfcn;
/* P_target subtracted by any bulk gain added by the user */
p_target_mdBm = get_p_target_mdBm(trx, bs_power_ie) - tpp->user_gain_mdB;
/* determine input drive level required at input to user PA */
user_pa_drvlvl_mdBm = get_pa_drive_level_mdBm(&tpp->user_pa, p_target_mdBm, arfcn);
/* determine input drive level required at input to internal PA */
pa_drvlvl_mdBm = get_pa_drive_level_mdBm(&tpp->pa, user_pa_drvlvl_mdBm, arfcn);
/* internal PA input drive level is TRX output power */
return pa_drvlvl_mdBm;
}
int get_p_trxout_target_mdBm_lchan(struct gsm_lchan *lchan)
{
return get_p_trxout_target_mdBm(lchan->ts->trx, lchan->bs_power);
}
/* output power ramping code */
/* The idea here is to avoid a hard switch from 0 to 100, but to actually
* slowly and gradually ramp up or down the power. This is needed on the
* one hand side to avoid very fast dynamic load changes towards the PA power
* supply, but is also needed in order to avoid a DoS by too many subscriber
* attempting to register at the same time. Rather, grow the cell slowly in
* radius than start with the full raduis at once. */
static int we_are_ramping_up(struct gsm_bts_trx *trx)
{
struct trx_power_params *tpp = &trx->power_params;
if (tpp->p_total_tgt_mdBm > tpp->p_total_cur_mdBm)
return 1;
else
return 0;
}
static void power_ramp_do_step(struct gsm_bts_trx *trx, int first);
/* timer call-back for the ramp tumer */
static void power_ramp_timer_cb(void *_trx)
{
struct gsm_bts_trx *trx = _trx;
struct trx_power_params *tpp = &trx->power_params;
int p_trxout_eff_mdBm;
/* compute new actual total output power (= minus ramp attenuation) */
tpp->p_total_cur_mdBm = get_p_actual_mdBm(trx, tpp->p_total_tgt_mdBm);
/* compute new effective (= minus ramp and thermal attenuation) TRX output required */
p_trxout_eff_mdBm = get_p_trxout_eff_mdBm(trx, tpp->p_total_tgt_mdBm);
LOGP(DL1C, LOGL_DEBUG, "ramp_timer_cb(cur_pout=%d, tgt_pout=%d, "
"ramp_att=%d, therm_att=%d, user_gain=%d)\n",
tpp->p_total_cur_mdBm, tpp->p_total_tgt_mdBm,
tpp->ramp.attenuation_mdB, tpp->thermal_attenuation_mdB,
tpp->user_gain_mdB);
LOGP(DL1C, LOGL_INFO,
"ramping TRX board output power to %d mdBm.\n", p_trxout_eff_mdBm);
/* Instruct L1 to apply new effective TRX output power required */
bts_model_change_power(trx, p_trxout_eff_mdBm);
}
/* BTS model call-back once one a call to bts_model_change_power()
* completes, indicating actual L1 transmit power */
void power_trx_change_compl(struct gsm_bts_trx *trx, int p_trxout_cur_mdBm)
{
struct trx_power_params *tpp = &trx->power_params;
int p_trxout_should_mdBm;
p_trxout_should_mdBm = get_p_trxout_eff_mdBm(trx, tpp->p_total_tgt_mdBm);
/* for now we simply write an error message, but in the future
* we might use the value (again) as part of our math? */
if (p_trxout_cur_mdBm != p_trxout_should_mdBm) {
LOGP(DL1C, LOGL_ERROR, "bts_model notifies us of %u mdBm TRX "
"output power. However, it should be %u mdBm!\n",
p_trxout_cur_mdBm, p_trxout_should_mdBm);
}
/* and do another step... */
power_ramp_do_step(trx, 0);
}
static void power_ramp_do_step(struct gsm_bts_trx *trx, int first)
{
struct trx_power_params *tpp = &trx->power_params;
/* we had finished in last loop iteration */
if (!first && tpp->ramp.attenuation_mdB == 0)
return;
if (we_are_ramping_up(trx)) {
/* ramp up power -> ramp down attenuation */
tpp->ramp.attenuation_mdB -= tpp->ramp.step_size_mdB;
if (tpp->ramp.attenuation_mdB <= 0) {
/* we are done */
tpp->ramp.attenuation_mdB = 0;
}
} else {
/* ramp down power -> ramp up attenuation */
tpp->ramp.attenuation_mdB += tpp->ramp.step_size_mdB;
if (tpp->ramp.attenuation_mdB >= 0) {
/* we are done */
tpp->ramp.attenuation_mdB = 0;
}
}
/* schedule timer for the next step */
tpp->ramp.step_timer.data = trx;
tpp->ramp.step_timer.cb = power_ramp_timer_cb;
osmo_timer_schedule(&tpp->ramp.step_timer, tpp->ramp.step_interval_sec, 0);
}
int power_ramp_start(struct gsm_bts_trx *trx, int p_total_tgt_mdBm, int bypass)
{
struct trx_power_params *tpp = &trx->power_params;
/* The input to this function is the actual desired output power, i.e.
* the maximum total system power subtracted by OML as well as RSL
* reductions */
LOGP(DL1C, LOGL_INFO, "power_ramp_start(cur=%d, tgt=%d)\n",
tpp->p_total_cur_mdBm, p_total_tgt_mdBm);
if (!bypass && (p_total_tgt_mdBm > get_p_nominal_mdBm(trx))) {
LOGP(DL1C, LOGL_ERROR, "Asked to ramp power up to "
"%d mdBm, which exceeds P_max_out (%d)\n",
p_total_tgt_mdBm, get_p_nominal_mdBm(trx));
return -ERANGE;
}
/* Cancel any pending request */
osmo_timer_del(&tpp->ramp.step_timer);
/* set the new target */
tpp->p_total_tgt_mdBm = p_total_tgt_mdBm;
if (we_are_ramping_up(trx)) {
if (tpp->p_total_tgt_mdBm <= tpp->ramp.max_initial_pout_mdBm) {
LOGP(DL1C, LOGL_INFO,
"target_power(%d) is below max.initial power\n",
tpp->p_total_tgt_mdBm);
/* new setting is below the maximum initial output
* power, so we can directly jump to this level */
tpp->p_total_cur_mdBm = tpp->p_total_tgt_mdBm;
tpp->ramp.attenuation_mdB = 0;
power_ramp_timer_cb(trx);
} else {
/* We need to step it up. Start from the current value */
/* Set attenuation to cause no power change right now */
tpp->ramp.attenuation_mdB = tpp->p_total_tgt_mdBm - tpp->p_total_cur_mdBm;
/* start with the firsrt step */
power_ramp_do_step(trx, 1);
}
} else {
/* Set ramp attenuation to negative value, and increase that by
* steps until it reaches 0 */
tpp->ramp.attenuation_mdB = tpp->p_total_tgt_mdBm - tpp->p_total_cur_mdBm;
/* start with the firsrt step */
power_ramp_do_step(trx, 1);
}
return 0;
}

View File

@@ -1,6 +1,6 @@
/* OsmoBTS VTY interface */ /* OsmoBTS VTY interface */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org> /* (C) 2011-2014 by Harald Welte <laforge@gnumonks.org>
* *
* All Rights Reserved * All Rights Reserved
* *
@@ -19,6 +19,8 @@
* *
*/ */
#include "btsconfig.h"
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
@@ -29,6 +31,8 @@
#include <osmocom/vty/command.h> #include <osmocom/vty/command.h>
#include <osmocom/vty/logging.h> #include <osmocom/vty/logging.h>
#include <osmocom/trau/osmo_ortp.h>
#include <osmo-bts/logging.h> #include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h> #include <osmo-bts/gsm_data.h>
@@ -40,11 +44,19 @@
#include <osmo-bts/bts_model.h> #include <osmo-bts/bts_model.h>
#include <osmo-bts/measurement.h> #include <osmo-bts/measurement.h>
#include <osmo-bts/vty.h> #include <osmo-bts/vty.h>
#include <osmo-bts/paging.h>
enum node_type bts_vty_go_parent(struct vty *vty) enum node_type bts_vty_go_parent(struct vty *vty)
{ {
switch (vty->node) { switch (vty->node) {
case TRX_NODE:
vty->node = BTS_NODE;
{
struct gsm_bts_trx *trx = vty->index;
vty->index = trx->bts;
}
break;
default: default:
vty->node = CONFIG_NODE; vty->node = CONFIG_NODE;
} }
@@ -54,6 +66,7 @@ enum node_type bts_vty_go_parent(struct vty *vty)
int bts_vty_is_config_node(struct vty *vty, int node) int bts_vty_is_config_node(struct vty *vty, int node)
{ {
switch (node) { switch (node) {
case TRX_NODE:
case BTS_NODE: case BTS_NODE:
return 1; return 1;
default: default:
@@ -65,6 +78,13 @@ gDEFUN(ournode_exit, ournode_exit_cmd, "exit",
"Exit current node, go down to provious node") "Exit current node, go down to provious node")
{ {
switch (vty->node) { switch (vty->node) {
case TRX_NODE:
vty->node = BTS_NODE;
{
struct gsm_bts_trx *trx = vty->index;
vty->index = trx->bts;
}
break;
default: default:
break; break;
} }
@@ -85,19 +105,20 @@ gDEFUN(ournode_end, ournode_end_cmd, "end",
return CMD_SUCCESS; return CMD_SUCCESS;
} }
struct vty_app_info bts_vty_info = { static const char osmobts_copyright[] =
.name = "OsmoBTS",
.version = PACKAGE_VERSION,
.go_parent_cb = bts_vty_go_parent,
.is_config_node = bts_vty_is_config_node,
};
const char *osmobts_copyright =
"Copyright (C) 2010, 2011 by Harald Welte, Andreas Eversberg and On-Waves\r\n" "Copyright (C) 2010, 2011 by Harald Welte, Andreas Eversberg and On-Waves\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n" "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n" "This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n"; "There is NO WARRANTY, to the extent permitted by law.\r\n";
struct vty_app_info bts_vty_info = {
.name = "OsmoBTS",
.version = PACKAGE_VERSION,
.copyright = osmobts_copyright,
.go_parent_cb = bts_vty_go_parent,
.is_config_node = bts_vty_is_config_node,
};
extern struct gsm_network bts_gsmnet; extern struct gsm_network bts_gsmnet;
struct gsm_network *gsmnet_from_vty(struct vty *v) struct gsm_network *gsmnet_from_vty(struct vty *v)
@@ -105,30 +126,43 @@ struct gsm_network *gsmnet_from_vty(struct vty *v)
return &bts_gsmnet; return &bts_gsmnet;
} }
struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num)
{
struct gsm_bts *bts;
if (num >= net->num_bts)
return NULL;
llist_for_each_entry(bts, &net->bts_list, list) {
if (bts->nr == num)
return bts;
}
return NULL;
}
static struct cmd_node bts_node = { static struct cmd_node bts_node = {
BTS_NODE, BTS_NODE,
"%s(bts)#", "%s(bts)#",
1, 1,
}; };
static struct cmd_node trx_node = {
TRX_NODE,
"%s(trx)#",
1,
};
DEFUN(cfg_bts_trx, cfg_bts_trx_cmd,
"trx <0-0>",
"Select a TRX to configure\n" "TRX number\n")
{
int trx_nr = atoi(argv[0]);
struct gsm_bts *bts = vty->index;
struct gsm_bts_trx *trx;
trx = gsm_bts_trx_num(bts, trx_nr);
if (!trx) {
vty_out(vty, "Unknown TRX %u%s", trx_nr, VTY_NEWLINE);
return CMD_WARNING;
}
vty->index = trx;
vty->index_sub = &trx->description;
vty->node = TRX_NODE;
return CMD_SUCCESS;
}
static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) 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_role_bts *btsb = bts_role_bts(bts);
struct gsm_bts_trx *trx;
vty_out(vty, "bts %u%s", bts->nr, VTY_NEWLINE); vty_out(vty, "bts %u%s", bts->nr, VTY_NEWLINE);
if (bts->description) if (bts->description)
@@ -137,10 +171,40 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
vty_out(vty, " ipa unit-id %u %u%s", vty_out(vty, " ipa unit-id %u %u%s",
bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE); bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE);
vty_out(vty, " oml remote-ip %s%s", btsb->bsc_oml_host, VTY_NEWLINE); vty_out(vty, " oml remote-ip %s%s", btsb->bsc_oml_host, VTY_NEWLINE);
vty_out(vty, " rtp bind-ip %s%s", btsb->rtp_bind_host, VTY_NEWLINE); vty_out(vty, " rtp jitter-buffer %u%s", btsb->rtp_jitter_buf_ms,
VTY_NEWLINE);
vty_out(vty, " paging queue-size %u%s", paging_get_queue_max(btsb->paging_state),
VTY_NEWLINE);
vty_out(vty, " paging lifetime %u%s", paging_get_lifetime(btsb->paging_state),
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)
vty_out(vty, " agch-queue-mgmt threshold %d low %d high %d%s",
btsb->agch_queue_thresh_level, btsb->agch_queue_low_level,
btsb->agch_queue_high_level, VTY_NEWLINE);
bts_model_config_write_bts(vty, bts);
llist_for_each_entry(trx, &bts->trx_list, list) {
struct trx_power_params *tpp = &trx->power_params;
vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE);
if (trx->power_params.user_gain_mdB)
vty_out(vty, " user-gain %u mdB%s",
tpp->user_gain_mdB, VTY_NEWLINE);
vty_out(vty, " power-ramp max-initial %d mdBm%s",
tpp->ramp.max_initial_pout_mdBm, VTY_NEWLINE);
vty_out(vty, " power-ramp step-size %d mdB%s",
tpp->ramp.step_size_mdB, VTY_NEWLINE);
vty_out(vty, " power-ramp step-interval %d%s",
tpp->ramp.step_interval_sec, VTY_NEWLINE);
bts_model_config_write_trx(vty, trx);
}
} }
int config_write_bts(struct vty *vty) static int config_write_bts(struct vty *vty)
{ {
struct gsm_network *net = gsmnet_from_vty(vty); struct gsm_network *net = gsmnet_from_vty(vty);
struct gsm_bts *bts; struct gsm_bts *bts;
@@ -151,6 +215,11 @@ int config_write_bts(struct vty *vty)
return CMD_SUCCESS; return CMD_SUCCESS;
} }
static int config_write_dummy(struct vty *vty)
{
return CMD_SUCCESS;
}
/* per-BTS configuration */ /* per-BTS configuration */
DEFUN(cfg_bts, DEFUN(cfg_bts,
cfg_bts_cmd, cfg_bts_cmd,
@@ -180,7 +249,9 @@ DEFUN(cfg_bts,
DEFUN(cfg_bts_unit_id, DEFUN(cfg_bts_unit_id,
cfg_bts_unit_id_cmd, cfg_bts_unit_id_cmd,
"ipa unit-id <0-65534> <0-255>", "ipa unit-id <0-65534> <0-255>",
"Set the ip.access BTS Unit ID of this BTS\n") "ip.access RSL commands\n"
"Set the Unit ID of this BTS\n"
"Site ID\n" "Unit ID\n")
{ {
struct gsm_bts *bts = vty->index; struct gsm_bts *bts = vty->index;
int site_id = atoi(argv[0]); int site_id = atoi(argv[0]);
@@ -195,7 +266,15 @@ DEFUN(cfg_bts_unit_id,
DEFUN(cfg_bts_band, DEFUN(cfg_bts_band,
cfg_bts_band_cmd, cfg_bts_band_cmd,
"band (450|GSM450|480|GSM480|750|GSM750|810|GSM810|850|GSM850|900|GSM900|1800|DCS1800|1900|PCS1900)", "band (450|GSM450|480|GSM480|750|GSM750|810|GSM810|850|GSM850|900|GSM900|1800|DCS1800|1900|PCS1900)",
"Set the frequency band of this BTS\n" "Frequency band\n") "Set the frequency band of this BTS\n"
"Alias for GSM450\n450Mhz\n"
"Alias for GSM480\n480Mhz\n"
"Alias for GSM750\n750Mhz\n"
"Alias for GSM810\n810Mhz\n"
"Alias for GSM850\n850Mhz\n"
"Alias for GSM900\n900Mhz\n"
"Alias for DCS1800\n1800Mhz\n"
"Alias for PCS1900\n1900Mhz\n")
{ {
struct gsm_bts *bts = vty->index; struct gsm_bts *bts = vty->index;
int band = gsm_band_parse(argv[0]); int band = gsm_band_parse(argv[0]);
@@ -227,21 +306,163 @@ DEFUN(cfg_bts_oml_ip,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
DEFUN(cfg_bts_rtp_bind_ip, #define RTP_STR "RTP parameters\n"
DEFUN_HIDDEN(cfg_bts_rtp_bind_ip,
cfg_bts_rtp_bind_ip_cmd, cfg_bts_rtp_bind_ip_cmd,
"rtp bind-ip A.B.C.D", "rtp bind-ip A.B.C.D",
"RTP Parameters\n" "RTP local bind IP Address\n" "RTP local bind IP Address\n") RTP_STR "RTP local bind IP Address\n" "RTP local bind IP Address\n")
{
vty_out(vty, "%% rtp bind-ip is now deprecated%s", VTY_NEWLINE);
return CMD_WARNING;
}
DEFUN(cfg_bts_rtp_jitbuf,
cfg_bts_rtp_jitbuf_cmd,
"rtp jitter-buffer <0-10000>",
RTP_STR "RTP jitter buffer\n" "jitter buffer in ms\n")
{ {
struct gsm_bts *bts = vty->index; struct gsm_bts *bts = vty->index;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts); struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
if (btsb->rtp_bind_host) btsb->rtp_jitter_buf_ms = atoi(argv[0]);
talloc_free(btsb->rtp_bind_host);
btsb->rtp_bind_host = talloc_strdup(btsb, argv[0]);
return CMD_SUCCESS; return CMD_SUCCESS;
} }
#define PAG_STR "Paging related parameters\n"
DEFUN(cfg_bts_paging_queue_size,
cfg_bts_paging_queue_size_cmd,
"paging queue-size <1-1024>",
PAG_STR "Maximum length of BTS-internal paging queue\n"
"Maximum length of BTS-internal paging queue\n")
{
struct gsm_bts *bts = vty->index;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
paging_set_queue_max(btsb->paging_state, atoi(argv[0]));
return CMD_SUCCESS;
}
DEFUN(cfg_bts_paging_lifetime,
cfg_bts_paging_lifetime_cmd,
"paging lifetime <0-60>",
PAG_STR "Maximum lifetime of a paging record\n"
"Maximum lifetime of a paging record (secods)\n")
{
struct gsm_bts *bts = vty->index;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
paging_set_lifetime(btsb->paging_state, atoi(argv[0]));
return CMD_SUCCESS;
}
#define AGCH_QUEUE_STR "AGCH queue mgmt\n"
DEFUN(cfg_bts_agch_queue_mgmt_params,
cfg_bts_agch_queue_mgmt_params_cmd,
"agch-queue-mgmt threshold <0-100> low <0-100> high <0-100000>",
AGCH_QUEUE_STR
"Threshold to start cleanup\nin %% of the maximum queue length\n"
"Low water mark for cleanup\nin %% of the maximum queue length\n"
"High water mark for cleanup\nin %% of the maximum queue length\n")
{
struct gsm_bts *bts = vty->index;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
btsb->agch_queue_thresh_level = atoi(argv[0]);
btsb->agch_queue_low_level = atoi(argv[1]);
btsb->agch_queue_high_level = atoi(argv[2]);
return CMD_SUCCESS;
}
DEFUN(cfg_bts_agch_queue_mgmt_default,
cfg_bts_agch_queue_mgmt_default_cmd,
"agch-queue-mgmt default",
AGCH_QUEUE_STR
"Reset clean parameters to default values\n")
{
struct gsm_bts *bts = vty->index;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
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;
return CMD_SUCCESS;
}
#define DB_DBM_STR \
"Unit is dB (decibels)\n" \
"Unit is mdB (milli-decibels, or rather 1/10000 bel)\n"
static int parse_mdbm(const char *valstr, const char *unit)
{
int val = atoi(valstr);
if (!strcmp(unit, "dB") || !strcmp(unit, "dBm"))
return val * 1000;
else
return val;
}
DEFUN(cfg_trx_user_gain,
cfg_trx_user_gain_cmd,
"user-gain <-100000-100000> (dB|mdB)",
"Inform BTS about additional, user-provided gain or attenuation at TRX output\n"
"Value of user-provided external gain(+)/attenuation(-)\n" DB_DBM_STR)
{
struct gsm_bts_trx *trx = vty->index;
trx->power_params.user_gain_mdB = parse_mdbm(argv[0], argv[1]);
return CMD_SUCCESS;
}
#define PR_STR "Power-Ramp settings"
DEFUN(cfg_trx_pr_max_initial, cfg_trx_pr_max_initial_cmd,
"power-ramp max-initial <0-100000> (dBm|mdBm)",
PR_STR "Maximum initial power\n"
"Value\n" DB_DBM_STR)
{
struct gsm_bts_trx *trx = vty->index;
trx->power_params.ramp.max_initial_pout_mdBm =
parse_mdbm(argv[0], argv[1]);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_pr_step_size, cfg_trx_pr_step_size_cmd,
"power-ramp step-size <1-100000> (dB|mdB)",
PR_STR "Power increase by step\n"
"Step size\n" DB_DBM_STR)
{
struct gsm_bts_trx *trx = vty->index;
trx->power_params.ramp.step_size_mdB =
parse_mdbm(argv[0], argv[1]);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_pr_step_interval, cfg_trx_pr_step_interval_cmd,
"power-ramp step-interval <1-100>",
PR_STR "Power increase by step\n"
"Step time in seconds\n")
{
struct gsm_bts_trx *trx = vty->index;
trx->power_params.ramp.step_interval_sec = atoi(argv[0]);
return CMD_SUCCESS;
}
/* ====================================================================== /* ======================================================================
* SHOW * SHOW
* ======================================================================*/ * ======================================================================*/
@@ -253,8 +474,21 @@ static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms)
abis_nm_avail_name(nms->availability), VTY_NEWLINE); abis_nm_avail_name(nms->availability), VTY_NEWLINE);
} }
static unsigned int llist_length(struct llist_head *list)
{
unsigned int len = 0;
struct llist_head *pos;
llist_for_each(pos, list)
len++;
return len;
}
static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
{ {
struct gsm_bts_role_bts *btsb = bts->role;
vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, " vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, "
"BSIC %u, TSC %u and %u TRX%s", "BSIC %u, TSC %u and %u TRX%s",
bts->nr, "FIXME", gsm_band_name(bts->band), bts->nr, "FIXME", gsm_band_name(bts->band),
@@ -270,6 +504,19 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
net_dump_nmstate(vty, &bts->mo.nm_state); net_dump_nmstate(vty, &bts->mo.nm_state);
vty_out(vty, " Site Mgr NM State: "); vty_out(vty, " Site Mgr NM State: ");
net_dump_nmstate(vty, &bts->site_mgr.mo.nm_state); net_dump_nmstate(vty, &bts->site_mgr.mo.nm_state);
vty_out(vty, " Paging: Queue size %u, occupied %u, lifetime %us%s",
paging_get_queue_max(btsb->paging_state), paging_queue_length(btsb->paging_state),
paging_get_lifetime(btsb->paging_state), VTY_NEWLINE);
vty_out(vty, " AGCH: Queue limit %u, occupied %d, "
"dropped %llu, merged %llu, rejected %llu, "
"ag-res %llu, non-res %llu%s",
btsb->agch_max_queue_length, btsb->agch_queue_length,
btsb->agch_queue_dropped_msgs, btsb->agch_queue_merged_msgs,
btsb->agch_queue_rejected_msgs, btsb->agch_queue_agch_msgs,
btsb->agch_queue_pch_msgs,
VTY_NEWLINE);
vty_out(vty, " CBCH backlog queue length: %u%s",
llist_length(&btsb->smscb_state.queue), VTY_NEWLINE);
#if 0 #if 0
vty_out(vty, " Paging: %u pending requests, %u free slots%s", vty_out(vty, " Paging: %u pending requests, %u free slots%s",
paging_pending_requests_nr(bts), paging_pending_requests_nr(bts),
@@ -316,6 +563,123 @@ DEFUN(show_bts, show_bts_cmd, "show bts <0-255>",
return CMD_SUCCESS; return CMD_SUCCESS;
} }
static struct gsm_lchan *resolve_lchan(struct gsm_network *net,
const char **argv, int idx)
{
int bts_nr = atoi(argv[idx+0]);
int trx_nr = atoi(argv[idx+1]);
int ts_nr = atoi(argv[idx+2]);
int lchan_nr = atoi(argv[idx+3]);
struct gsm_bts *bts;
struct gsm_bts_trx *trx;
struct gsm_bts_trx_ts *ts;
bts = gsm_bts_num(net, bts_nr);
if (!bts)
return NULL;
trx = gsm_bts_trx_num(bts, trx_nr);
if (!trx)
return NULL;
if (ts_nr >= ARRAY_SIZE(trx->ts))
return NULL;
ts = &trx->ts[ts_nr];
if (lchan_nr >= ARRAY_SIZE(ts->lchan))
return NULL;
return &ts->lchan[lchan_nr];
}
#define BTS_T_T_L_STR \
"BTS related commands\n" \
"BTS number\n" \
"TRX related commands\n" \
"TRX number\n" \
"timeslot related commands\n" \
"timeslot number\n" \
"logical channel commands\n" \
"logical channel number\n"
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>",
BTS_T_T_L_STR "RTP settings\n"
"Jitter buffer\n" "Size of jitter buffer in (ms)\n")
{
struct gsm_network *net = gsmnet_from_vty(vty);
struct gsm_lchan *lchan;
int jitbuf_ms = atoi(argv[4]);
lchan = resolve_lchan(net, argv, 0);
if (!lchan) {
vty_out(vty, "%% can't find BTS%s", VTY_NEWLINE);
return CMD_WARNING;
}
if (!lchan->abis_ip.rtp_socket) {
vty_out(vty, "%% this channel has no active RTP stream%s",
VTY_NEWLINE);
return CMD_WARNING;
}
osmo_rtp_socket_set_param(lchan->abis_ip.rtp_socket,
OSMO_RTP_P_JITBUF, jitbuf_ms);
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")
{
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);
return CMD_WARNING;
}
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) int bts_vty_init(const struct log_info *cat)
{ {
@@ -329,7 +693,27 @@ int bts_vty_init(const struct log_info *cat)
install_element(BTS_NODE, &cfg_bts_unit_id_cmd); install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
install_element(BTS_NODE, &cfg_bts_oml_ip_cmd); install_element(BTS_NODE, &cfg_bts_oml_ip_cmd);
install_element(BTS_NODE, &cfg_bts_rtp_bind_ip_cmd); install_element(BTS_NODE, &cfg_bts_rtp_bind_ip_cmd);
install_element(BTS_NODE, &cfg_bts_rtp_jitbuf_cmd);
install_element(BTS_NODE, &cfg_bts_band_cmd); install_element(BTS_NODE, &cfg_bts_band_cmd);
install_element(BTS_NODE, &cfg_description_cmd); install_element(BTS_NODE, &cfg_description_cmd);
install_element(BTS_NODE, &cfg_no_description_cmd); install_element(BTS_NODE, &cfg_no_description_cmd);
install_element(BTS_NODE, &cfg_bts_paging_queue_size_cmd);
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);
/* add and link to TRX config node */
install_element(BTS_NODE, &cfg_bts_trx_cmd);
install_node(&trx_node, config_write_dummy);
install_default(TRX_NODE);
install_element(TRX_NODE, &cfg_trx_user_gain_cmd);
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(ENABLE_NODE, &bts_t_t_l_jitter_buf_cmd);
install_element(ENABLE_NODE, &bts_etsw_idle_cmd);
return 0;
} }

View File

@@ -1,8 +0,0 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS)
LDADD = $(LIBOSMOCORE_LIBS)
bin_PROGRAMS = osmo-bts-bb
osmo_bts_bb_SOURCES = main.c
osmo_bts_bb_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)

View File

@@ -1,304 +0,0 @@
/* Layer1 control code, talking L1CTL protocol with L1 on the phone */
/* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2011 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 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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <l1ctl_proto.h>
#include <osmocom/core/signal.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/gsmtap_util.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/gsm/rsl.h>
#include <osmocom/bb/common/l1ctl.h>
#include <osmocom/bb/common/osmocom_data.h>
#include <osmocom/bb/common/l1l2_interface.h>
#include <osmocom/bb/common/lapdm.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/abis.h>
#include <osmo-bts/rtp.h>
#include <osmo-bts/bts.h>
static struct msgb *osmo_l1_alloc(uint8_t msg_type)
{
struct l1ctl_hdr *l1h;
struct msgb *msg = msgb_alloc_headroom(256, 4, "osmo_l1");
if (!msg) {
LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory.\n");
return NULL;
}
msg->l1h = msgb_put(msg, sizeof(*l1h));
l1h = (struct l1ctl_hdr *) msg->l1h;
l1h->msg_type = msg_type;
return msg;
}
static int osmo_make_band_arfcn(struct osmocom_ms *ms, uint16_t arfcn)
{
/* TODO: Include the band */
return arfcn;
}
/* Receive L1CTL_DATA_IND (Data Indication from L1) */
int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg)
{
struct l1ctl_info_dl *dl, dl_cpy;
struct l1ctl_data_ind *ccch;
struct lapdm_entity *le;
struct rx_meas_stat *meas = &ms->meas;
uint8_t chan_type, chan_ts, chan_ss;
uint8_t gsmtap_chan_type;
struct gsm_time tm;
struct osmobts_ms *bts_ms = container_of(ms, struct osmobts_ms, ms);
struct osmobts_lchan *lchan;
if (msgb_l3len(msg) < sizeof(*ccch)) {
LOGP(DL1C, LOGL_ERROR, "MSG too short Data Ind: %u\n",
msgb_l3len(msg));
msgb_free(msg);
return -1;
}
dl = (struct l1ctl_info_dl *) msg->l1h;
msg->l2h = dl->payload;
ccch = (struct l1ctl_data_ind *) msg->l2h;
gsm_fn2gsmtime(&tm, ntohl(dl->frame_nr));
rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts);
lchan = bts_ms->trx->slot[chan_ts].lchan[chan_ss];
if (!lchan) {
LOGP(DL1C, LOGL_ERROR, "Data IND for non existing lchan\n");
msgb_free(msg);
return -1;
}
LOGP(DL1C, LOGL_NOTICE, "RX: %s | %s(%.4u/%.2u/%.2u) %d dBm\n",
rsl_chan_nr_str(dl->chan_nr),
hexdump(ccch->data, sizeof(ccch->data)),
tm.t1, tm.t2, tm.t3, (int)dl->rx_level-110);
meas->last_fn = ntohl(dl->frame_nr);
meas->frames++;
meas->snr += dl->snr;
meas->berr += dl->num_biterr;
meas->rxlev += dl->rx_level;
if (dl->fire_crc >= 2) {
LOGP(DL1C, LOGL_NOTICE, "Dropping frame with %u bit errors\n",
dl->num_biterr);
msgb_free(msg);
return 0;
}
/* send CCCH data via GSMTAP */
gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, dl->link_id);
gsmtap_sendmsg(ntohs(dl->band_arfcn), chan_ts, gsmtap_chan_type, chan_ss,
tm.fn, dl->rx_level-110, dl->snr, ccch->data,
sizeof(ccch->data));
/* determine LAPDm entity based on SACCH or not */
if (dl->link_id & 0x40)
le = &lchan->l2_entity.lapdm_acch;
else
le = &lchan->l2_entity.lapdm_dcch;
/* make local stack copy of l1ctl_info_dl, as LAPDm will
* overwrite skb hdr */
memcpy(&dl_cpy, dl, sizeof(dl_cpy));
/* pull the L1 header from the msgb */
msgb_pull(msg, msg->l2h - (msg->l1h-sizeof(struct l1ctl_hdr)));
msg->l1h = NULL;
/* send it up into LAPDm */
l2_ph_data_ind(msg, le, &dl_cpy);
return 0;
}
/* Receive L1CTL_DATA_CONF (Data Confirm from L1) */
static int rx_ph_data_conf(struct osmocom_ms *ms, struct msgb *msg)
{
struct l1ctl_info_dl *dl;
struct lapdm_entity *le;
uint8_t chan_type, chan_ts, chan_ss;
struct osmobts_ms *bts_ms = container_of(ms, struct osmobts_ms, ms);
struct osmobts_lchan *lchan;
dl = (struct l1ctl_info_dl *) msg->l1h;
rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts);
lchan = bts_ms->trx->slot[chan_ts].lchan[chan_ss];
if (!lchan) {
LOGP(DL1C, LOGL_ERROR, "Data IND for non existing lchan\n");
msgb_free(msg);
return -1;
}
/* determine LAPDm entity based on SACCH or not */
if (dl->link_id & 0x40)
le = &lchan->l2_entity.lapdm_acch;
else
le = &lchan->l2_entity.lapdm_dcch;
/* send it up into LAPDm */
l2_ph_data_conf(msg, le);
return 0;
}
/* Transmit L1CTL_DATA_REQ */
int l1ctl_tx_data_req(struct osmocom_ms *ms, struct msgb *msg,
uint8_t chan_nr, uint8_t link_id)
{
struct l1ctl_hdr *l1h;
struct l1ctl_info_ul *l1i_ul;
uint8_t chan_type, chan_ts, chan_ss;
uint8_t gsmtap_chan_type;
if (msgb_l2len(msg) > GSM_MACBLOCK_LEN) {
LOGP(DL1C, LOGL_ERROR, "L1 cannot handle message length "
"> 23 (%u)\n", msgb_l2len(msg));
msgb_free(msg);
return -EINVAL;
} else if (msgb_l2len(msg) < GSM_MACBLOCK_LEN)
LOGP(DL1C, LOGL_ERROR, "L1 message length < 23 (%u) "
"doesn't seem right!\n", msgb_l2len(msg));
/* send copy via GSMTAP */
rsl_dec_chan_nr(chan_nr, &chan_type, &chan_ss, &chan_ts);
gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, link_id);
gsmtap_sendmsg(0|0x4000, chan_ts, gsmtap_chan_type, chan_ss,
0, 127, 255, msg->l2h, msgb_l2len(msg));
LOGP(DL1C, LOGL_NOTICE, "TX: %s | %s\n", rsl_chan_nr_str(chan_nr),
hexdump(msg->l2h, msgb_l2len(msg)));
/* prepend uplink info header */
l1i_ul = (struct l1ctl_info_ul *) msgb_push(msg, sizeof(*l1i_ul));
l1i_ul->chan_nr = chan_nr;
l1i_ul->link_id = link_id;
/* prepend l1 header */
msg->l1h = msgb_push(msg, sizeof(*l1h));
l1h = (struct l1ctl_hdr *) msg->l1h;
l1h->msg_type = L1CTL_DATA_REQ;
printf("todo: transcode message\n");
return osmo_send_l1(ms, msg);
}
/* Transmit L1CTL_RESET_REQ */
int l1ctl_tx_reset_req(struct osmocom_ms *ms, uint8_t type)
{
struct msgb *msg;
struct l1ctl_reset *res;
msg = osmo_l1_alloc(L1CTL_RESET_REQ);
if (!msg)
return -1;
LOGP(DL1C, LOGL_INFO, "Tx Reset Req (%u)\n", type);
res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
res->type = type;
return osmo_send_l1(ms, msg);
}
/* Receive L1CTL_RESET_IND */
static int rx_l1_reset(struct osmocom_ms *ms)
{
LOGP(DL1C, LOGL_INFO, "Layer1 Reset indication\n");
dispatch_signal(SS_L1CTL, S_L1CTL_RESET, ms);
return 0;
}
/* Receive incoming data from L1 using L1CTL format */
int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg)
{
int rc = 0;
struct l1ctl_hdr *l1h;
struct l1ctl_info_dl *dl;
if (msgb_l2len(msg) < sizeof(*dl)) {
LOGP(DL1C, LOGL_ERROR, "Short Layer2 message: %u\n",
msgb_l2len(msg));
msgb_free(msg);
return -1;
}
l1h = (struct l1ctl_hdr *) msg->l1h;
/* move the l1 header pointer to point _BEHIND_ l1ctl_hdr,
as the l1ctl header is of no interest to subsequent code */
msg->l1h = l1h->data;
switch (l1h->msg_type) {
case L1CTL_DATA_IND:
rc = rx_ph_data_ind(ms, msg);
break;
case L1CTL_DATA_CONF:
rc = rx_ph_data_conf(ms, msg);
break;
case L1CTL_RESET_IND:
case L1CTL_RESET_CONF:
rc = rx_l1_reset(ms);
msgb_free(msg);
break;
default:
LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type);
msgb_free(msg);
break;
}
return rc;
}
int l1ctl_tx_param_req(struct osmocom_ms *ms, uint8_t ta, uint8_t tx_power)
{
return -ENOTSUP;
}
int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset,
uint8_t combined)
{
return -ENOTSUP;
}

View File

@@ -1,215 +0,0 @@
/* (C) 2011 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 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 <netinet/in.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/signal.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/select.h>
#include <osmo-bts/logging.h>
//#include <osmocom/bb/common/osmocom_data.h>
#include <osmo-bts/support.h>
#include <osmo-bts/abis.h>
#include <osmo-bts/rtp.h>
#include <osmo-bts/bts.h>
#include <net/if.h>
#define _GNU_SOURCE
#include <getopt.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <string.h>
#include <time.h>
struct log_target *stderr_target;
char *debugs = "DL1C:DLAPDM:DABIS:DOML:DRSL:DSUM";
void *l23_ctx = NULL;
static struct osmocom_bts *bts;
int debug_set = 0;
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";
int quit = 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);
}
static void print_usage(const char *app)
{
printf("Usage: %s [option]\n", app);
printf(" -h --help this text\n");
printf(" -d --debug Change debug flags. (-d %s)\n", debugs);
printf(" -i --bsc-ip IP address of the BSC\n");
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"debug", 1, 0, 'd'},
{"bsc-ip", 1, 0, 'i'},
{0, 0, 0, 0},
};
c = getopt_long(argc, argv, "hd:i:",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
print_usage(argv[0]);
exit(0);
case 'd':
log_parse_category_mask(stderr_target, optarg);
debug_set = 1;
break;
case 'i':
bsc_host = strdup(optarg);
break;
default:
break;
}
}
}
void sighandler(int sigset)
{
if (sigset == SIGHUP || sigset == SIGPIPE)
return;
fprintf(stderr, "Signal %d recevied.\n", sigset);
/* in case there is a lockup during exit */
signal(SIGINT, SIG_DFL);
signal(SIGHUP, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
quit = 1;
// dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL);
}
int main(int argc, char **argv)
{
int ret;
struct hostent *hostent;
struct in_addr *ina;
uint32_t bsc_ip;
uint8_t maskv_tx[2], maskv_rx[2];
printf("((*))\n");
printf(" |\n");
printf(" / \\ OsmoBTS\n");
get_mac();
bts_support_init();
srand(time(NULL));
log_init(&log_info);
stderr_target = log_target_create_stderr();
log_add_target(stderr_target);
log_set_all_filter(stderr_target, 1);
l23_ctx = talloc_named_const(NULL, 1, "layer2 context");
handle_options(argc, argv);
if (!debug_set)
log_parse_category_mask(stderr_target, debugs);
log_set_log_level(stderr_target, LOGL_INFO);
hostent = gethostbyname(bsc_host);
if (!hostent) {
fprintf(stderr, "Failed to resolve BSC hostname '%s'.\n",
bsc_host);
}
ina = (struct in_addr *) hostent->h_addr;
bsc_ip = ntohl(ina->s_addr);
printf("Using BSC at IP: '%d.%d.%d.%d'\n", bsc_ip >> 24,
(bsc_ip >> 16) & 0xff, (bsc_ip >> 8) & 0xff, bsc_ip & 0xff);
printf("Using BTS ID: '%s'\n", bts_id);
bts = create_bts(1, bts_id);
if (!bts)
exit(-1);
maskv_tx[0] = 0x55;
maskv_rx[0] = 0x55;
ret = create_ms(bts->trx[0], 1, maskv_tx, maskv_rx);
if (ret < 0)
goto fail;
ret = abis_open(&bts->link, bsc_ip);
if (ret < 0)
goto fail;
signal(SIGINT, sighandler);
signal(SIGHUP, sighandler);
signal(SIGTERM, sighandler);
signal(SIGPIPE, sighandler);
while (!quit) {
work_bts(bts);
osmo_select_main(0);
}
fail:
destroy_bts(bts);
return 0;
}

View File

@@ -1,16 +1,34 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOCTRL_CFLAGS)
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) -lortp COMMON_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCTRL_LIBS) -lortp
bin_PROGRAMS = sysmobts sysmobts-remote l1fwd-proxy EXTRA_DIST = misc/sysmobts_mgr.h misc/sysmobts_misc.h misc/sysmobts_par.h \
misc/sysmobts_eeprom.h misc/sysmobts_nl.h femtobts.h hw_misc.h \
l1_fwd.h l1_if.h l1_transp.h eeprom.h utils.h oml_router.h
COMMON_SOURCES = main.c femtobts.c l1_if.c oml.c bts_model.c sysmobts_vty.c tch.c bin_PROGRAMS = sysmobts sysmobts-remote l1fwd-proxy sysmobts-mgr sysmobts-util
COMMON_SOURCES = main.c femtobts.c l1_if.c oml.c sysmobts_vty.c tch.c hw_misc.c calib_file.c \
eeprom.c calib_fixup.c utils.c misc/sysmobts_par.c oml_router.c sysmobts_ctrl.c
sysmobts_SOURCES = $(COMMON_SOURCES) l1_transp_hw.c sysmobts_SOURCES = $(COMMON_SOURCES) l1_transp_hw.c
sysmobts_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD) sysmobts_LDADD = $(top_builddir)/src/common/libbts.a $(COMMON_LDADD)
sysmobts_remote_SOURCES = $(COMMON_SOURCES) l1_transp_fwd.c sysmobts_remote_SOURCES = $(COMMON_SOURCES) l1_transp_fwd.c
sysmobts_remote_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD) sysmobts_remote_LDADD = $(top_builddir)/src/common/libbts.a $(COMMON_LDADD)
l1fwd_proxy_SOURCES = l1_fwd_main.c l1_transp_hw.c l1fwd_proxy_SOURCES = l1_fwd_main.c l1_transp_hw.c
l1fwd_proxy_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD) l1fwd_proxy_LDADD = $(top_builddir)/src/common/libbts.a $(COMMON_LDADD)
sysmobts_mgr_SOURCES = \
misc/sysmobts_mgr.c misc/sysmobts_misc.c \
misc/sysmobts_par.c misc/sysmobts_nl.c \
misc/sysmobts_mgr_2050.c \
misc/sysmobts_mgr_vty.c \
misc/sysmobts_mgr_nl.c \
misc/sysmobts_mgr_temp.c \
eeprom.c
sysmobts_mgr_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS)
sysmobts_util_SOURCES = misc/sysmobts_util.c misc/sysmobts_par.c eeprom.c
sysmobts_util_LDADD = $(LIBOSMOCORE_LIBS)

View File

@@ -1,55 +0,0 @@
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/oml.h>
#include <osmo-bts/bts_model.h>
#include "l1_if.h"
int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp)
{
//uint8_t mode = *TLVP_VAL(tp, RSL_IE_CHAN_MODE);
//uint8_t type = *TLVP_VAL(tp, RSL_IE_ACT_TYPE);
lchan_activate(lchan);
/* FIXME: only do this in case of success */
return rsl_tx_chan_act_ack(lchan, bts_model_get_time(lchan->ts->trx->bts));
}
int bts_model_rsl_chan_rel(struct gsm_lchan *lchan)
{
lchan_deactivate(lchan);
return rsl_tx_rf_rel_ack(lchan);
}
int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan)
{
return lchan_deactivate_sacch(lchan);
}
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx)
{
struct femtol1_hdl *fl1 = trx_femtol1_hdl(trx);
return l1if_activate_rf(fl1, 0);
}

View File

@@ -0,0 +1,461 @@
/* sysmocom femtobts L1 calibration file routines*/
/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#include <osmocom/core/utils.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1const.h>
#include "l1_if.h"
#include "femtobts.h"
#include "eeprom.h"
#include "utils.h"
struct calib_file_desc {
const char *fname;
GsmL1_FreqBand_t band;
int uplink;
int rx;
};
static const struct calib_file_desc calib_files[] = {
{
.fname = "calib_rxu_850.cfg",
.band = GsmL1_FreqBand_850,
.uplink = 1,
.rx = 1,
}, {
.fname = "calib_rxu_900.cfg",
.band = GsmL1_FreqBand_900,
.uplink = 1,
.rx = 1,
}, {
.fname = "calib_rxu_1800.cfg",
.band = GsmL1_FreqBand_1800,
.uplink = 1,
.rx = 1,
}, {
.fname = "calib_rxu_1900.cfg",
.band = GsmL1_FreqBand_1900,
.uplink = 1,
.rx = 1,
}, {
.fname = "calib_rxd_850.cfg",
.band = GsmL1_FreqBand_850,
.uplink = 0,
.rx = 1,
}, {
.fname = "calib_rxd_900.cfg",
.band = GsmL1_FreqBand_900,
.uplink = 0,
.rx = 1,
}, {
.fname = "calib_rxd_1800.cfg",
.band = GsmL1_FreqBand_1800,
.uplink = 0,
.rx = 1,
}, {
.fname = "calib_rxd_1900.cfg",
.band = GsmL1_FreqBand_1900,
.uplink = 0,
.rx = 1,
}, {
.fname = "calib_tx_850.cfg",
.band = GsmL1_FreqBand_850,
.uplink = 0,
.rx = 0,
}, {
.fname = "calib_tx_900.cfg",
.band = GsmL1_FreqBand_900,
.uplink = 0,
.rx = 0,
}, {
.fname = "calib_tx_1800.cfg",
.band = GsmL1_FreqBand_1800,
.uplink = 0,
.rx = 0,
}, {
.fname = "calib_tx_1900.cfg",
.band = GsmL1_FreqBand_1900,
.uplink = 0,
.rx = 0,
},
};
static const unsigned int arrsize_by_band[] = {
[GsmL1_FreqBand_850] = 124,
[GsmL1_FreqBand_900] = 194,
[GsmL1_FreqBand_1800] = 374,
[GsmL1_FreqBand_1900] = 299
};
/* determine next calibration file index based on supported bands */
static int next_calib_file_idx(uint32_t band_mask, int last_idx)
{
int i;
for (i = last_idx+1; i < ARRAY_SIZE(calib_files); i++) {
int band = band_femto2osmo(calib_files[i].band);
if (band < 0)
continue;
if (band_mask & band)
return i;
}
return -1;
}
static float read_float(FILE *in)
{
int rc;
float f = 0.0f;
rc = fscanf(in, "%f\n", &f);
if (rc != 1)
LOGP(DL1C, LOGL_ERROR,
"Reading a float from calib data failed.\n");
return f;
}
static int read_int(FILE *in)
{
int rc;
int i = 0;
rc = fscanf(in, "%d\n", &i);
if (rc != 1)
LOGP(DL1C, LOGL_ERROR,
"Reading an int from calib data failed.\n");
return i;
}
/* some particular units have calibration data that is incompatible with
* firmware >= 3.3, so we need to alter it as follows: */
static const float delta_by_band[Num_GsmL1_FreqBand] = {
[GsmL1_FreqBand_850] = -2.5f,
[GsmL1_FreqBand_900] = -2.0f,
[GsmL1_FreqBand_1800] = -8.0f,
[GsmL1_FreqBand_1900] = -12.0f,
};
extern const uint8_t fixup_macs[95][6];
static void determine_fixup(struct femtol1_hdl *fl1h)
{
uint8_t macaddr[6];
int rc, i;
rc = eeprom_ReadEthAddr(macaddr);
if (rc != EEPROM_SUCCESS) {
LOGP(DL1C, LOGL_ERROR,
"Unable to read Ethenet MAC from EEPROM\n");
return;
}
/* assume no fixup is needed */
fl1h->fixup_needed = FIXUP_NOT_NEEDED;
if (fl1h->hw_info.dsp_version[0] < 3 ||
(fl1h->hw_info.dsp_version[0] == 3 &&
fl1h->hw_info.dsp_version[1] < 3)) {
LOGP(DL1C, LOGL_NOTICE, "No calibration table fix-up needed, "
"firmware < 3.3\n");
return;
}
for (i = 0; i < sizeof(fixup_macs)/6; i++) {
if (!memcmp(fixup_macs[i], macaddr, 6)) {
fl1h->fixup_needed = FIXUP_NEEDED;
break;
}
}
LOGP(DL1C, LOGL_NOTICE, "MAC Address is %02x:%02x:%02x:%02x:%02x:%02x -> %s\n",
macaddr[0], macaddr[1], macaddr[2], macaddr[3],
macaddr[4], macaddr[5],
fl1h->fixup_needed == FIXUP_NEEDED ? "FIXUP" : "NO FIXUP");
}
static int fixup_needed(struct femtol1_hdl *fl1h)
{
if (fl1h->fixup_needed == FIXUP_UNITILIAZED)
determine_fixup(fl1h);
return fl1h->fixup_needed == FIXUP_NEEDED;
}
static void calib_fixup_rx(struct femtol1_hdl *fl1h, SuperFemto_Prim_t *prim)
{
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,4,0)
SuperFemto_SetRxCalibTblReq_t *rx = &prim->u.setRxCalibTblReq;
if (fixup_needed(fl1h))
rx->fExtRxGain += delta_by_band[rx->freqBand];
#endif
}
static int calib_file_read(const char *path, const struct calib_file_desc *desc,
SuperFemto_Prim_t *prim)
{
FILE *in;
char fname[PATH_MAX];
int i;
fname[0] = '\0';
snprintf(fname, sizeof(fname)-1, "%s/%s", path, desc->fname);
fname[sizeof(fname)-1] = '\0';
in = fopen(fname, "r");
if (!in) {
LOGP(DL1C, LOGL_ERROR,
"Failed to open '%s' for calibration data.\n", fname);
return -1;
}
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,4,0)
if (desc->rx) {
SuperFemto_SetRxCalibTblReq_t *rx = &prim->u.setRxCalibTblReq;
memset(rx, 0, sizeof(*rx));
prim->id = SuperFemto_PrimId_SetRxCalibTblReq;
rx->freqBand = desc->band;
rx->bUplink = desc->uplink;
rx->fExtRxGain = read_float(in);
rx->fRxMixGainCorr = read_float(in);
for (i = 0; i < ARRAY_SIZE(rx->fRxLnaGainCorr); i++)
rx->fRxLnaGainCorr[i] = read_float(in);
for (i = 0; i < arrsize_by_band[desc->band]; i++)
rx->fRxRollOffCorr[i] = read_float(in);
if (desc->uplink) {
rx->u8IqImbalMode = read_int(in);
printf("%s: u8IqImbalMode=%d\n", desc->fname, rx->u8IqImbalMode);
for (i = 0; i < ARRAY_SIZE(rx->u16IqImbalCorr); i++)
rx->u16IqImbalCorr[i] = read_int(in);
}
} else {
SuperFemto_SetTxCalibTblReq_t *tx = &prim->u.setTxCalibTblReq;
memset(tx, 0, sizeof(*tx));
prim->id = SuperFemto_PrimId_SetTxCalibTblReq;
tx->freqBand = desc->band;
for (i = 0; i < ARRAY_SIZE(tx->fTxGainGmsk); i++)
tx->fTxGainGmsk[i] = read_float(in);
tx->fTx8PskCorr = read_float(in);
for (i = 0; i < ARRAY_SIZE(tx->fTxExtAttCorr); i++)
tx->fTxExtAttCorr[i] = read_float(in);
for (i = 0; i < arrsize_by_band[desc->band]; i++)
tx->fTxRollOffCorr[i] = read_float(in);
}
#else
#warning Format of calibration tables before API version 2.4.0 not supported
#endif
fclose(in);
return 0;
}
static int calib_eeprom_read(const struct calib_file_desc *desc, SuperFemto_Prim_t *prim)
{
eeprom_Error_t eerr;
int i;
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,4,0)
if (desc->rx) {
SuperFemto_SetRxCalibTblReq_t *rx = &prim->u.setRxCalibTblReq;
eeprom_RxCal_t rx_cal;
memset(rx, 0, sizeof(*rx));
prim->id = SuperFemto_PrimId_SetRxCalibTblReq;
rx->freqBand = desc->band;
rx->bUplink = desc->uplink;
eerr = eeprom_ReadRxCal(desc->band, desc->uplink, &rx_cal);
if (eerr != EEPROM_SUCCESS) {
LOGP(DL1C, LOGL_ERROR, "Error reading RxCalibration "
"from EEPROM, band=%d, ul=%d, err=%d\n",
desc->band, desc->uplink, eerr);
return -EIO;
}
rx->fExtRxGain = rx_cal.fExtRxGain;
rx->fRxMixGainCorr = rx_cal.fRxMixGainCorr;
for (i = 0; i < ARRAY_SIZE(rx->fRxLnaGainCorr); i++)
rx->fRxLnaGainCorr[i] = rx_cal.fRxLnaGainCorr[i];
for (i = 0; i < arrsize_by_band[desc->band]; i++)
rx->fRxRollOffCorr[i] = rx_cal.fRxRollOffCorr[i];
if (desc->uplink) {
rx->u8IqImbalMode = rx_cal.u8IqImbalMode;
for (i = 0; i < ARRAY_SIZE(rx->u16IqImbalCorr); i++)
rx->u16IqImbalCorr[i] = rx_cal.u16IqImbalCorr[i];
}
} else {
SuperFemto_SetTxCalibTblReq_t *tx = &prim->u.setTxCalibTblReq;
eeprom_TxCal_t tx_cal;
memset(tx, 0, sizeof(*tx));
prim->id = SuperFemto_PrimId_SetTxCalibTblReq;
tx->freqBand = desc->band;
eerr = eeprom_ReadTxCal(desc->band, &tx_cal);
if (eerr != EEPROM_SUCCESS) {
LOGP(DL1C, LOGL_ERROR, "Error reading TxCalibration "
"from EEPROM, band=%d, err=%d\n",
desc->band, eerr);
return -EIO;
}
for (i = 0; i < ARRAY_SIZE(tx->fTxGainGmsk); i++)
tx->fTxGainGmsk[i] = tx_cal.fTxGainGmsk[i];
tx->fTx8PskCorr = tx_cal.fTx8PskCorr;
for (i = 0; i < ARRAY_SIZE(tx->fTxExtAttCorr); i++)
tx->fTxExtAttCorr[i] = tx_cal.fTxExtAttCorr[i];
for (i = 0; i < arrsize_by_band[desc->band]; i++)
tx->fTxRollOffCorr[i] = tx_cal.fTxRollOffCorr[i];
}
#endif
return 0;
}
/* iteratively download the calibration data into the L1 */
static int calib_send_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
void *data);
/* send the calibration table for a single specified file */
static int calib_file_send(struct femtol1_hdl *fl1h,
const struct calib_file_desc *desc)
{
struct calib_send_state *st = &fl1h->st;
struct msgb *msg;
int rc;
msg = sysp_msgb_alloc();
if (fl1h->calib_path)
rc = calib_file_read(fl1h->calib_path, desc, msgb_sysprim(msg));
else
rc = calib_eeprom_read(desc, msgb_sysprim(msg));
if (rc < 0) {
msgb_free(msg);
/* still, we'd like to continue trying to load
* calibration for all other bands */
st->last_file_idx = next_calib_file_idx(fl1h->hw_info.band_support,
st->last_file_idx);
if (st->last_file_idx >= 0)
return calib_file_send(fl1h,
&calib_files[st->last_file_idx]);
else
return rc;
}
calib_fixup_rx(fl1h, msgb_sysprim(msg));
return l1if_req_compl(fl1h, msg, calib_send_compl_cb, NULL);
}
/* completion callback after every SetCalibTbl is confirmed */
static int calib_send_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
void *data)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
struct calib_send_state *st = &fl1h->st;
LOGP(DL1C, LOGL_NOTICE, "L1 calibration table %s loaded (src: %s)\n",
calib_files[st->last_file_idx].fname,
fl1h->calib_path ? "file" : "eeprom");
msgb_free(l1_msg);
st->last_file_idx = next_calib_file_idx(fl1h->hw_info.band_support,
st->last_file_idx);
if (st->last_file_idx >= 0)
return calib_file_send(fl1h,
&calib_files[st->last_file_idx]);
LOGP(DL1C, LOGL_INFO, "L1 calibration table loading complete!\n");
eeprom_free_resources();
return 0;
}
int calib_load(struct femtol1_hdl *fl1h)
{
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
LOGP(DL1C, LOGL_ERROR, "L1 calibration is not supported on pre 2.4.0 firmware.\n");
return -1;
#else
int idx = next_calib_file_idx(fl1h->hw_info.band_support, -1);
if (idx < 0) {
LOGP(DL1C, LOGL_ERROR, "No band_support?!?\n");
return -1;
}
return calib_file_send(fl1h, &calib_files[idx]);
#endif
}
#if 0
int main(int argc, char **argv)
{
SuperFemto_Prim_t p;
int i;
for (i = 0; i < ARRAY_SIZE(calib_files); i++) {
memset(&p, 0, sizeof(p));
calib_read_file(argv[1], &calib_files[i], &p);
}
exit(0);
}
#endif

View File

@@ -0,0 +1,101 @@
/* AUTOGENERATED, DO NOT EDIT */
#include <stdint.h>
const uint8_t fixup_macs[95][6] = {
{ 0x00, 0x0D, 0xCC, 0x08, 0x02, 0x3B },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x31 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x32 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x33 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x34 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x35 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x36 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x37 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x38 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x39 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x3A },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x3C },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x3D },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x3E },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x40 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x41 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x42 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x43 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x44 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x45 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x46 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x47 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x48 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x49 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x4A },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x4B },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x4C },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x4D },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x4E },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x4F },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x50 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x51 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x52 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x53 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x55 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x56 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x57 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x58 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x59 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x5A },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x5B },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x5C },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x5D },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x5E },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x5F },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x60 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x97 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x98 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x99 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x9A },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x9B },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x9C },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x9D },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x9E },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x9F },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA0 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA1 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA3 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA4 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA5 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA6 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA7 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA8 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA9 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xAA },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xAB },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xAC },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xAD },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xAE },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xAF },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB0 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB1 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB2 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB3 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB4 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB5 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB6 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB7 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB8 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB9 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xBA },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xBB },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xBC },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xBE },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xBF },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xC0 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xC1 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xC3 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xC6 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xC7 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xC8 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xC9 },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xCA },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xCB },
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xCD },
};

1426
src/osmo-bts-sysmo/eeprom.c Normal file

File diff suppressed because it is too large Load Diff

296
src/osmo-bts-sysmo/eeprom.h Normal file
View File

@@ -0,0 +1,296 @@
/***************************************************************************
*
* **** I
* ****** ***
* ******* ****
* ******** **** **** **** ********* ******* **** ***********
* ********* **** **** **** ********* ************** *************
* **** ***** **** **** **** **** ***** ****** ***** ****
* **** ***** **** **** **** **** ***** **** **** ****
* **** ********* **** **** **** **** **** **** ****
* **** ******** **** ****I **** ***** ***** **** ****
* **** ****** ***** ****** ***** ****** ******* ****** *******
* **** **** ************ ****** ************* *************
* **** *** **** **** **** ***** **** ***** ****
* ****
* I N N O V A T I O N T O D A Y F O R T O M M O R O W ****
* ***
*
***************************************************************************
*
* Project : SuperFemto
* File : eeprom.h
* Description : EEPROM interface.
*
* Copyright (c) Nutaq. 2012
*
***************************************************************************
*
* "$Revision: 1.1 $"
* "$Name: $"
* "$Date: 2012/06/20 02:18:30 $"
* "$Author: Yves.Godin $"
*
***************************************************************************/
#ifndef EEPROM_H__
#define EEPROM_H__
#include <stdint.h>
/****************************************************************************
* Public constants *
****************************************************************************/
/**
* EEPROM error code
*/
typedef enum
{
EEPROM_SUCCESS = 0, ///< Success
EEPROM_ERR_DEVICE = -1, ///< Device access error
EEPROM_ERR_PARITY = -2, ///< Parity error
EEPROM_ERR_UNAVAILABLE = -3, ///< Information unavailable
EEPROM_ERR_INVALID = -4, ///< Invalid format
EEPROM_ERR_UNSUPPORTED = -5, ///< Unsupported format
} eeprom_Error_t;
/****************************************************************************
* Struct : eeprom_SysInfo_t
************************************************************************//**
*
* SuperFemto system information.
*
***************************************************************************/
typedef struct eeprom_SysInfo
{
char szSn[16]; ///< Serial number
uint8_t u8Rev; ///< Board revision
uint8_t u8Tcxo; ///< TCXO present (0:absent, 1:present, X:unknown)
uint8_t u8Ocxo; ///< OCXO present (0:absent, 1:present, X:unknown)
uint8_t u8GSM850; ///< GSM-850 supported (0:unsupported, 1:supported, X:unknown)
uint8_t u8GSM900; ///< GSM-900 supported (0:unsupported, 1:supported, X:unknown)
uint8_t u8DCS1800; ///< GSM-1800 supported (0:unsupported, 1:supported, X:unknown)
uint8_t u8PCS1900; ///< GSM-1900 supported (0:unsupported, 1:supported, X:unknown)
} eeprom_SysInfo_t;
/****************************************************************************
* Struct : eeprom_RfClockCal_t
************************************************************************//**
*
* SuperFemto RF clock calibration.
*
***************************************************************************/
typedef struct eeprom_RfClockCal
{
int iClkCor; ///< Clock correction value in PPB.
uint8_t u8ClkSrc; ///< Clock source (0:None, 1:OCXO, 2:TCXO, 3:External, 4:GPS PPS, 5:reserved, 6:RX, 7:Edge)
} eeprom_RfClockCal_t;
/****************************************************************************
* Struct : eeprom_TxCal_t
************************************************************************//**
*
* SuperFemto transmit calibration table.
*
***************************************************************************/
typedef struct eeprom_TxCal
{
float fTxGainGmsk[80]; ///< Gain setting for GMSK output level from +50dBm to -29 dBm
float fTx8PskCorr; ///< Gain adjustment for 8 PSK (default to +3.25 dB)
float fTxExtAttCorr[31]; ///< Gain adjustment for external attenuator (0:@1dB, 1:@2dB, ..., 31:@32dB)
float fTxRollOffCorr[374]; /**< Gain correction for each ARFCN
for GSM-850 : 0=128, 1:129, ..., 123:251, [124-373]:unused
for GSM-900 : 0=955, 1:956, ..., 70:1, ..., 317:956, [318-373]:unused
for DCS-1800: 0=512, 1:513, ..., 373:885
for PCS-1900: 0=512, 1:513, ..., 298:810, [299-373]:unused */
} eeprom_TxCal_t;
/****************************************************************************
* Struct : eeprom_RxCal_t
************************************************************************//**
*
* SuperFemto receive calibration table.
*
***************************************************************************/
typedef struct eeprom_RxCal
{
float fExtRxGain; ///< External RX gain
float fRxMixGainCorr; ///< Mixer gain error compensation
float fRxLnaGainCorr[3]; ///< LNA gain error compensation (1:@-12 dB, 2:@-24 dB, 3:@-36 dB)
float fRxRollOffCorr[374]; /***< Frequency roll-off compensation
for GSM-850 : 0=128, 1:129, ..., 123:251, [124-373]:unused
for GSM-900 : 0=955, 1:956, ..., 70:1, ..., 317:956, [318-373]:unused
for DCS-1800: 0=512, 1:513, ..., 373:885
for PCS-1900: 0=512, 1:513, ..., 298:810, [299-373]:unused */
uint8_t u8IqImbalMode; ///< IQ imbalance mode (0:off, 1:on, 2:auto)
uint16_t u16IqImbalCorr[4]; ///< IQ imbalance compensation
} eeprom_RxCal_t;
/****************************************************************************
* Public functions *
****************************************************************************/
eeprom_Error_t eeprom_ReadEthAddr( uint8_t *ethaddr );
/****************************************************************************
* Function : eeprom_ResetCfg
************************************************************************//**
*
* This function reset the content of the EEPROM config area.
*
* @return
* 0 if or an error core.
*
****************************************************************************/
eeprom_Error_t eeprom_ResetCfg( void );
/****************************************************************************
* Function : eeprom_ReadSysInfo
************************************************************************//**
*
* This function reads the system information from the EEPROM.
*
* @param [inout] pTime
* Pointer to a system info structure.
*
* @param [inout] pSysInfo
* Pointer to a system info structure.
*
* @return
* 0 if or an error core.
*
****************************************************************************/
eeprom_Error_t eeprom_ReadSysInfo( eeprom_SysInfo_t *pSysInfo );
/****************************************************************************
* Function : eeprom_WriteSysInfo
************************************************************************//**
*
* This function writes the system information to the EEPROM.
*
* @param [in] pSysInfo
* Pointer to the system info structure to be written.
*
* @return
* 0 if or an error core.
*
****************************************************************************/
eeprom_Error_t eeprom_WriteSysInfo( const eeprom_SysInfo_t *pSysInfo );
/****************************************************************************
* Function : eeprom_ReadRfClockCal
************************************************************************//**
*
* This function reads the RF clock calibration data from the EEPROM.
*
* @param [inout] pRfClockCal
* Pointer to a RF clock calibration structure.
*
* @return
* 0 if or an error core.
*
****************************************************************************/
eeprom_Error_t eeprom_ReadRfClockCal( eeprom_RfClockCal_t *pRfClockCal );
/****************************************************************************
* Function : eeprom_WriteRfClockCal
************************************************************************//**
*
* This function writes the RF clock calibration data to the EEPROM.
*
* @param [in] pSysInfo
* Pointer to the system info structure to be written.
*
* @return
* 0 if or an error core.
*
****************************************************************************/
eeprom_Error_t eeprom_WriteRfClockCal( const eeprom_RfClockCal_t *pRfClockCal );
/****************************************************************************
* Function : eeprom_ReadTxCal
************************************************************************//**
*
* This function reads the TX calibration tables for the specified band from
* the EEPROM.
*
* @param [in] iBand
* GSM band (0:GSM-850, 1:GSM-900, 2:DCS-1800, 3:PCS-1900).
*
* @param [inout] pTxCal
* Pointer to a TX calibration table structure.
*
* @return
* 0 if or an error core.
*
****************************************************************************/
eeprom_Error_t eeprom_ReadTxCal( int iBand, eeprom_TxCal_t *pTxCal );
/****************************************************************************
* Function : eeprom_WriteTxCal
************************************************************************//**
*
* This function writes the TX calibration tables for the specified band to
* the EEPROM.
*
* @param [in] iBand
* GSM band (0:GSM-850, 1:GSM-900, 2:DCS-1800, 3:PCS-1900).
*
* @param [in] pTxCal
* Pointer to a TX calibration table structure.
*
* @return
* 0 if or an error core.
*
****************************************************************************/
eeprom_Error_t eeprom_WriteTxCal( int iBand, const eeprom_TxCal_t *pTxCal );
/****************************************************************************
* Function : eeprom_ReadRxCal
************************************************************************//**
*
* This function reads the RX calibration tables for the specified band from
* the EEPROM.
*
* @param [in] iBand
* GSM band (0:GSM-850, 1:GSM-900, 2:DCS-1800, 3:PCS-1900).
*
* @param [in] iUplink
* Uplink flag (0:downlink, X:downlink).
*
* @param [inout] pRxCal
* Pointer to a RX calibration table structure.
*
* @return
* 0 if or an error core.
*
****************************************************************************/
eeprom_Error_t eeprom_ReadRxCal( int iBand, int iUplink, eeprom_RxCal_t *pRxCal );
/****************************************************************************
* Function : eeprom_WriteRxCal
************************************************************************//**
*
* This function writes the RX calibration tables for the specified band to
* the EEPROM.
*
* @param [in] iBand
* GSM band (0:GSM-850, 1:GSM-900, 2:DCS-1800, 3:PCS-1900).
*
* @param [in] iUplink
* Uplink flag (0:downlink, X:downlink).
*
* @param [in] pRxCal
* Pointer to a RX calibration table structure.
*
* @return
* 0 if or an error core.
*
****************************************************************************/
eeprom_Error_t eeprom_WriteRxCal( int iBand, int iUplink, const eeprom_RxCal_t *pRxCal );
void eeprom_free_resources(void);
#endif // EEPROM_H__

View File

@@ -19,7 +19,7 @@
* *
*/ */
#include <sysmocom/femtobts/femtobts.h> #include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1const.h> #include <sysmocom/femtobts/gsml1const.h>
#include <sysmocom/femtobts/gsml1dbg.h> #include <sysmocom/femtobts/gsml1dbg.h>
@@ -91,51 +91,89 @@ const GsmL1_PrimId_t femtobts_l1prim_req2conf[GsmL1_PrimId_NUM] = {
[GsmL1_PrimId_MphMeasureReq] = GsmL1_PrimId_MphMeasureCnf, [GsmL1_PrimId_MphMeasureReq] = GsmL1_PrimId_MphMeasureCnf,
}; };
const enum l1prim_type femtobts_sysprim_type[FemtoBts_PrimId_NUM] = { const enum l1prim_type femtobts_sysprim_type[SuperFemto_PrimId_NUM] = {
[FemtoBts_PrimId_SystemInfoReq] = L1P_T_REQ, [SuperFemto_PrimId_SystemInfoReq] = L1P_T_REQ,
[FemtoBts_PrimId_SystemInfoCnf] = L1P_T_CONF, [SuperFemto_PrimId_SystemInfoCnf] = L1P_T_CONF,
[FemtoBts_PrimId_SystemFailureInd] = L1P_T_IND, [SuperFemto_PrimId_SystemFailureInd] = L1P_T_IND,
[FemtoBts_PrimId_ActivateRfReq] = L1P_T_REQ, [SuperFemto_PrimId_ActivateRfReq] = L1P_T_REQ,
[FemtoBts_PrimId_ActivateRfCnf] = L1P_T_CONF, [SuperFemto_PrimId_ActivateRfCnf] = L1P_T_CONF,
[FemtoBts_PrimId_DeactivateRfReq] = L1P_T_REQ, [SuperFemto_PrimId_DeactivateRfReq] = L1P_T_REQ,
[FemtoBts_PrimId_DeactivateRfCnf] = L1P_T_CONF, [SuperFemto_PrimId_DeactivateRfCnf] = L1P_T_CONF,
[FemtoBts_PrimId_SetTraceFlagsReq] = L1P_T_REQ, [SuperFemto_PrimId_SetTraceFlagsReq] = L1P_T_REQ,
[FemtoBts_PrimId_RfClockInfoReq] = L1P_T_REQ, [SuperFemto_PrimId_RfClockInfoReq] = L1P_T_REQ,
[FemtoBts_PrimId_RfClockInfoCnf] = L1P_T_CONF, [SuperFemto_PrimId_RfClockInfoCnf] = L1P_T_CONF,
[FemtoBts_PrimId_RfClockSetupReq] = L1P_T_REQ, [SuperFemto_PrimId_RfClockSetupReq] = L1P_T_REQ,
[FemtoBts_PrimId_RfClockSetupCnf] = L1P_T_CONF, [SuperFemto_PrimId_RfClockSetupCnf] = L1P_T_CONF,
[FemtoBts_PrimId_Layer1ResetReq] = L1P_T_REQ, [SuperFemto_PrimId_Layer1ResetReq] = L1P_T_REQ,
[FemtoBts_PrimId_Layer1ResetCnf] = L1P_T_CONF, [SuperFemto_PrimId_Layer1ResetCnf] = L1P_T_CONF,
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
[SuperFemto_PrimId_GetTxCalibTblReq] = L1P_T_REQ,
[SuperFemto_PrimId_GetTxCalibTblCnf] = L1P_T_CONF,
[SuperFemto_PrimId_SetTxCalibTblReq] = L1P_T_REQ,
[SuperFemto_PrimId_SetTxCalibTblCnf] = L1P_T_CONF,
[SuperFemto_PrimId_GetRxCalibTblReq] = L1P_T_REQ,
[SuperFemto_PrimId_GetRxCalibTblCnf] = L1P_T_CONF,
[SuperFemto_PrimId_SetRxCalibTblReq] = L1P_T_REQ,
[SuperFemto_PrimId_SetRxCalibTblCnf] = L1P_T_CONF,
#endif
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(3,6,0)
[SuperFemto_PrimId_MuteRfReq] = L1P_T_REQ,
[SuperFemto_PrimId_MuteRfCnf] = L1P_T_CONF,
#endif
}; };
const struct value_string femtobts_sysprim_names[FemtoBts_PrimId_NUM+1] = { const struct value_string femtobts_sysprim_names[SuperFemto_PrimId_NUM+1] = {
{ FemtoBts_PrimId_SystemInfoReq, "SYSTEM-INFO.req" }, { SuperFemto_PrimId_SystemInfoReq, "SYSTEM-INFO.req" },
{ FemtoBts_PrimId_SystemInfoCnf, "SYSTEM-INFO.conf" }, { SuperFemto_PrimId_SystemInfoCnf, "SYSTEM-INFO.conf" },
{ FemtoBts_PrimId_SystemFailureInd, "SYSTEM-FAILURE.ind" }, { SuperFemto_PrimId_SystemFailureInd, "SYSTEM-FAILURE.ind" },
{ FemtoBts_PrimId_ActivateRfReq, "ACTIVATE-RF.req" }, { SuperFemto_PrimId_ActivateRfReq, "ACTIVATE-RF.req" },
{ FemtoBts_PrimId_ActivateRfCnf, "ACTIVATE-RF.conf" }, { SuperFemto_PrimId_ActivateRfCnf, "ACTIVATE-RF.conf" },
{ FemtoBts_PrimId_DeactivateRfReq, "DEACTIVATE-RF.req" }, { SuperFemto_PrimId_DeactivateRfReq, "DEACTIVATE-RF.req" },
{ FemtoBts_PrimId_DeactivateRfCnf, "DEACTIVATE-RF.conf" }, { SuperFemto_PrimId_DeactivateRfCnf, "DEACTIVATE-RF.conf" },
{ FemtoBts_PrimId_SetTraceFlagsReq, "SET-TRACE-FLAGS.req" }, { SuperFemto_PrimId_SetTraceFlagsReq, "SET-TRACE-FLAGS.req" },
{ FemtoBts_PrimId_RfClockInfoReq, "RF-CLOCK-INFO.req" }, { SuperFemto_PrimId_RfClockInfoReq, "RF-CLOCK-INFO.req" },
{ FemtoBts_PrimId_RfClockInfoCnf, "RF-CLOCK-INFO.conf" }, { SuperFemto_PrimId_RfClockInfoCnf, "RF-CLOCK-INFO.conf" },
{ FemtoBts_PrimId_RfClockSetupReq, "RF-CLOCK-SETUP.req" }, { SuperFemto_PrimId_RfClockSetupReq, "RF-CLOCK-SETUP.req" },
{ FemtoBts_PrimId_RfClockSetupCnf, "RF-CLOCK-SETUP.conf" }, { SuperFemto_PrimId_RfClockSetupCnf, "RF-CLOCK-SETUP.conf" },
{ FemtoBts_PrimId_Layer1ResetReq, "LAYER1-RESET.req" }, { SuperFemto_PrimId_Layer1ResetReq, "LAYER1-RESET.req" },
{ FemtoBts_PrimId_Layer1ResetCnf, "LAYER1-RESET.conf" }, { SuperFemto_PrimId_Layer1ResetCnf, "LAYER1-RESET.conf" },
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
{ SuperFemto_PrimId_GetTxCalibTblReq, "GET-TX-CALIB.req" },
{ SuperFemto_PrimId_GetTxCalibTblCnf, "GET-TX-CALIB.cnf" },
{ SuperFemto_PrimId_SetTxCalibTblReq, "SET-TX-CALIB.req" },
{ SuperFemto_PrimId_SetTxCalibTblCnf, "SET-TX-CALIB.cnf" },
{ SuperFemto_PrimId_GetRxCalibTblReq, "GET-RX-CALIB.req" },
{ SuperFemto_PrimId_GetRxCalibTblCnf, "GET-RX-CALIB.cnf" },
{ SuperFemto_PrimId_SetRxCalibTblReq, "SET-RX-CALIB.req" },
{ SuperFemto_PrimId_SetRxCalibTblCnf, "SET-RX-CALIB.cnf" },
#endif
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(3,6,0)
{ SuperFemto_PrimId_MuteRfReq, "MUTE-RF.req" },
{ SuperFemto_PrimId_MuteRfCnf, "MUTE-RF.cnf" },
#endif
{ 0, NULL } { 0, NULL }
}; };
const FemtoBts_PrimId_t femtobts_sysprim_req2conf[FemtoBts_PrimId_NUM] = { const SuperFemto_PrimId_t femtobts_sysprim_req2conf[SuperFemto_PrimId_NUM] = {
[FemtoBts_PrimId_SystemInfoReq] = FemtoBts_PrimId_SystemInfoCnf, [SuperFemto_PrimId_SystemInfoReq] = SuperFemto_PrimId_SystemInfoCnf,
[FemtoBts_PrimId_ActivateRfReq] = FemtoBts_PrimId_ActivateRfCnf, [SuperFemto_PrimId_ActivateRfReq] = SuperFemto_PrimId_ActivateRfCnf,
[FemtoBts_PrimId_DeactivateRfReq] = FemtoBts_PrimId_DeactivateRfCnf, [SuperFemto_PrimId_DeactivateRfReq] = SuperFemto_PrimId_DeactivateRfCnf,
[FemtoBts_PrimId_RfClockInfoReq] = FemtoBts_PrimId_RfClockInfoCnf, [SuperFemto_PrimId_RfClockInfoReq] = SuperFemto_PrimId_RfClockInfoCnf,
[FemtoBts_PrimId_RfClockSetupReq] = FemtoBts_PrimId_RfClockSetupCnf, [SuperFemto_PrimId_RfClockSetupReq] = SuperFemto_PrimId_RfClockSetupCnf,
[FemtoBts_PrimId_Layer1ResetReq] = FemtoBts_PrimId_Layer1ResetCnf, [SuperFemto_PrimId_Layer1ResetReq] = SuperFemto_PrimId_Layer1ResetCnf,
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
[SuperFemto_PrimId_GetTxCalibTblReq] = SuperFemto_PrimId_GetTxCalibTblCnf,
[SuperFemto_PrimId_SetTxCalibTblReq] = SuperFemto_PrimId_SetTxCalibTblCnf,
[SuperFemto_PrimId_GetRxCalibTblReq] = SuperFemto_PrimId_GetRxCalibTblCnf,
[SuperFemto_PrimId_SetRxCalibTblReq] = SuperFemto_PrimId_SetRxCalibTblCnf,
#endif
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(3,6,0)
[SuperFemto_PrimId_MuteRfReq] = SuperFemto_PrimId_MuteRfCnf,
#endif
}; };
const struct value_string femtobts_l1sapi_names[GsmL1_Sapi_NUM+1] = { const struct value_string femtobts_l1sapi_names[GsmL1_Sapi_NUM+1] = {
{ GsmL1_Sapi_Idle, "IDLE" },
{ GsmL1_Sapi_Fcch, "FCCH" }, { GsmL1_Sapi_Fcch, "FCCH" },
{ GsmL1_Sapi_Sch, "SCH" }, { GsmL1_Sapi_Sch, "SCH" },
{ GsmL1_Sapi_Sacch, "SACCH" }, { GsmL1_Sapi_Sacch, "SACCH" },
@@ -220,10 +258,42 @@ const struct value_string femtobts_tracef_names[29] = {
{ 0, NULL } { 0, NULL }
}; };
const struct value_string femtobts_tracef_docs[29] = {
{ DBG_DEBUG, "Debug Region" },
{ DBG_L1WARNING, "L1 Warning Region" },
{ DBG_ERROR, "Error Region" },
{ DBG_L1RXMSG, "L1_RX_MSG Region" },
{ DBG_L1RXMSGBYTE, "L1_RX_MSG_BYTE Region" },
{ DBG_L1TXMSG, "L1_TX_MSG Region" },
{ DBG_L1TXMSGBYTE, "L1_TX_MSG_BYTE Region" },
{ DBG_MPHCNF, "MphConfirmation Region" },
{ DBG_MPHIND, "MphIndication Region" },
{ DBG_MPHREQ, "MphRequest Region" },
{ DBG_PHIND, "PhIndication Region" },
{ DBG_PHREQ, "PhRequest Region" },
{ DBG_PHYRF, "PhyRF Region" },
{ DBG_PHYRFMSGBYTE, "PhyRF Message Region" },
{ DBG_MODE, "Mode Region" },
{ DBG_TDMAINFO, "TDMA Info Region" },
{ DBG_BADCRC, "Bad CRC Region" },
{ DBG_PHINDBYTE, "PH_IND_BYTE" },
{ DBG_PHREQBYTE, "PH_REQ_BYTE" },
{ DBG_DEVICEMSG, "Device Message Region" },
{ DBG_RACHINFO, "RACH Info" },
{ DBG_LOGCHINFO, "LOG_CH_INFO" },
{ DBG_MEMORY, "Memory Region" },
{ DBG_PROFILING, "Profiling Region" },
{ DBG_TESTCOMMENT, "Test Comments" },
{ DBG_TEST, "Test Region" },
{ DBG_STATUS, "Status Region" },
{ 0, NULL }
};
const struct value_string femtobts_tch_pl_names[] = { const struct value_string femtobts_tch_pl_names[] = {
{ GsmL1_TchPlType_NA, "N/A" }, { GsmL1_TchPlType_NA, "N/A" },
{ GsmL1_TchPlType_Fr, "FR" }, { GsmL1_TchPlType_Fr, "FR" },
{ GsmL1_TchPlType_Hr, "HR" }, { GsmL1_TchPlType_Hr, "HR" },
{ GsmL1_TchPlType_Efr, "EFR" },
{ GsmL1_TchPlType_Amr, "AMR(IF2)" }, { GsmL1_TchPlType_Amr, "AMR(IF2)" },
{ GsmL1_TchPlType_Amr_SidBad, "AMR(SID BAD)" }, { GsmL1_TchPlType_Amr_SidBad, "AMR(SID BAD)" },
{ GsmL1_TchPlType_Amr_Onset, "AMR(ONSET)" }, { GsmL1_TchPlType_Amr_Onset, "AMR(ONSET)" },
@@ -236,3 +306,62 @@ const struct value_string femtobts_tch_pl_names[] = {
{ GsmL1_TchPlType_Amr_RatscchData, "AMR(RATSCCH DATA)" }, { GsmL1_TchPlType_Amr_RatscchData, "AMR(RATSCCH DATA)" },
{ 0, NULL } { 0, NULL }
}; };
const struct value_string femtobts_clksrc_names[] = {
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
{ SuperFemto_ClkSrcId_None, "None" },
{ SuperFemto_ClkSrcId_Ocxo, "ocxo" },
{ SuperFemto_ClkSrcId_Tcxo, "tcxo" },
{ SuperFemto_ClkSrcId_External, "ext" },
{ SuperFemto_ClkSrcId_GpsPps, "gps" },
{ SuperFemto_ClkSrcId_Trx, "trx" },
{ SuperFemto_ClkSrcId_Rx, "rx" },
{ SuperFemto_ClkSrcId_Edge, "edge" },
{ SuperFemto_ClkSrcId_NetList, "nwl" },
#else
{ SF_CLKSRC_NONE, "None" },
{ SF_CLKSRC_OCXO, "ocxo" },
{ SF_CLKSRC_TCXO, "tcxo" },
{ SF_CLKSRC_EXT, "ext" },
{ SF_CLKSRC_GPS, "gps" },
{ SF_CLKSRC_TRX, "trx" },
{ SF_CLKSRC_RX, "rx" },
#endif
{ 0, NULL }
};
const struct value_string femtobts_dir_names[] = {
{ GsmL1_Dir_TxDownlink, "TxDL" },
{ GsmL1_Dir_TxUplink, "TxUL" },
{ GsmL1_Dir_RxUplink, "RxUL" },
{ GsmL1_Dir_RxDownlink, "RxDL" },
{ GsmL1_Dir_TxDownlink|GsmL1_Dir_RxUplink, "BOTH" },
{ 0, NULL }
};
const struct value_string femtobts_chcomb_names[] = {
{ GsmL1_LogChComb_0, "dummy" },
{ GsmL1_LogChComb_I, "tch_f" },
{ GsmL1_LogChComb_II, "tch_h" },
{ GsmL1_LogChComb_IV, "ccch" },
{ GsmL1_LogChComb_V, "ccch_sdcch4" },
{ GsmL1_LogChComb_VII, "sdcch8" },
{ GsmL1_LogChComb_XIII, "pdtch" },
{ 0, NULL }
};
const uint8_t pdch_msu_size[_NUM_PDCH_CS] = {
[PDCH_CS_1] = 23,
[PDCH_CS_2] = 34,
[PDCH_CS_3] = 40,
[PDCH_CS_4] = 54,
[PDCH_MCS_1] = 27,
[PDCH_MCS_2] = 33,
[PDCH_MCS_3] = 42,
[PDCH_MCS_4] = 49,
[PDCH_MCS_5] = 60,
[PDCH_MCS_6] = 78,
[PDCH_MCS_7] = 118,
[PDCH_MCS_8] = 142,
[PDCH_MCS_9] = 154
};

View File

@@ -4,29 +4,107 @@
#include <stdlib.h> #include <stdlib.h>
#include <osmocom/core/utils.h> #include <osmocom/core/utils.h>
#include <sysmocom/femtobts/femtobts.h> #include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1const.h> #include <sysmocom/femtobts/gsml1const.h>
#ifdef FEMTOBTS_API_VERSION
#define SuperFemto_PrimId_t FemtoBts_PrimId_t
#define SuperFemto_Prim_t FemtoBts_Prim_t
#define SuperFemto_PrimId_SystemInfoReq FemtoBts_PrimId_SystemInfoReq
#define SuperFemto_PrimId_SystemInfoCnf FemtoBts_PrimId_SystemInfoCnf
#define SuperFemto_SystemInfoCnf_t FemtoBts_SystemInfoCnf_t
#define SuperFemto_PrimId_SystemFailureInd FemtoBts_PrimId_SystemFailureInd
#define SuperFemto_PrimId_ActivateRfReq FemtoBts_PrimId_ActivateRfReq
#define SuperFemto_PrimId_ActivateRfCnf FemtoBts_PrimId_ActivateRfCnf
#define SuperFemto_PrimId_DeactivateRfReq FemtoBts_PrimId_DeactivateRfReq
#define SuperFemto_PrimId_DeactivateRfCnf FemtoBts_PrimId_DeactivateRfCnf
#define SuperFemto_PrimId_SetTraceFlagsReq FemtoBts_PrimId_SetTraceFlagsReq
#define SuperFemto_PrimId_RfClockInfoReq FemtoBts_PrimId_RfClockInfoReq
#define SuperFemto_PrimId_RfClockInfoCnf FemtoBts_PrimId_RfClockInfoCnf
#define SuperFemto_PrimId_RfClockSetupReq FemtoBts_PrimId_RfClockSetupReq
#define SuperFemto_PrimId_RfClockSetupCnf FemtoBts_PrimId_RfClockSetupCnf
#define SuperFemto_PrimId_Layer1ResetReq FemtoBts_PrimId_Layer1ResetReq
#define SuperFemto_PrimId_Layer1ResetCnf FemtoBts_PrimId_Layer1ResetCnf
#define SuperFemto_PrimId_NUM FemtoBts_PrimId_NUM
#define HW_SYSMOBTS_V1 1
#define SUPERFEMTO_API(x,y,z) FEMTOBTS_API(x,y,z)
#endif
#ifdef L1_HAS_RTP_MODE
/*
* The bit ordering has been fixed on >= 3.10 but I am verifying
* this on 3.11.
*/
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(3, 11, 0)
#define USE_L1_RTP_MODE /* Tell L1 to use RTP mode */
#endif
#endif
/*
* Depending on the firmware version either GsmL1_Prim_t or SuperFemto_Prim_t
* is the bigger struct. For earlier firmware versions the GsmL1_Prim_t was the
* bigger struct.
*/
#define SYSMOBTS_PRIM_SIZE \
(OSMO_MAX(sizeof(SuperFemto_Prim_t), sizeof(GsmL1_Prim_t)) + 128)
enum l1prim_type { enum l1prim_type {
L1P_T_INVALID, /* this must be 0 to detect uninitialized elements */
L1P_T_REQ, L1P_T_REQ,
L1P_T_CONF, L1P_T_CONF,
L1P_T_IND, L1P_T_IND,
}; };
#if !defined(SUPERFEMTO_API_VERSION) || SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,1,0)
enum uperfemto_clk_src {
SF_CLKSRC_NONE = 0,
SF_CLKSRC_OCXO = 1,
SF_CLKSRC_TCXO = 2,
SF_CLKSRC_EXT = 3,
SF_CLKSRC_GPS = 4,
SF_CLKSRC_TRX = 5,
SF_CLKSRC_RX = 6,
SF_CLKSRC_NL = 7,
};
#endif
const enum l1prim_type femtobts_l1prim_type[GsmL1_PrimId_NUM]; const enum l1prim_type femtobts_l1prim_type[GsmL1_PrimId_NUM];
const struct value_string femtobts_l1prim_names[GsmL1_PrimId_NUM+1]; const struct value_string femtobts_l1prim_names[GsmL1_PrimId_NUM+1];
const GsmL1_PrimId_t femtobts_l1prim_req2conf[GsmL1_PrimId_NUM]; const GsmL1_PrimId_t femtobts_l1prim_req2conf[GsmL1_PrimId_NUM];
const enum l1prim_type femtobts_sysprim_type[FemtoBts_PrimId_NUM]; const enum l1prim_type femtobts_sysprim_type[SuperFemto_PrimId_NUM];
const struct value_string femtobts_sysprim_names[FemtoBts_PrimId_NUM+1]; const struct value_string femtobts_sysprim_names[SuperFemto_PrimId_NUM+1];
const FemtoBts_PrimId_t femtobts_sysprim_req2conf[FemtoBts_PrimId_NUM]; const SuperFemto_PrimId_t femtobts_sysprim_req2conf[SuperFemto_PrimId_NUM];
const struct value_string femtobts_l1sapi_names[GsmL1_Sapi_NUM+1]; const struct value_string femtobts_l1sapi_names[GsmL1_Sapi_NUM+1];
const struct value_string femtobts_l1status_names[GSML1_STATUS_NUM+1]; const struct value_string femtobts_l1status_names[GSML1_STATUS_NUM+1];
const struct value_string femtobts_tracef_names[29]; const struct value_string femtobts_tracef_names[29];
const struct value_string femtobts_tracef_docs[29];
const struct value_string femtobts_tch_pl_names[]; const struct value_string femtobts_tch_pl_names[15];
const struct value_string femtobts_clksrc_names[10];
const struct value_string femtobts_dir_names[6];
enum pdch_cs {
PDCH_CS_1,
PDCH_CS_2,
PDCH_CS_3,
PDCH_CS_4,
PDCH_MCS_1,
PDCH_MCS_2,
PDCH_MCS_3,
PDCH_MCS_4,
PDCH_MCS_5,
PDCH_MCS_6,
PDCH_MCS_7,
PDCH_MCS_8,
PDCH_MCS_9,
_NUM_PDCH_CS
};
const uint8_t pdch_msu_size[_NUM_PDCH_CS];
#endif /* FEMTOBTS_H */ #endif /* FEMTOBTS_H */

View File

@@ -0,0 +1,113 @@
/* Misc HW routines for Sysmocom BTS */
/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <osmocom/core/utils.h>
#include "hw_misc.h"
static const struct value_string sysmobts_led_names[] = {
{ LED_RF_ACTIVE, "activity_led" },
{ LED_ONLINE, "online_led" },
{ 0, NULL }
};
int sysmobts_led_set(enum sysmobts_led nr, int on)
{
char tmp[PATH_MAX+1];
const char *filename;
int fd;
uint8_t byte;
if (on)
byte = '1';
else
byte = '0';
filename = get_value_string(sysmobts_led_names, nr);
if (!filename)
return -EINVAL;
snprintf(tmp, sizeof(tmp)-1, "/sys/class/leds/%s/brightness", filename);
tmp[sizeof(tmp)-1] = '\0';
fd = open(tmp, O_WRONLY);
if (fd < 0)
return -ENODEV;
write(fd, &byte, 1);
close(fd);
return 0;
}
#if 0
#define HWMON_PREFIX "/sys/class/hwmon/hwmon0/device"
static FILE *temperature_f[NUM_TEMP];
int sysmobts_temp_init()
{
char tmp[PATH_MAX+1];
FILE *in;
int rc = 0;
for (i = 0; i < NUM_TEMP; i++) {
snprintf(tmp, sizeof(tmp)-1, HWMON_PREFIX "/temp%u_input", i+1),
tmp[sizeof(tmp)-1] = '\0';
temperature_f[i] = fopen(tmp, "r");
if (!temperature_f[i])
rc = -ENODEV;
}
return 0;
}
int sysmobts_temp_get(uint8_t num)
{
if (num >= NUM_TEMP)
return -EINVAL;
if (!temperature_f[num])
return -ENODEV;
in = fopen(tmp, "r");
if (!in)
return -ENODEV;
fclose(tmp);
return 0;
}
#endif

View File

@@ -0,0 +1,12 @@
#ifndef _SYSMOBTS_HW_MISC_H
#define _SYSMOBTS_HW_MISC_H
enum sysmobts_led {
LED_NONE,
LED_RF_ACTIVE,
LED_ONLINE,
};
int sysmobts_led_set(enum sysmobts_led nr, int on);
#endif

View File

@@ -1,3 +1,5 @@
#define L1FWD_L1_PORT 9999 #define L1FWD_L1_PORT 9999
#define L1FWD_SYS_PORT 9998 #define L1FWD_SYS_PORT 9998
#define L1FWD_TCH_PORT 9997
#define L1FWD_PDTCH_PORT 9996

View File

@@ -42,7 +42,7 @@
#include <osmo-bts/logging.h> #include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h> #include <osmo-bts/gsm_data.h>
#include <sysmocom/femtobts/femtobts.h> #include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h> #include <sysmocom/femtobts/gsml1prim.h>
#include <sysmocom/femtobts/gsml1const.h> #include <sysmocom/femtobts/gsml1const.h>
#include <sysmocom/femtobts/gsml1types.h> #include <sysmocom/femtobts/gsml1types.h>
@@ -55,11 +55,15 @@
static const uint16_t fwd_udp_ports[_NUM_MQ_WRITE] = { static const uint16_t fwd_udp_ports[_NUM_MQ_WRITE] = {
[MQ_SYS_READ] = L1FWD_SYS_PORT, [MQ_SYS_READ] = L1FWD_SYS_PORT,
[MQ_L1_READ] = L1FWD_L1_PORT, [MQ_L1_READ] = L1FWD_L1_PORT,
#ifndef HW_SYSMOBTS_V1
[MQ_TCH_READ] = L1FWD_TCH_PORT,
[MQ_PDTCH_READ] = L1FWD_PDTCH_PORT,
#endif
}; };
struct l1fwd_hdl { struct l1fwd_hdl {
struct sockaddr_storage remote_sa; struct sockaddr_storage remote_sa[_NUM_MQ_WRITE];
socklen_t remote_sa_len; socklen_t remote_sa_len[_NUM_MQ_WRITE];
struct osmo_wqueue udp_wq[_NUM_MQ_WRITE]; struct osmo_wqueue udp_wq[_NUM_MQ_WRITE];
@@ -68,12 +72,12 @@ struct l1fwd_hdl {
/* callback when there's a new L1 primitive coming in from the HW */ /* callback when there's a new L1 primitive coming in from the HW */
int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg) int l1if_handle_l1prim(int wq, struct femtol1_hdl *fl1h, struct msgb *msg)
{ {
struct l1fwd_hdl *l1fh = fl1h->priv; struct l1fwd_hdl *l1fh = fl1h->priv;
/* Enqueue message to UDP socket */ /* Enqueue message to UDP socket */
return osmo_wqueue_enqueue(&l1fh->udp_wq[MQ_L1_WRITE], msg); return osmo_wqueue_enqueue(&l1fh->udp_wq[wq], msg);
} }
/* callback when there's a new SYS primitive coming in from the HW */ /* callback when there's a new SYS primitive coming in from the HW */
@@ -89,7 +93,7 @@ int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg)
/* data has arrived on the udp socket */ /* data has arrived on the udp socket */
static int udp_read_cb(struct osmo_fd *ofd) static int udp_read_cb(struct osmo_fd *ofd)
{ {
struct msgb *msg = msgb_alloc_headroom(2048, 128, "udp_rx"); struct msgb *msg = msgb_alloc_headroom(SYSMOBTS_PRIM_SIZE, 128, "udp_rx");
struct l1fwd_hdl *l1fh = ofd->data; struct l1fwd_hdl *l1fh = ofd->data;
struct femtol1_hdl *fl1h = l1fh->fl1h; struct femtol1_hdl *fl1h = l1fh->fl1h;
int rc; int rc;
@@ -99,9 +103,9 @@ static int udp_read_cb(struct osmo_fd *ofd)
msg->l1h = msg->data; msg->l1h = msg->data;
l1fh->remote_sa_len = sizeof(l1fh->remote_sa); l1fh->remote_sa_len[ofd->priv_nr] = sizeof(l1fh->remote_sa[ofd->priv_nr]);
rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0, rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0,
(struct sockaddr *) &l1fh->remote_sa, &l1fh->remote_sa_len); (struct sockaddr *) &l1fh->remote_sa[ofd->priv_nr], &l1fh->remote_sa_len[ofd->priv_nr]);
if (rc < 0) { if (rc < 0) {
perror("read from udp"); perror("read from udp");
msgb_free(msg); msgb_free(msg);
@@ -113,14 +117,11 @@ static int udp_read_cb(struct osmo_fd *ofd)
} }
msgb_put(msg, rc); msgb_put(msg, rc);
DEBUGP(DL1C, "UDP: Received %u bytes for %s queue\n", rc, DEBUGP(DL1C, "UDP: Received %u bytes for queue %d\n", rc,
ofd->priv_nr == MQ_SYS_WRITE ? "SYS" : "L1"); ofd->priv_nr);
/* put the message into the right queue */ /* put the message into the right queue */
if (ofd->priv_nr == MQ_SYS_WRITE) rc = osmo_wqueue_enqueue(&fl1h->write_q[ofd->priv_nr], msg);
rc = osmo_wqueue_enqueue(&fl1h->write_q[MQ_SYS_WRITE], msg);
else
rc = osmo_wqueue_enqueue(&fl1h->write_q[MQ_L1_WRITE], msg);
return rc; return rc;
} }
@@ -131,11 +132,11 @@ static int udp_write_cb(struct osmo_fd *ofd, struct msgb *msg)
int rc; int rc;
struct l1fwd_hdl *l1fh = ofd->data; struct l1fwd_hdl *l1fh = ofd->data;
DEBUGP(DL1C, "UDP: Writing %u bytes for %s queue\n", msgb_l1len(msg), DEBUGP(DL1C, "UDP: Writing %u bytes for queue %d\n", msgb_l1len(msg),
ofd->priv_nr == MQ_SYS_WRITE ? "SYS" : "L1"); ofd->priv_nr);
rc = sendto(ofd->fd, msg->l1h, msgb_l1len(msg), 0, rc = sendto(ofd->fd, msg->l1h, msgb_l1len(msg), 0,
(const struct sockaddr *)&l1fh->remote_sa, l1fh->remote_sa_len); (const struct sockaddr *)&l1fh->remote_sa[ofd->priv_nr], l1fh->remote_sa_len[ofd->priv_nr]);
if (rc < 0) { if (rc < 0) {
LOGP(DL1C, LOGL_ERROR, "error writing to L1 msg_queue: %s\n", LOGP(DL1C, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
strerror(errno)); strerror(errno));
@@ -155,19 +156,32 @@ int main(int argc, char **argv)
struct femtol1_hdl *fl1h; struct femtol1_hdl *fl1h;
int rc, i; int rc, i;
printf("sizeof(GsmL1_Prim_t) = %lu\n", sizeof(GsmL1_Prim_t)); printf("sizeof(GsmL1_Prim_t) = %zu\n", sizeof(GsmL1_Prim_t));
printf("sizeof(FemtoBts_Prim_t) = %lu\n", sizeof(FemtoBts_Prim_t)); printf("sizeof(SuperFemto_Prim_t) = %zu\n", sizeof(SuperFemto_Prim_t));
bts_log_init(NULL); bts_log_init(NULL);
/*
* hack and prevent that two l1fwd-proxy/sysmobts run at the same
* time. This is done by binding to the same VTY port.
*/
rc = osmo_sock_init(AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP,
"127.0.0.1", 4241, OSMO_SOCK_F_BIND);
if (rc < 0) {
fprintf(stderr, "Failed to bind to the BTS VTY port.\n");
return EXIT_FAILURE;
}
/* allocate new femtol1_handle */ /* allocate new femtol1_handle */
fl1h = talloc_zero(NULL, struct femtol1_hdl); fl1h = talloc_zero(NULL, struct femtol1_hdl);
INIT_LLIST_HEAD(&fl1h->wlc_list); INIT_LLIST_HEAD(&fl1h->wlc_list);
/* open the actual hardware transport */ /* open the actual hardware transport */
rc = l1if_transport_open(fl1h); for (i = 0; i < ARRAY_SIZE(fl1h->write_q); i++) {
if (rc < 0) rc = l1if_transport_open(i, fl1h);
exit(1); if (rc < 0)
exit(1);
}
/* create our fwd handle */ /* create our fwd handle */
l1fh = talloc_zero(NULL, struct l1fwd_hdl); l1fh = talloc_zero(NULL, struct l1fwd_hdl);
@@ -176,7 +190,7 @@ int main(int argc, char **argv)
fl1h->priv = l1fh; fl1h->priv = l1fh;
/* Open UDP */ /* Open UDP */
for (i = 0; i < 2; i++) { for (i = 0; i < ARRAY_SIZE(l1fh->udp_wq); i++) {
struct osmo_wqueue *wq = &l1fh->udp_wq[i]; struct osmo_wqueue *wq = &l1fh->udp_wq[i];
osmo_wqueue_init(wq, 10); osmo_wqueue_init(wq, 10);

File diff suppressed because it is too large Load Diff

View File

@@ -3,26 +3,59 @@
#include <osmocom/core/select.h> #include <osmocom/core/select.h>
#include <osmocom/core/write_queue.h> #include <osmocom/core/write_queue.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsm_utils.h> #include <osmocom/gsm/gsm_utils.h>
#include <sysmocom/femtobts/gsml1prim.h>
enum { enum {
MQ_SYS_READ, MQ_SYS_READ,
MQ_L1_READ, MQ_L1_READ,
#ifndef HW_SYSMOBTS_V1
MQ_TCH_READ,
MQ_PDTCH_READ,
#endif
_NUM_MQ_READ _NUM_MQ_READ
}; };
enum { enum {
MQ_SYS_WRITE, MQ_SYS_WRITE,
MQ_L1_WRITE, MQ_L1_WRITE,
#ifndef HW_SYSMOBTS_V1
MQ_TCH_WRITE,
MQ_PDTCH_WRITE,
#endif
_NUM_MQ_WRITE _NUM_MQ_WRITE
}; };
struct calib_send_state {
const char *path;
int last_file_idx;
};
enum {
FIXUP_UNITILIAZED,
FIXUP_NEEDED,
FIXUP_NOT_NEEDED,
};
struct femtol1_hdl { struct femtol1_hdl {
struct gsm_time gsm_time; struct gsm_time gsm_time;
uint32_t hLayer1; /* handle to the L1 instance in the DSP */ uint32_t hLayer1; /* handle to the L1 instance in the DSP */
uint32_t dsp_trace_f; 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 llist_head wlc_list;
struct gsmtap_inst *gsmtap;
uint32_t gsmtap_sapi_mask;
void *priv; /* user reference */ void *priv; /* user reference */
struct osmo_timer_list alive_timer; struct osmo_timer_list alive_timer;
@@ -30,31 +63,73 @@ struct femtol1_hdl {
struct osmo_fd read_ofd[_NUM_MQ_READ]; /* osmo file descriptors */ struct osmo_fd read_ofd[_NUM_MQ_READ]; /* osmo file descriptors */
struct osmo_wqueue write_q[_NUM_MQ_WRITE]; struct osmo_wqueue write_q[_NUM_MQ_WRITE];
struct {
/* from DSP/FPGA after L1 Init */
uint8_t dsp_version[3];
uint8_t fpga_version[3];
uint32_t band_support; /* bitmask of GSM_BAND_* */
uint8_t ver_major;
uint8_t ver_minor;
/* from EEPROM */
uint16_t model_nr;
uint16_t model_flags;
uint8_t trx_nr;
} hw_info;
int fixup_needed;
struct calib_send_state st;
uint8_t last_rf_mute[8];
}; };
#define msgb_l1prim(msg) ((GsmL1_Prim_t *)(msg)->l1h) #define msgb_l1prim(msg) ((GsmL1_Prim_t *)(msg)->l1h)
#define msgb_sysprim(msg) ((FemtoBts_Prim_t *)(msg)->l1h) #define msgb_sysprim(msg) ((SuperFemto_Prim_t *)(msg)->l1h)
typedef int l1if_compl_cb(struct msgb *l1_msg, void *data); typedef int l1if_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg, void *data);
/* send a request primitive to the L1 and schedule completion call-back */ /* send a request primitive to the L1 and schedule completion call-back */
int l1if_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg, int l1if_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
int is_system_prim, l1if_compl_cb *cb, void *data); l1if_compl_cb *cb, void *cb_data);
int l1if_gsm_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
l1if_compl_cb *cb, void *cb_data);
struct femtol1_hdl *l1if_open(void *priv); struct femtol1_hdl *l1if_open(void *priv);
int l1if_close(struct femtol1_hdl *hdl); int l1if_close(struct femtol1_hdl *hdl);
int l1if_reset(struct femtol1_hdl *hdl); int l1if_reset(struct femtol1_hdl *hdl);
int l1if_activate_rf(struct femtol1_hdl *hdl, int on); int l1if_activate_rf(struct femtol1_hdl *hdl, int on);
int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags); int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags);
int l1if_set_txpower(struct femtol1_hdl *fl1h, float tx_power);
int l1if_mute_rf(struct femtol1_hdl *hdl, uint8_t mute[8], l1if_compl_cb *cb);
struct msgb *l1p_msgb_alloc(void); struct msgb *l1p_msgb_alloc(void);
struct msgb *sysp_msgb_alloc(void); struct msgb *sysp_msgb_alloc(void);
uint32_t l1if_lchan_to_hLayer2(struct gsm_lchan *lchan); uint32_t l1if_lchan_to_hLayer(struct gsm_lchan *lchan);
struct gsm_lchan *l1if_hLayer2_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer2); struct gsm_lchan *l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer);
/* tch.c */ /* tch.c */
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg); int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer); int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
/* ciphering */
int l1if_set_ciphering(struct femtol1_hdl *fl1h,
struct gsm_lchan *lchan,
int dir_downlink);
/* calibration loading */
int calib_load(struct femtol1_hdl *fl1h);
/* on-line re-calibration */
int l1if_rf_clock_info_reset(struct femtol1_hdl *fl1h);
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);
#endif /* _FEMTO_L1_H */ #endif /* _FEMTO_L1_H */

View File

@@ -4,11 +4,11 @@
#include <osmocom/core/msgb.h> #include <osmocom/core/msgb.h>
/* functions a transport calls on arrival of primitive from BTS */ /* functions a transport calls on arrival of primitive from BTS */
int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg); int l1if_handle_l1prim(int wq, struct femtol1_hdl *fl1h, struct msgb *msg);
int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg); int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg);
/* functions exported by a transport */ /* functions exported by a transport */
int l1if_transport_open(struct femtol1_hdl *fl1h); int l1if_transport_open(int q, struct femtol1_hdl *fl1h);
int l1if_transport_close(struct femtol1_hdl *fl1h); int l1if_transport_close(int q, struct femtol1_hdl *fl1h);
#endif /* _FEMTOL1_TRANSP_H */ #endif /* _FEMTOL1_TRANSP_H */

View File

@@ -43,7 +43,7 @@
#include <osmo-bts/logging.h> #include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h> #include <osmo-bts/gsm_data.h>
#include <sysmocom/femtobts/femtobts.h> #include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h> #include <sysmocom/femtobts/gsml1prim.h>
#include <sysmocom/femtobts/gsml1const.h> #include <sysmocom/femtobts/gsml1const.h>
#include <sysmocom/femtobts/gsml1types.h> #include <sysmocom/femtobts/gsml1types.h>
@@ -56,11 +56,15 @@
static const uint16_t fwd_udp_ports[] = { static const uint16_t fwd_udp_ports[] = {
[MQ_SYS_WRITE] = L1FWD_SYS_PORT, [MQ_SYS_WRITE] = L1FWD_SYS_PORT,
[MQ_L1_WRITE] = L1FWD_L1_PORT, [MQ_L1_WRITE] = L1FWD_L1_PORT,
#ifndef HW_SYSMOBTS_V1
[MQ_TCH_WRITE] = L1FWD_TCH_PORT,
[MQ_PDTCH_WRITE]= L1FWD_PDTCH_PORT,
#endif
}; };
static int fwd_read_cb(struct osmo_fd *ofd) static int fwd_read_cb(struct osmo_fd *ofd)
{ {
struct msgb *msg = msgb_alloc_headroom(2048, 127, "udp_rx"); struct msgb *msg = msgb_alloc_headroom(SYSMOBTS_PRIM_SIZE, 128, "udp_rx");
struct femtol1_hdl *fl1h = ofd->data; struct femtol1_hdl *fl1h = ofd->data;
int rc; int rc;
@@ -83,7 +87,7 @@ static int fwd_read_cb(struct osmo_fd *ofd)
if (ofd->priv_nr == MQ_SYS_WRITE) if (ofd->priv_nr == MQ_SYS_WRITE)
rc = l1if_handle_sysprim(fl1h, msg); rc = l1if_handle_sysprim(fl1h, msg);
else else
rc = l1if_handle_l1prim(fl1h, msg); rc = l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
return rc; return rc;
} }
@@ -94,55 +98,55 @@ static int prim_write_cb(struct osmo_fd *ofd, struct msgb *msg)
return write(ofd->fd, msg->head, msg->len); return write(ofd->fd, msg->head, msg->len);
} }
int l1if_transport_open(struct femtol1_hdl *fl1h) int l1if_transport_open(int q, struct femtol1_hdl *fl1h)
{ {
int rc, i; int rc;
char *bts_host = getenv("L1FWD_BTS_HOST"); char *bts_host = getenv("L1FWD_BTS_HOST");
printf("sizeof(GsmL1_Prim_t) = %lu\n", sizeof(GsmL1_Prim_t)); switch (q) {
printf("sizeof(FemtoBts_Prim_t) = %lu\n", sizeof(FemtoBts_Prim_t)); case MQ_L1_WRITE:
LOGP(DL1C, LOGL_INFO, "sizeof(GsmL1_Prim_t) = %zu\n",
sizeof(GsmL1_Prim_t));
break;
case MQ_SYS_WRITE:
LOGP(DL1C, LOGL_INFO, "sizeof(SuperFemto_Prim_t) = %zu\n",
sizeof(SuperFemto_Prim_t));
break;
}
if (!bts_host) { if (!bts_host) {
fprintf(stderr, "You have to set the L1FWD_BTS_HOST environment variable\n"); fprintf(stderr, "You have to set the L1FWD_BTS_HOST environment variable\n");
exit(2); exit(2);
} }
for (i = 0; i < ARRAY_SIZE(fl1h->write_q); i++) { struct osmo_wqueue *wq = &fl1h->write_q[q];
struct osmo_wqueue *wq = &fl1h->write_q[i]; struct osmo_fd *ofd = &wq->bfd;
struct osmo_fd *ofd = &wq->bfd;
osmo_wqueue_init(wq, 10); osmo_wqueue_init(wq, 10);
wq->write_cb = prim_write_cb; wq->write_cb = prim_write_cb;
wq->read_cb = fwd_read_cb; wq->read_cb = fwd_read_cb;
ofd->data = fl1h; ofd->data = fl1h;
ofd->priv_nr = i; ofd->priv_nr = q;
ofd->when |= BSC_FD_READ; ofd->when |= BSC_FD_READ;
rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
bts_host, fwd_udp_ports[i], bts_host, fwd_udp_ports[q],
OSMO_SOCK_F_CONNECT); OSMO_SOCK_F_CONNECT);
if (rc < 0) { if (rc < 0)
talloc_free(fl1h); return rc;
return rc;
}
}
return 0; return 0;
} }
int l1if_transport_close(struct femtol1_hdl *fl1h) int l1if_transport_close(int q, struct femtol1_hdl *fl1h)
{ {
int i; struct osmo_wqueue *wq = &fl1h->write_q[q];
struct osmo_fd *ofd = &wq->bfd;
for (i = 0; i < ARRAY_SIZE(fl1h->write_q); i++) { osmo_wqueue_clear(wq);
struct osmo_wqueue *wq = &fl1h->write_q[i]; osmo_fd_unregister(ofd);
struct osmo_fd *ofd = &wq->bfd; close(ofd->fd);
osmo_wqueue_clear(wq);
osmo_fd_unregister(ofd);
close(ofd->fd);
}
return 0; return 0;
} }

View File

@@ -19,6 +19,7 @@
* *
*/ */
#include <assert.h>
#include <stdint.h> #include <stdint.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
@@ -36,7 +37,7 @@
#include <osmo-bts/logging.h> #include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h> #include <osmo-bts/gsm_data.h>
#include <sysmocom/femtobts/femtobts.h> #include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h> #include <sysmocom/femtobts/gsml1prim.h>
#include <sysmocom/femtobts/gsml1const.h> #include <sysmocom/femtobts/gsml1const.h>
#include <sysmocom/femtobts/gsml1types.h> #include <sysmocom/femtobts/gsml1types.h>
@@ -46,45 +47,188 @@
#include "l1_transp.h" #include "l1_transp.h"
#ifdef HW_SYSMOBTS_V1
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/femtobts_dsp2arm" #define DEV_SYS_DSP2ARM_NAME "/dev/msgq/femtobts_dsp2arm"
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/femtobts_arm2dsp" #define DEV_SYS_ARM2DSP_NAME "/dev/msgq/femtobts_arm2dsp"
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_dsp2arm" #define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_dsp2arm"
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_arm2dsp" #define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_arm2dsp"
#else
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/superfemto_dsp2arm"
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/superfemto_arm2dsp"
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_sig_dsp2arm"
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_sig_arm2dsp"
#define DEV_TCH_DSP2ARM_NAME "/dev/msgq/gsml1_tch_dsp2arm"
#define DEV_TCH_ARM2DSP_NAME "/dev/msgq/gsml1_tch_arm2dsp"
#define DEV_PDTCH_DSP2ARM_NAME "/dev/msgq/gsml1_pdtch_dsp2arm"
#define DEV_PDTCH_ARM2DSP_NAME "/dev/msgq/gsml1_pdtch_arm2dsp"
#endif
static const char *rd_devnames[] = { static const char *rd_devnames[] = {
[MQ_SYS_READ] = DEV_SYS_DSP2ARM_NAME, [MQ_SYS_READ] = DEV_SYS_DSP2ARM_NAME,
[MQ_L1_READ] = DEV_L1_DSP2ARM_NAME, [MQ_L1_READ] = DEV_L1_DSP2ARM_NAME,
#ifndef HW_SYSMOBTS_V1
[MQ_TCH_READ] = DEV_TCH_DSP2ARM_NAME,
[MQ_PDTCH_READ] = DEV_PDTCH_DSP2ARM_NAME,
#endif
}; };
static const char *wr_devnames[] = { static const char *wr_devnames[] = {
[MQ_SYS_WRITE] = DEV_SYS_ARM2DSP_NAME, [MQ_SYS_WRITE] = DEV_SYS_ARM2DSP_NAME,
[MQ_L1_WRITE] = DEV_L1_ARM2DSP_NAME, [MQ_L1_WRITE] = DEV_L1_ARM2DSP_NAME,
#ifndef HW_SYSMOBTS_V1
[MQ_TCH_WRITE] = DEV_TCH_ARM2DSP_NAME,
[MQ_PDTCH_WRITE]= DEV_PDTCH_ARM2DSP_NAME,
#endif
}; };
/*
* Make sure that all structs we read fit into the SYSMOBTS_PRIM_SIZE
*/
osmo_static_assert(sizeof(GsmL1_Prim_t) + 128 <= SYSMOBTS_PRIM_SIZE, l1_prim)
osmo_static_assert(sizeof(SuperFemto_Prim_t) + 128 <= SYSMOBTS_PRIM_SIZE, super_prim)
static int wqueue_vector_cb(struct osmo_fd *fd, unsigned int what)
{
struct osmo_wqueue *queue;
queue = container_of(fd, struct osmo_wqueue, bfd);
if (what & BSC_FD_READ)
queue->read_cb(fd);
if (what & BSC_FD_EXCEPT)
queue->except_cb(fd);
if (what & BSC_FD_WRITE) {
struct iovec iov[5];
struct msgb *msg, *tmp;
int written, count = 0;
fd->when &= ~BSC_FD_WRITE;
llist_for_each_entry(msg, &queue->msg_queue, list) {
/* more writes than we have */
if (count >= ARRAY_SIZE(iov))
break;
iov[count].iov_base = msg->l1h;
iov[count].iov_len = msgb_l1len(msg);
count += 1;
}
/* TODO: check if all lengths are the same. */
/* Nothing scheduled? This should not happen. */
if (count == 0) {
if (!llist_empty(&queue->msg_queue))
fd->when |= BSC_FD_WRITE;
return 0;
}
written = writev(fd->fd, iov, count);
if (written < 0) {
/* nothing written?! */
if (!llist_empty(&queue->msg_queue))
fd->when |= BSC_FD_WRITE;
return 0;
}
/* now delete the written entries */
written = written / iov[0].iov_len;
count = 0;
llist_for_each_entry_safe(msg, tmp, &queue->msg_queue, list) {
queue->current_length -= 1;
llist_del(&msg->list);
msgb_free(msg);
count += 1;
if (count >= written)
break;
}
if (!llist_empty(&queue->msg_queue))
fd->when |= BSC_FD_WRITE;
}
return 0;
}
static int prim_size_for_queue(int queue)
{
switch (queue) {
case MQ_SYS_WRITE:
return sizeof(SuperFemto_Prim_t);
case MQ_L1_WRITE:
#ifndef HW_SYSMOBTS_V1
case MQ_TCH_WRITE:
case MQ_PDTCH_WRITE:
#endif
return sizeof(GsmL1_Prim_t);
default:
/* The compiler can't know that priv_nr is an enum. Assist. */
LOGP(DL1C, LOGL_FATAL, "writing on a wrong queue: %d\n",
queue);
assert(false);
break;
}
}
/* callback when there's something to read from the l1 msg_queue */ /* callback when there's something to read from the l1 msg_queue */
static int read_dispatch_one(struct femtol1_hdl *fl1h, struct msgb *msg, int queue)
{
switch (queue) {
case MQ_SYS_WRITE:
return l1if_handle_sysprim(fl1h, msg);
case MQ_L1_WRITE:
#ifndef HW_SYSMOBTS_V1
case MQ_TCH_WRITE:
case MQ_PDTCH_WRITE:
#endif
return l1if_handle_l1prim(queue, fl1h, msg);
default:
/* The compiler can't know that priv_nr is an enum. Assist. */
LOGP(DL1C, LOGL_FATAL, "writing on a wrong queue: %d\n",
queue);
assert(false);
break;
}
};
static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what) static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
{ {
//struct msgb *msg = l1p_msgb_alloc(); int i, rc;
struct msgb *msg = msgb_alloc_headroom(2048, 128, "1l_fd");
struct femtol1_hdl *fl1h = ofd->data;
int rc;
msg->l1h = msg->data; const uint32_t prim_size = prim_size_for_queue(ofd->priv_nr);
rc = read(ofd->fd, msg->l1h, sizeof(GsmL1_Prim_t)); uint32_t count;
if (rc < 0) {
if (rc != -1) struct iovec iov[3];
LOGP(DL1C, LOGL_ERROR, "error reading from L1 msg_queue: %s\n", struct msgb *msg[ARRAY_SIZE(iov)];
strerror(errno));
msgb_free(msg); for (i = 0; i < ARRAY_SIZE(iov); ++i) {
return rc; msg[i] = msgb_alloc_headroom(prim_size + 128, 128, "1l_fd");
msg[i]->l1h = msg[i]->data;
iov[i].iov_base = msg[i]->l1h;
iov[i].iov_len = msgb_tailroom(msg[i]);
} }
msgb_put(msg, rc);
if (ofd->priv_nr == MQ_L1_WRITE)
return l1if_handle_l1prim(fl1h, msg); rc = readv(ofd->fd, iov, ARRAY_SIZE(iov));
else count = rc / prim_size;
return l1if_handle_sysprim(fl1h, msg);
}; for (i = 0; i < count; ++i) {
msgb_put(msg[i], prim_size);
read_dispatch_one(ofd->data, msg[i], ofd->priv_nr);
}
for (i = count; i < ARRAY_SIZE(iov); ++i)
msgb_free(msg[i]);
return 1;
}
/* callback when we can write to one of the l1 msg_queue devices */ /* callback when we can write to one of the l1 msg_queue devices */
static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg) static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
@@ -105,88 +249,74 @@ static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
return 0; return 0;
} }
int l1if_transport_open(struct femtol1_hdl *hdl) int l1if_transport_open(int q, struct femtol1_hdl *hdl)
{ {
int rc, i; int rc;
/* Step 1: Open all msg_queue file descriptors */ /* Step 1: Open all msg_queue file descriptors */
for (i = 0; i < ARRAY_SIZE(hdl->read_ofd); i++) { struct osmo_fd *read_ofd = &hdl->read_ofd[q];
struct osmo_fd *ofd = &hdl->read_ofd[i]; struct osmo_wqueue *wq = &hdl->write_q[q];
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
rc = open(rd_devnames[i], O_RDONLY); rc = open(rd_devnames[q], O_RDONLY);
if (rc < 0) { if (rc < 0) {
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue: %s\n", LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue: %s\n",
strerror(errno)); strerror(errno));
goto out_free; return rc;
}
ofd->fd = rc;
ofd->priv_nr = i;
ofd->data = hdl;
ofd->cb = l1if_fd_cb;
ofd->when = BSC_FD_READ;
rc = osmo_fd_register(ofd);
if (rc < 0) {
close(ofd->fd);
ofd->fd = -1;
goto out_free;
}
} }
for (i = 0; i < ARRAY_SIZE(hdl->write_q); i++) { read_ofd->fd = rc;
struct osmo_wqueue *wq = &hdl->write_q[i]; read_ofd->priv_nr = q;
struct osmo_fd *ofd = &hdl->write_q[i].bfd; read_ofd->data = hdl;
read_ofd->cb = l1if_fd_cb;
rc = open(wr_devnames[i], O_WRONLY); read_ofd->when = BSC_FD_READ;
if (rc < 0) { rc = osmo_fd_register(read_ofd);
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue: %s\n", if (rc < 0) {
strerror(errno)); close(read_ofd->fd);
goto out_read; read_ofd->fd = -1;
} return rc;
osmo_wqueue_init(wq, 10);
wq->write_cb = l1fd_write_cb;
ofd->fd = rc;
ofd->priv_nr = i;
ofd->data = hdl;
ofd->when = BSC_FD_WRITE;
rc = osmo_fd_register(ofd);
if (rc < 0) {
close(ofd->fd);
ofd->fd = -1;
goto out_read;
}
} }
rc = open(wr_devnames[q], O_WRONLY);
if (rc < 0) {
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue: %s\n",
strerror(errno));
goto out_read;
}
osmo_wqueue_init(wq, 10);
wq->write_cb = l1fd_write_cb;
write_ofd->cb = wqueue_vector_cb;
write_ofd->fd = rc;
write_ofd->priv_nr = q;
write_ofd->data = hdl;
write_ofd->when = BSC_FD_WRITE;
rc = osmo_fd_register(write_ofd);
if (rc < 0) {
close(write_ofd->fd);
write_ofd->fd = -1;
goto out_read;
}
return 0; return 0;
out_read: out_read:
for (i = 0; i < ARRAY_SIZE(hdl->read_ofd); i++) { close(hdl->read_ofd[q].fd);
close(hdl->read_ofd[i].fd); osmo_fd_unregister(&hdl->read_ofd[q]);
osmo_fd_unregister(&hdl->read_ofd[i]);
}
out_free:
talloc_free(hdl);
return rc; return rc;
} }
int l1if_transport_close(struct femtol1_hdl *hdl) int l1if_transport_close(int q, struct femtol1_hdl *hdl)
{ {
int i; struct osmo_fd *read_ofd = &hdl->read_ofd[q];
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
for (i = 0; i < ARRAY_SIZE(hdl->read_ofd); i++) { osmo_fd_unregister(read_ofd);
struct osmo_fd *ofd = &hdl->read_ofd[i]; close(read_ofd->fd);
read_ofd->fd = -1;
osmo_fd_unregister(ofd); osmo_fd_unregister(write_ofd);
close(ofd->fd); close(write_ofd->fd);
ofd->fd = -1; write_ofd->fd = -1;
}
for (i = 0; i < ARRAY_SIZE(hdl->write_q); i++) {
struct osmo_fd *ofd = &hdl->write_q[i].bfd;
osmo_fd_unregister(ofd);
close(ofd->fd);
ofd->fd = -1;
}
return 0; return 0;
} }

View File

@@ -1,54 +0,0 @@
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <osmocom/gsm/prim.h>
#include <osmocom/bb/common/l1ctl.h>
#include <osmocom/bb/common/lapdm.h>
/* LAPDm wants to send a PH-* primitive to the physical layer (L1) */
int sysmol1_ph_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
struct osmocom_ms *ms = ctx;
struct osmo_phsap_prim *pp = (struct osmo_phsap_prim *) oph;
int rc = 0;
if (oph->sap != SAP_GSM_PH)
return -ENODEV;
if (oph->operation != PRIM_OP_REQUEST)
return -EINVAL;
switch (oph->primitive) {
case PRIM_PH_RACH:
/* A BTS never transmits RACH */
case PRIM_PH_DATA:
/* we use the LAPDm code in polling only, we should never
* get a PH-DATA.req */
default:
LOGP(DLAPDM, LOGL_ERROR, "LAPDm sends unknown prim %u\n",
oph->primitive);
rc = -EINVAL;
break;
}
return rc;
}

View File

@@ -1,6 +1,6 @@
/* Main program for Sysmocom BTS */ /* Main program for Sysmocom BTS */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org> /* (C) 2011-2013 by Harald Welte <laforge@gnumonks.org>
* *
* All Rights Reserved * All Rights Reserved
* *
@@ -24,7 +24,11 @@
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <getopt.h> #include <getopt.h>
#include <limits.h>
#include <sys/signal.h> #include <sys/signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sched.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
@@ -33,28 +37,36 @@
#include <osmocom/core/application.h> #include <osmocom/core/application.h>
#include <osmocom/vty/telnet_interface.h> #include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h> #include <osmocom/vty/logging.h>
#include <osmocom/vty/ports.h>
#include <osmo-bts/gsm_data.h> #include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h> #include <osmo-bts/logging.h>
#include <osmo-bts/abis.h> #include <osmo-bts/abis.h>
#include <osmo-bts/bts.h> #include <osmo-bts/bts.h>
#include <osmo-bts/vty.h> #include <osmo-bts/vty.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/control_if.h>
#define SYSMOBTS_RF_LOCK_PATH "/var/lock/bts_rf_lock"
#include "utils.h"
#include "eeprom.h"
#include "l1_if.h" #include "l1_if.h"
#include "hw_misc.h"
#include "oml_router.h"
/* FIXME: read from real hardware */ int pcu_direct = 0;
const uint8_t abis_mac[6] = { 0,1,2,3,4,5 };
/* FIXME: generate from git */
const char *software_version = "0815";
static const char *config_file = "osmo-bts.cfg"; static const char *config_file = "osmo-bts.cfg";
extern const char *osmobts_copyright;
static int daemonize = 0; static int daemonize = 0;
static unsigned int dsp_trace = 0; static unsigned int dsp_trace = 0x71c00020;
static int rt_prio = -1;
int bts_model_init(struct gsm_bts *bts) int bts_model_init(struct gsm_bts *bts)
{ {
struct femtol1_hdl *fl1h; struct femtol1_hdl *fl1h;
int rc;
fl1h = l1if_open(bts->c0); fl1h = l1if_open(bts->c0);
if (!fl1h) { if (!fl1h) {
@@ -64,31 +76,77 @@ int bts_model_init(struct gsm_bts *bts)
fl1h->dsp_trace_f = dsp_trace; fl1h->dsp_trace_f = dsp_trace;
bts->c0->role_bts.l1h = fl1h; bts->c0->role_bts.l1h = fl1h;
bts->c0->nominal_power = 23;
l1if_reset(fl1h); rc = sysmobts_get_nominal_power(bts->c0);
if (rc < 0) {
LOGP(DL1C, LOGL_NOTICE, "Cannot determine nominal "
"transmit power. Assuming 23dBm.\n");
rc = 23;
}
bts->c0->nominal_power = rc;
bts->c0->power_params.trx_p_max_out_mdBm = to_mdB(rc);
femtol1_vty_init(bts); bts_model_vty_init(bts);
return 0; return 0;
} }
struct ipabis_link *link_init(struct gsm_bts *bts, const char *ip) int bts_model_oml_estab(struct gsm_bts *bts)
{
struct femtol1_hdl *fl1h = bts->c0->role_bts.l1h;
l1if_reset(fl1h);
return 0;
}
/* Set the clock calibration to the value
* read from the eeprom.
*/
void clk_cal_use_eeprom(struct gsm_bts *bts)
{ {
struct ipabis_link *link = talloc_zero(bts, struct ipabis_link);
struct in_addr ia;
int rc; int rc;
struct femtol1_hdl *hdl;
eeprom_RfClockCal_t rf_clk;
inet_aton(ip, &ia); hdl = bts->c0->role_bts.l1h;
link->bts = bts; if (!hdl || !hdl->clk_use_eeprom)
bts->oml_link = link; return;
rc = abis_open(link, ntohl(ia.s_addr)); rc = eeprom_ReadRfClockCal(&rf_clk);
if (rc < 0) if (rc != EEPROM_SUCCESS) {
return NULL; LOGP(DL1C, LOGL_ERROR, "Failed to read from EEPROM.\n");
return;
}
return link; hdl->clk_cal = rf_clk.iClkCor;
LOGP(DL1C, LOGL_NOTICE,
"Read clock calibration(%d) from EEPROM.\n", hdl->clk_cal);
}
void bts_update_status(enum bts_global_status which, int on)
{
static uint64_t states = 0;
uint64_t old_states = states;
int led_rf_active_on;
if (on)
states |= (1ULL << which);
else
states &= ~(1ULL << which);
led_rf_active_on =
(states & (1ULL << BTS_STATUS_RF_ACTIVE)) &&
!(states & (1ULL << BTS_STATUS_RF_MUTE));
LOGP(DL1C, LOGL_INFO,
"Set global status #%d to %d (%04llx -> %04llx), LEDs: ACT %d\n",
which, on,
(long long)old_states, (long long)states,
led_rf_active_on);
sysmobts_led_set(LED_RF_ACTIVE, led_rf_active_on);
} }
static void print_help() static void print_help()
@@ -102,10 +160,23 @@ static void print_help()
" -T --timestamp Prefix every log line with a timestamp\n" " -T --timestamp Prefix every log line with a timestamp\n"
" -V --version Print version information and exit\n" " -V --version Print version information and exit\n"
" -e --log-level Set a global log-level\n" " -e --log-level Set a global log-level\n"
" -p --dsp-trace Set DSP trace flags\n" " -p --dsp-trace Set DSP trace flags\n"
" -r --realtime PRIO Use SCHED_RR with the specified priority\n"
" -w --hw-version Print the targeted HW Version\n"
" -M --pcu-direct Force PCU to access message queue for "
"PDCH dchannel directly\n"
); );
} }
static void print_hwversion()
{
#ifdef HW_SYSMOBTS_V1
printf("sysmobts was compiled for hw version 1.\n");
#else
printf("sysmobts was compiled for hw version 2.\n");
#endif
}
/* FIXME: finally get some option parsing code into libosmocore */ /* FIXME: finally get some option parsing code into libosmocore */
static void handle_options(int argc, char **argv) static void handle_options(int argc, char **argv)
{ {
@@ -122,10 +193,13 @@ static void handle_options(int argc, char **argv)
{ "version", 0, 0, 'V' }, { "version", 0, 0, 'V' },
{ "log-level", 1, 0, 'e' }, { "log-level", 1, 0, 'e' },
{ "dsp-trace", 1, 0, 'p' }, { "dsp-trace", 1, 0, 'p' },
{ "hw-version", 0, 0, 'w' },
{ "pcu-direct", 0, 0, 'M' },
{ "realtime", 1, 0, 'r' },
{ 0, 0, 0, 0 } { 0, 0, 0, 0 }
}; };
c = getopt_long(argc, argv, "hc:d:Dc:sTVe:p:", c = getopt_long(argc, argv, "hc:d:Dc:sTVe:p:w:Mr:",
long_options, &option_idx); long_options, &option_idx);
if (c == -1) if (c == -1)
break; break;
@@ -145,11 +219,14 @@ static void handle_options(int argc, char **argv)
daemonize = 1; daemonize = 1;
break; break;
case 'c': case 'c':
config_file = strdup(optarg); config_file = optarg;
break; break;
case 'T': case 'T':
log_set_print_timestamp(osmo_stderr_target, 1); log_set_print_timestamp(osmo_stderr_target, 1);
break; break;
case 'M':
pcu_direct = 1;
break;
case 'V': case 'V':
print_version(1); print_version(1);
exit(0); exit(0);
@@ -160,6 +237,13 @@ static void handle_options(int argc, char **argv)
case 'p': case 'p':
dsp_trace = strtoul(optarg, NULL, 16); dsp_trace = strtoul(optarg, NULL, 16);
break; break;
case 'w':
print_hwversion();
exit(0);
break;
case 'r':
rt_prio = atoi(optarg);
break;
default: default:
break; break;
} }
@@ -187,11 +271,35 @@ static void signal_handler(int signal)
} }
} }
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;
}
extern int sysmobts_ctrlif_inst_cmds(void);
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
struct stat st;
struct sched_param param;
struct gsm_bts_role_bts *btsb; struct gsm_bts_role_bts *btsb;
struct ipabis_link *link; struct e1inp_line *line;
void *tall_msgb_ctx; void *tall_msgb_ctx;
struct osmo_fd accept_fd, read_fd;
int rc; int rc;
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context"); tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
@@ -203,14 +311,29 @@ int main(int argc, char **argv)
vty_init(&bts_vty_info); vty_init(&bts_vty_info);
bts_vty_init(&bts_log_info); bts_vty_init(&bts_log_info);
handle_options(argc, argv);
/* enable realtime priority for us */
if (rt_prio != -1) {
memset(&param, 0, sizeof(param));
param.sched_priority = rt_prio;
rc = sched_setscheduler(getpid(), SCHED_RR, &param);
if (rc != 0) {
fprintf(stderr, "Setting SCHED_RR priority(%d) failed: %s\n",
param.sched_priority, strerror(errno));
exit(1);
}
}
bts = gsm_bts_alloc(tall_bts_ctx); bts = gsm_bts_alloc(tall_bts_ctx);
if (bts_init(bts) < 0) { if (bts_init(bts) < 0) {
fprintf(stderr, "unable to to open bts\n"); fprintf(stderr, "unable to open bts\n");
exit(1); exit(1);
} }
btsb = bts_role_bts(bts); btsb = bts_role_bts(bts);
btsb->support.ciphers = CIPHER_A5(1) | CIPHER_A5(2) | CIPHER_A5(3);
handle_options(argc, argv); abis_init(bts);
rc = vty_read_config_file(config_file, NULL); rc = vty_read_config_file(config_file, NULL);
if (rc < 0) { if (rc < 0) {
@@ -219,29 +342,51 @@ int main(int argc, char **argv)
exit(1); exit(1);
} }
rc = telnet_init(tall_bts_ctx, NULL, 4241); clk_cal_use_eeprom(bts);
if (stat(SYSMOBTS_RF_LOCK_PATH, &st) == 0) {
LOGP(DL1C, LOGL_NOTICE, "Not starting BTS due to RF_LOCK file present\n");
exit(23);
}
write_pid_file("osmo-bts");
bts_controlif_setup(bts);
sysmobts_ctrlif_inst_cmds();
rc = telnet_init(tall_bts_ctx, NULL, OSMO_VTY_PORT_BTS);
if (rc < 0) { if (rc < 0) {
fprintf(stderr, "Error initializing telnet\n"); fprintf(stderr, "Error initializing telnet\n");
exit(1); exit(1);
} }
if (pcu_sock_init()) {
fprintf(stderr, "PCU L1 socket failed\n");
exit(1);
}
signal(SIGINT, &signal_handler); signal(SIGINT, &signal_handler);
//signal(SIGABRT, &signal_handler); //signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler); signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler); signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals(); osmo_init_ignore_signals();
rc = oml_router_init(bts, OML_ROUTER_PATH, &accept_fd, &read_fd);
if (rc < 0) {
fprintf(stderr, "Error creating the OML router: %s rc=%d\n",
OML_ROUTER_PATH, rc);
exit(1);
}
if (!btsb->bsc_oml_host) { if (!btsb->bsc_oml_host) {
fprintf(stderr, "Cannot start BTS without knowing BSC OML IP\n"); fprintf(stderr, "Cannot start BTS without knowing BSC OML IP\n");
exit(1); exit(1);
} }
link = link_init(bts, btsb->bsc_oml_host); line = abis_open(bts, btsb->bsc_oml_host, "sysmoBTS");
if (!link) { if (!line) {
fprintf(stderr, "unable to connect to BSC\n"); fprintf(stderr, "unable to connect to BSC\n");
exit(1); exit(2);
} }
bts->oml_link = link;
if (daemonize) { if (daemonize) {
rc = osmo_daemonize(); rc = osmo_daemonize();

View File

@@ -0,0 +1,27 @@
#ifndef _SYSMOBTS_EEPROM_H
#define _SYSMOBTS_EEPROM_H
#include <stdint.h>
struct sysmobts_eeprom { /* offset */
uint8_t eth_mac[6]; /* 0-5 */
uint8_t _pad0[10]; /* 6-15 */
uint16_t unused1; /* 16-17 */
uint8_t temp1_max; /* 18 */
uint8_t temp2_max; /* 19 */
uint32_t serial_nr; /* 20-23 */
uint32_t operational_hours; /* 24-27 */
uint32_t boot_count; /* 28-31 */
uint16_t model_nr; /* 32-33 */
uint16_t model_flags; /* 34-35 */
uint8_t trx_nr; /* 36 */
uint8_t _pad1[84]; /* 37-120 */
uint8_t gpg_key[128]; /* 121-249 */
} __attribute__((packed));
enum sysmobts_model_number {
MODEL_SYSMOBTS_1020 = 1002,
MODEL_SYSMOBTS_2050 = 2050,
};
#endif

View File

@@ -0,0 +1,308 @@
/* Main program for SysmoBTS management daemon */
/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
* (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 <stdlib.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <sys/signal.h>
#include <sys/stat.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/application.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/msgb.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/ports.h>
#include "misc/sysmobts_misc.h"
#include "misc/sysmobts_mgr.h"
#include "misc/sysmobts_par.h"
static int bts_type;
static int trx_number;
static int no_eeprom_write = 0;
static int daemonize = 0;
void *tall_mgr_ctx;
/* every 6 hours means 365*4 = 1460 EEprom writes per year (max) */
#define TEMP_TIMER_SECS (6 * 3600)
/* every 1 hours means 365*24 = 8760 EEprom writes per year (max) */
#define HOURS_TIMER_SECS (1 * 3600)
/* the initial state */
static struct sysmobts_mgr_instance manager = {
.config_file = "sysmobts-mgr.cfg",
.rf_limit = {
.thresh_warn = 60,
.thresh_crit = 78,
},
.digital_limit = {
.thresh_warn = 60,
.thresh_crit = 78,
},
.board_limit = {
.thresh_warn = 60,
.thresh_crit = 78,
},
.pa_limit = {
.thresh_warn = 60,
.thresh_crit = 100,
},
.action_warn = 0,
.action_crit = TEMP_ACT_PA_OFF,
.state = STATE_NORMAL,
};
static int classify_bts(void)
{
int rc;
rc = sysmobts_par_get_int(SYSMOBTS_PAR_MODEL_NR, &bts_type);
if (rc < 0) {
fprintf(stderr, "Failed to get model number.\n");
return -1;
}
rc = sysmobts_par_get_int(SYSMOBTS_PAR_TRX_NR, &trx_number);
if (rc < 0) {
fprintf(stderr, "Failed to get the trx number.\n");
return -1;
}
return 0;
}
int sysmobts_bts_type(void)
{
return bts_type;
}
int sysmobts_trx_number(void)
{
return trx_number;
}
int is_sbts2050(void)
{
return bts_type == 2050;
}
int is_sbts2050_trx(int trx)
{
return trx_number == trx;
}
int is_sbts2050_master(void)
{
if (!is_sbts2050())
return 0;
if (!is_sbts2050_trx(0))
return 0;
return 1;
}
static struct osmo_timer_list temp_timer;
static void check_temp_timer_cb(void *unused)
{
sysmobts_check_temp(no_eeprom_write);
osmo_timer_schedule(&temp_timer, TEMP_TIMER_SECS, 0);
}
static struct osmo_timer_list hours_timer;
static void hours_timer_cb(void *unused)
{
sysmobts_update_hours(no_eeprom_write);
osmo_timer_schedule(&hours_timer, HOURS_TIMER_SECS, 0);
}
static void print_help(void)
{
printf("sysmobts-mgr [-nsD] [-d cat]\n");
printf(" -n Do not write to EEPROM\n");
printf(" -s Disable color\n");
printf(" -d CAT enable debugging\n");
printf(" -D daemonize\n");
printf(" -c Specify the filename of the config file\n");
}
static int parse_options(int argc, char **argv)
{
int opt;
while ((opt = getopt(argc, argv, "nhsd:c:")) != -1) {
switch (opt) {
case 'n':
no_eeprom_write = 1;
break;
case 'h':
print_help();
return -1;
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':
manager.config_file = optarg;
break;
default:
return -1;
}
}
return 0;
}
static void signal_handler(int signal)
{
fprintf(stderr, "signal %u received\n", signal);
switch (signal) {
case SIGINT:
sysmobts_check_temp(no_eeprom_write);
sysmobts_update_hours(no_eeprom_write);
exit(0);
break;
case SIGABRT:
case SIGUSR1:
case SIGUSR2:
talloc_report_full(tall_mgr_ctx, stderr);
break;
default:
break;
}
}
static struct log_info_cat mgr_log_info_cat[] = {
[DTEMP] = {
.name = "DTEMP",
.description = "Temperature monitoring",
.color = "\033[1;35m",
.enabled = 1, .loglevel = LOGL_INFO,
},
[DFW] = {
.name = "DFW",
.description = "DSP/FPGA firmware management",
.color = "\033[1;36m",
.enabled = 1, .loglevel = LOGL_INFO,
},
[DFIND] = {
.name = "DFIND",
.description = "ipaccess-find handling",
.color = "\033[1;37m",
.enabled = 1, .loglevel = LOGL_INFO,
},
};
static const struct log_info mgr_log_info = {
.cat = mgr_log_info_cat,
.num_cat = ARRAY_SIZE(mgr_log_info_cat),
};
static int mgr_log_init(void)
{
osmo_init_logging(&mgr_log_info);
return 0;
}
int main(int argc, char **argv)
{
void *tall_msgb_ctx;
int rc;
tall_mgr_ctx = talloc_named_const(NULL, 1, "bts manager");
tall_msgb_ctx = talloc_named_const(tall_mgr_ctx, 1, "msgb");
msgb_set_talloc_ctx(tall_msgb_ctx);
mgr_log_init();
if (classify_bts() != 0)
exit(2);
osmo_init_ignore_signals();
signal(SIGINT, &signal_handler);
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
rc = parse_options(argc, argv);
if (rc < 0)
exit(2);
sysmobts_mgr_vty_init();
logging_vty_add_cmds(&mgr_log_info);
rc = sysmobts_mgr_parse_config(&manager);
if (rc < 0) {
LOGP(DFIND, LOGL_FATAL, "Cannot parse config file\n");
exit(1);
}
rc = telnet_init(tall_msgb_ctx, NULL, OSMO_VTY_PORT_BTSMGR);
if (rc < 0) {
fprintf(stderr, "Error initializing telnet\n");
exit(1);
}
/* start temperature check timer */
temp_timer.cb = check_temp_timer_cb;
check_temp_timer_cb(NULL);
/* start operational hours timer */
hours_timer.cb = hours_timer_cb;
hours_timer_cb(NULL);
/* start uc temperature check timer */
sbts2050_uc_initialize();
/* handle broadcast messages for ipaccess-find */
if (sysmobts_mgr_nl_init() != 0)
exit(3);
/* Initialize the temperature control */
sysmobts_mgr_temp_init(&manager);
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
perror("Error during daemonize");
exit(1);
}
}
while (1) {
log_reset_context();
osmo_select_main(0);
}
}

View File

@@ -0,0 +1,85 @@
#ifndef _SYSMOBTS_MGR_H
#define _SYSMOBTS_MGR_H
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
enum {
DTEMP,
DFW,
DFIND,
};
enum {
#if 0
TEMP_ACT_PWR_CONTRL = 0x1,
#endif
TEMP_ACT_SLAVE_OFF = 0x4,
TEMP_ACT_PA_OFF = 0x8,
TEMP_ACT_BTS_SRV_OFF = 0x10,
};
/* actions only for normal state */
enum {
#if 0
TEMP_ACT_NORM_PW_CONTRL = 0x1,
#endif
TEMP_ACT_NORM_SLAVE_ON = 0x4,
TEMP_ACT_NORM_PA_ON = 0x8,
TEMP_ACT_NORM_BTS_SRV_ON= 0x10,
};
enum sysmobts_temp_state {
STATE_NORMAL, /* Everything is fine */
STATE_WARNING_HYST, /* Go back to normal next? */
STATE_WARNING, /* We are above the warning threshold */
STATE_CRITICAL, /* We have an issue. Wait for below warning */
};
/**
* Temperature Limits. We separate from a threshold
* that will generate a warning and one that is so
* severe that an action will be taken.
*/
struct sysmobts_temp_limit {
int thresh_warn;
int thresh_crit;
};
enum mgr_vty_node {
MGR_NODE = _LAST_OSMOVTY_NODE + 1,
ACT_NORM_NODE,
ACT_WARN_NODE,
ACT_CRIT_NODE,
LIMIT_RF_NODE,
LIMIT_DIGITAL_NODE,
LIMIT_BOARD_NODE,
LIMIT_PA_NODE,
};
struct sysmobts_mgr_instance {
const char *config_file;
struct sysmobts_temp_limit rf_limit;
struct sysmobts_temp_limit digital_limit;
/* Only available on sysmobts 2050 */
struct sysmobts_temp_limit board_limit;
struct sysmobts_temp_limit pa_limit;
int action_norm;
int action_warn;
int action_crit;
enum sysmobts_temp_state state;
};
int sysmobts_mgr_vty_init(void);
int sysmobts_mgr_parse_config(struct sysmobts_mgr_instance *mgr);
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);
#endif

View File

@@ -0,0 +1,384 @@
/* (C) 2014 by s.f.m.c. GmbH
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "sysmobts_misc.h"
#include "sysmobts_par.h"
#include "sysmobts_mgr.h"
#include "btsconfig.h"
#include <osmocom/core/logging.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/serial.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#ifdef BUILD_SBTS2050
#include <sysmocom/femtobts/sbts2050_header.h>
#define SERIAL_ALLOC_SIZE 300
#define SIZE_HEADER_RSP 5
#define SIZE_HEADER_CMD 4
struct uc {
int id;
int fd;
const char *path;
};
struct ucinfo {
uint16_t id;
int master;
int slave;
int pa;
};
static struct uc ucontrol0 = {
.id = 0,
.path = "/dev/ttyS0",
.fd = -1,
};
/**********************************************************************
* Functions read/write from serial interface
*********************************************************************/
static int hand_serial_read(int fd, struct msgb *msg, int numbytes)
{
int rc, bread = 0;
if (numbytes > msgb_tailroom(msg))
return -ENOSPC;
while (bread < numbytes) {
rc = read(fd, msg->tail, numbytes - bread);
if (rc < 0)
return -1;
if (rc == 0)
break;
bread += rc;
msgb_put(msg, rc);
}
return bread;
}
static int hand_serial_write(int fd, struct msgb *msg)
{
int rc, bwritten = 0;
while (msg->len > 0) {
rc = write(fd, msg->data, msg->len);
if (rc <= 0)
return -1;
msgb_pull(msg, rc);
bwritten += rc;
}
return bwritten;
}
/**********************************************************************
* Functions request information to Microcontroller
*********************************************************************/
static void add_parity(cmdpkt_t *command)
{
int n;
uint8_t parity = 0x00;
for (n = 0; n < SIZE_HEADER_CMD+command->u8Len; n++)
parity ^= ((uint8_t *)command)[n];
command->cmd.raw[command->u8Len] = parity;
}
static struct msgb *sbts2050_ucinfo_sndrcv(struct uc *ucontrol, const struct ucinfo *info)
{
int num, rc;
cmdpkt_t *command;
rsppkt_t *response;
struct msgb *msg;
fd_set fdread;
struct timeval tout = {
.tv_sec = 10,
};
switch (info->id) {
case SBTS2050_TEMP_RQT:
num = sizeof(command->cmd.tempGet);
break;
case SBTS2050_PWR_RQT:
num = sizeof(command->cmd.pwrSetState);
break;
case SBTS2050_PWR_STATUS:
num = sizeof(command->cmd.pwrGetStatus);
break;
default:
return NULL;
}
num = num + SIZE_HEADER_CMD+1;
msg = msgb_alloc(SERIAL_ALLOC_SIZE, "Message Microcontroller");
if (msg == NULL) {
LOGP(DTEMP, LOGL_ERROR, "Error creating msg\n");
return NULL;
}
command = (cmdpkt_t *) msgb_put(msg, num);
command->u16Magic = 0xCAFE;
switch (info->id) {
case SBTS2050_TEMP_RQT:
command->u8Id = info->id;
command->u8Len = sizeof(command->cmd.tempGet);
break;
case SBTS2050_PWR_RQT:
command->u8Id = info->id;
command->u8Len = sizeof(command->cmd.pwrSetState);
command->cmd.pwrSetState.u1MasterEn = !!info->master;
command->cmd.pwrSetState.u1SlaveEn = !!info->slave;
command->cmd.pwrSetState.u1PwrAmpEn = !!info->pa;
break;
case SBTS2050_PWR_STATUS:
command->u8Id = info->id;
command->u8Len = sizeof(command->cmd.pwrGetStatus);
break;
default:
goto err;
}
add_parity(command);
if (hand_serial_write(ucontrol->fd, msg) < 0)
goto err;
msgb_reset(msg);
FD_ZERO(&fdread);
FD_SET(ucontrol->fd, &fdread);
num = SIZE_HEADER_RSP;
while (1) {
rc = select(ucontrol->fd+1, &fdread, NULL, NULL, &tout);
if (rc > 0) {
if (hand_serial_read(ucontrol->fd, msg, num) < 0)
goto err;
response = (rsppkt_t *)msg->data;
if (response->u8Id != info->id || msg->len <= 0 ||
response->i8Error != RQT_SUCCESS)
goto err;
if (msg->len == SIZE_HEADER_RSP + response->u8Len + 1)
break;
num = response->u8Len + 1;
} else
goto err;
}
return msg;
err:
msgb_free(msg);
return NULL;
}
/**********************************************************************
* Get power status function
*********************************************************************/
int sbts2050_uc_get_status(struct sbts2050_power_status *status)
{
struct msgb *msg;
const struct ucinfo info = {
.id = SBTS2050_PWR_STATUS,
};
rsppkt_t *response;
memset(status, 0, sizeof(*status));
msg = sbts2050_ucinfo_sndrcv(&ucontrol0, &info);
if (msg == NULL) {
LOGP(DTEMP, LOGL_ERROR,
"Error requesting power status.\n");
return -1;
}
response = (rsppkt_t *)msg->data;
status->main_supply_current = response->rsp.pwrGetStatus.u8MainSupplyA / 64.f;
status->master_enabled = response->rsp.pwrGetStatus.u1MasterEn;
status->master_voltage = response->rsp.pwrGetStatus.u8MasterV / 32.f;
status->master_current = response->rsp.pwrGetStatus.u8MasterA / 64.f;;
status->slave_enabled = response->rsp.pwrGetStatus.u1SlaveEn;
status->slave_voltage = response->rsp.pwrGetStatus.u8SlaveV / 32.f;
status->slave_current = response->rsp.pwrGetStatus.u8SlaveA / 64.f;
status->pa_enabled = response->rsp.pwrGetStatus.u1PwrAmpEn;
status->pa_voltage = response->rsp.pwrGetStatus.u8PwrAmpV / 4.f;
status->pa_current = response->rsp.pwrGetStatus.u8PwrAmpA / 64.f;
status->pa_bias_voltage = response->rsp.pwrGetStatus.u8PwrAmpBiasV / 16.f;
msgb_free(msg);
return 0;
}
/**********************************************************************
* Uc Power Switching handling
*********************************************************************/
int sbts2050_uc_set_power(int pmaster, int pslave, int ppa)
{
struct msgb *msg;
const struct ucinfo info = {
.id = SBTS2050_PWR_RQT,
.master = pmaster,
.slave = pslave,
.pa = ppa
};
msg = sbts2050_ucinfo_sndrcv(&ucontrol0, &info);
if (msg == NULL) {
LOGP(DTEMP, LOGL_ERROR, "Error switching off some unit.\n");
return -1;
}
LOGP(DTEMP, LOGL_DEBUG, "Switch off/on success:\n"
"MASTER %s\n"
"SLAVE %s\n"
"PA %s\n",
pmaster ? "ON" : "OFF",
pslave ? "ON" : "OFF",
ppa ? "ON" : "OFF");
msgb_free(msg);
return 0;
}
/**********************************************************************
* Uc temperature handling
*********************************************************************/
int sbts2050_uc_check_temp(int *temp_pa, int *temp_board)
{
rsppkt_t *response;
struct msgb *msg;
const struct ucinfo info = {
.id = SBTS2050_TEMP_RQT,
};
msg = sbts2050_ucinfo_sndrcv(&ucontrol0, &info);
if (msg == NULL) {
LOGP(DTEMP, LOGL_ERROR, "Error reading temperature\n");
return -1;
}
response = (rsppkt_t *)msg->data;
*temp_board = response->rsp.tempGet.i8BrdTemp;
*temp_pa = response->rsp.tempGet.i8PaTemp;
LOGP(DTEMP, LOGL_DEBUG, "Temperature Board: %+3d C, "
"Tempeture PA: %+3d C\n",
response->rsp.tempGet.i8BrdTemp,
response->rsp.tempGet.i8PaTemp);
msgb_free(msg);
return 0;
}
void sbts2050_uc_initialize(void)
{
if (!is_sbts2050())
return;
ucontrol0.fd = osmo_serial_init(ucontrol0.path, 115200);
if (ucontrol0.fd < 0) {
LOGP(DTEMP, LOGL_ERROR,
"Failed to open the serial interface\n");
return;
}
if (is_sbts2050_master()) {
LOGP(DTEMP, LOGL_NOTICE, "Going to enable the PA.\n");
sbts2050_uc_set_pa_power(1);
}
}
int sbts2050_uc_set_pa_power(int on_off)
{
struct sbts2050_power_status status;
if (sbts2050_uc_get_status(&status) != 0) {
LOGP(DTEMP, LOGL_ERROR, "Failed to read current power status.\n");
return -1;
}
return sbts2050_uc_set_power(status.master_enabled, status.slave_enabled, on_off);
}
int sbts2050_uc_set_slave_power(int on_off)
{
struct sbts2050_power_status status;
if (sbts2050_uc_get_status(&status) != 0) {
LOGP(DTEMP, LOGL_ERROR, "Failed to read current power status.\n");
return -1;
}
return sbts2050_uc_set_power(
status.master_enabled,
on_off,
status.pa_enabled);
}
#else
void sbts2050_uc_initialize(void)
{
LOGP(DTEMP, LOGL_NOTICE, "sysmoBTS2050 was not enabled at compile time.\n");
}
int sbts2050_uc_check_temp(int *temp_pa, int *temp_board)
{
LOGP(DTEMP, LOGL_ERROR, "sysmoBTS2050 compiled without temp support.\n");
*temp_pa = *temp_board = 99999;
return -1;
}
int sbts2050_uc_get_status(struct sbts2050_power_status *status)
{
memset(status, 0, sizeof(*status));
LOGP(DTEMP, LOGL_ERROR, "sysmoBTS2050 compiled without status support.\n");
return -1;
}
int sbts2050_uc_set_pa_power(int on_off)
{
LOGP(DTEMP, LOGL_ERROR, "sysmoBTS2050 compiled without PA support.\n");
return -1;
}
int sbts2050_uc_set_slave_power(int on_off)
{
LOGP(DTEMP, LOGL_ERROR, "sysmoBTS2050 compiled without UC support.\n");
return -1;
}
#endif

View File

@@ -0,0 +1,205 @@
/* NetworkListen for SysmoBTS management daemon */
/*
* (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 "misc/sysmobts_mgr.h"
#include "misc/sysmobts_misc.h"
#include "misc/sysmobts_nl.h"
#include "misc/sysmobts_par.h"
#include <osmo-bts/logging.h>
#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/select.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
static struct osmo_fd nl_fd;
/*
* The TLV structure in IPA messages in UDP packages is a bit
* weird. First the header appears to have an extra NULL byte
* and second the L16 of the L16TV needs to include +1 for the
* tag. The default msgb/tlv and libosmo-abis routines do not
* provide this.
*/
static void ipaccess_prepend_header_quirk(struct msgb *msg, int proto)
{
struct ipaccess_head *hh;
/* prepend the ip.access header */
hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh) + 1);
hh->len = htons(msg->len - sizeof(*hh) - 1);
hh->proto = proto;
}
static void quirk_l16tv_put(struct msgb *msg, uint16_t len, uint8_t tag,
const uint8_t *val)
{
uint8_t *buf = msgb_put(msg, len + 2 + 1);
*buf++ = (len + 1) >> 8;
*buf++ = (len + 1) & 0xff;
*buf++ = tag;
memcpy(buf, val, len);
}
/*
* We don't look at the content of the request yet and lie
* about most of the responses.
*/
static void respond_to(struct sockaddr_in *src, struct osmo_fd *fd,
uint8_t *data, size_t len)
{
static int fetched_info = 0;
static char mac_str[20] = {0, };
static char *model_name;
static char ser_str[20] = {0, };
struct sockaddr_in loc_addr;
int rc;
char loc_ip[INET_ADDRSTRLEN];
struct msgb *msg = msgb_alloc_headroom(512, 128, "ipa get response");
if (!msg) {
LOGP(DFIND, LOGL_ERROR, "Failed to allocate msgb\n");
return;
}
if (!fetched_info) {
uint8_t mac[6];
int serno;
/* fetch the MAC */
sysmobts_par_get_buf(SYSMOBTS_PAR_MAC, mac, sizeof(mac));
snprintf(mac_str, sizeof(mac_str), "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
mac[0], mac[1], mac[2],
mac[3], mac[4], mac[5]);
/* fetch the serial number */
sysmobts_par_get_int(SYSMOBTS_PAR_SERNR, &serno);
snprintf(ser_str, sizeof(ser_str), "%d", serno);
/* fetch the model and trx number */
switch(sysmobts_bts_type()) {
case 0:
case 0xffff:
case 1002:
model_name = "sysmoBTS 1002";
break;
case 2050:
if (sysmobts_trx_number() == 0)
model_name = "sysmoBTS 2050 (master)";
else if (sysmobts_trx_number() == 1)
model_name = "sysmoBTS 2050 (slave)";
else
model_name = "sysmoBTS 2050 (unknown)";
break;
default:
model_name = "Unknown";
break;
}
fetched_info = 1;
}
if (source_for_dest(&src->sin_addr, &loc_addr.sin_addr) != 0) {
LOGP(DFIND, LOGL_ERROR, "Failed to determine local source\n");
return;
}
msgb_put_u8(msg, IPAC_MSGT_ID_RESP);
/* append MAC addr */
quirk_l16tv_put(msg, strlen(mac_str) + 1, IPAC_IDTAG_MACADDR, (uint8_t *) mac_str);
/* append ip address */
inet_ntop(AF_INET, &loc_addr.sin_addr, loc_ip, sizeof(loc_ip));
quirk_l16tv_put(msg, strlen(loc_ip) + 1, IPAC_IDTAG_IPADDR, (uint8_t *) loc_ip);
/* append the serial number */
quirk_l16tv_put(msg, strlen(ser_str) + 1, IPAC_IDTAG_SERNR, (uint8_t *) ser_str);
/* abuse some flags */
quirk_l16tv_put(msg, strlen(model_name) + 1, IPAC_IDTAG_UNIT, (uint8_t *) model_name);
/* ip.access nanoBTS would reply to port==3006 */
ipaccess_prepend_header_quirk(msg, IPAC_PROTO_IPACCESS);
rc = sendto(fd->fd, msg->data, msg->len, 0, (struct sockaddr *)src, sizeof(*src));
if (rc != msg->len)
LOGP(DFIND, LOGL_ERROR,
"Failed to send with rc(%d) errno(%d)\n", rc, errno);
}
static int ipaccess_bcast(struct osmo_fd *fd, unsigned int what)
{
uint8_t data[2048];
char src[INET_ADDRSTRLEN];
struct sockaddr_in addr = {};
socklen_t len = sizeof(addr);
int rc;
rc = recvfrom(fd->fd, data, sizeof(data), 0,
(struct sockaddr *) &addr, &len);
if (rc <= 0) {
LOGP(DFIND, LOGL_ERROR,
"Failed to read from socket errno(%d)\n", errno);
return -1;
}
LOGP(DFIND, LOGL_DEBUG,
"Received request from: %s size %d\n",
inet_ntop(AF_INET, &addr.sin_addr, src, sizeof(src)), rc);
if (rc < 6)
return 0;
if (data[2] != IPAC_PROTO_IPACCESS || data[4] != IPAC_MSGT_ID_GET)
return 0;
respond_to(&addr, fd, data + 6, rc - 6);
return 0;
}
int sysmobts_mgr_nl_init(void)
{
int rc;
nl_fd.cb = ipaccess_bcast;
rc = osmo_sock_init_ofd(&nl_fd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
"0.0.0.0", 3006, OSMO_SOCK_F_BIND);
if (rc < 0) {
perror("Socket creation");
return -1;
}
return 0;
}

View File

@@ -0,0 +1,294 @@
/* Temperature control for SysmoBTS management daemon */
/*
* (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 "misc/sysmobts_mgr.h"
#include "misc/sysmobts_misc.h"
#include <osmo-bts/logging.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/utils.h>
static struct sysmobts_mgr_instance *s_mgr;
static struct osmo_timer_list temp_ctrl_timer;
static const struct value_string state_names[] = {
{ STATE_NORMAL, "NORMAL" },
{ STATE_WARNING_HYST, "WARNING (HYST)" },
{ STATE_WARNING, "WARNING" },
{ STATE_CRITICAL, "CRITICAL" },
{ 0, NULL }
};
const char *sysmobts_mgr_temp_get_state(enum sysmobts_temp_state state)
{
return get_value_string(state_names, state);
}
static int next_state(enum sysmobts_temp_state current_state, int critical, int warning)
{
int next_state = -1;
switch (current_state) {
case STATE_NORMAL:
if (critical)
next_state = STATE_CRITICAL;
else if (warning)
next_state = STATE_WARNING;
break;
case STATE_WARNING_HYST:
if (critical)
next_state = STATE_CRITICAL;
else if (warning)
next_state = STATE_WARNING;
else
next_state = STATE_NORMAL;
break;
case STATE_WARNING:
if (critical)
next_state = STATE_CRITICAL;
else if (!warning)
next_state = STATE_WARNING_HYST;
break;
case STATE_CRITICAL:
if (!critical && !warning)
next_state = STATE_WARNING;
break;
};
return next_state;
}
static void handle_normal_actions(int actions)
{
/* switch off the PA */
if (actions & TEMP_ACT_NORM_PA_ON) {
if (!is_sbts2050()) {
LOGP(DTEMP, LOGL_NOTICE,
"PA can only be switched-on on the master\n");
} else if (sbts2050_uc_set_pa_power(1) != 0) {
LOGP(DTEMP, LOGL_ERROR,
"Failed to switch on the PA\n");
} else {
LOGP(DTEMP, LOGL_NOTICE,
"Switched on the PA as normal action.\n");
}
}
if (actions & TEMP_ACT_NORM_SLAVE_ON) {
if (!is_sbts2050()) {
LOGP(DTEMP, LOGL_NOTICE,
"Slave on only possible on the sysmoBTS2050\n");
} else if (sbts2050_uc_set_slave_power(1) != 0) {
LOGP(DTEMP, LOGL_ERROR,
"Failed to switch on the slave BTS\n");
} else {
LOGP(DTEMP, LOGL_NOTICE,
"Switched on the slave as normal action.\n");
}
}
if (actions & TEMP_ACT_NORM_BTS_SRV_ON) {
LOGP(DTEMP, LOGL_NOTICE,
"Going to switch on the BTS service\n");
/*
* TODO: use/create something like nspawn that serializes
* and used SIGCHLD/waitpid to pick up the dead processes
* without invoking shell.
*/
system("/bin/systemctl start sysmobts.service");
}
}
static void handle_actions(int actions)
{
/* switch off the PA */
if (actions & TEMP_ACT_PA_OFF) {
if (!is_sbts2050()) {
LOGP(DTEMP, LOGL_NOTICE,
"PA can only be switched-off on the master\n");
} else if (sbts2050_uc_set_pa_power(0) != 0) {
LOGP(DTEMP, LOGL_ERROR,
"Failed to switch off the PA. Stop BTS?\n");
} else {
LOGP(DTEMP, LOGL_NOTICE,
"Switched off the PA due temperature.\n");
}
}
if (actions & TEMP_ACT_SLAVE_OFF) {
if (!is_sbts2050()) {
LOGP(DTEMP, LOGL_NOTICE,
"Slave off only possible on the sysmoBTS2050\n");
} else if (sbts2050_uc_set_slave_power(0) != 0) {
LOGP(DTEMP, LOGL_ERROR,
"Failed to switch off the slave BTS\n");
} else {
LOGP(DTEMP, LOGL_NOTICE,
"Switched off the slave due temperature\n");
}
}
if (actions & TEMP_ACT_BTS_SRV_OFF) {
LOGP(DTEMP, LOGL_NOTICE,
"Going to switch off the BTS service\n");
/*
* TODO: use/create something like nspawn that serializes
* and used SIGCHLD/waitpid to pick up the dead processes
* without invoking shell.
*/
system("/bin/systemctl stop sysmobts.service");
}
}
/**
* Go back to normal! Depending on the configuration execute the normal
* actions that could (start to) undo everything we did in the other
* states. What is still missing is the power increase/decrease depending
* on the state. E.g. starting from WARNING_HYST we might want to slowly
* ramp up the output power again.
*/
static void execute_normal_act(struct sysmobts_mgr_instance *manager)
{
LOGP(DTEMP, LOGL_NOTICE, "System is back to normal temperature.\n");
handle_normal_actions(manager->action_norm);
}
static void execute_warning_act(struct sysmobts_mgr_instance *manager)
{
LOGP(DTEMP, LOGL_NOTICE, "System has reached temperature warning.\n");
handle_actions(manager->action_warn);
}
static void execute_critical_act(struct sysmobts_mgr_instance *manager)
{
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical warning.\n");
handle_actions(manager->action_crit);
}
static void sysmobts_mgr_temp_handle(struct sysmobts_mgr_instance *manager,
int critical, int warning)
{
int new_state = next_state(manager->state, critical, warning);
/* Nothing changed */
if (new_state < 0)
return;
LOGP(DTEMP, LOGL_NOTICE, "Moving from state %s to %s.\n",
get_value_string(state_names, manager->state),
get_value_string(state_names, new_state));
manager->state = new_state;
switch (manager->state) {
case STATE_NORMAL:
execute_normal_act(manager);
break;
case STATE_WARNING_HYST:
/* do nothing? Maybe start to increase transmit power? */
break;
case STATE_WARNING:
execute_warning_act(manager);
break;
case STATE_CRITICAL:
execute_critical_act(manager);
break;
};
}
static void temp_ctrl_check()
{
int rc;
int warn_thresh_passed = 0;
int crit_thresh_passed = 0;
LOGP(DTEMP, LOGL_DEBUG, "Going to check the temperature.\n");
/* Read the current digital temperature */
rc = sysmobts_temp_get(SYSMOBTS_TEMP_DIGITAL, SYSMOBTS_TEMP_INPUT);
if (rc < 0) {
LOGP(DTEMP, LOGL_ERROR,
"Failed to read the digital temperature. rc=%d\n", rc);
warn_thresh_passed = crit_thresh_passed = 1;
} else {
int temp = rc / 1000;
if (temp > s_mgr->digital_limit.thresh_warn)
warn_thresh_passed = 1;
if (temp > s_mgr->digital_limit.thresh_crit)
crit_thresh_passed = 1;
LOGP(DTEMP, LOGL_DEBUG, "Digital temperature is: %d\n", temp);
}
/* Read the current RF temperature */
rc = sysmobts_temp_get(SYSMOBTS_TEMP_RF, SYSMOBTS_TEMP_INPUT);
if (rc < 0) {
LOGP(DTEMP, LOGL_ERROR,
"Failed to read the RF temperature. rc=%d\n", rc);
warn_thresh_passed = crit_thresh_passed = 1;
} else {
int temp = rc / 1000;
if (temp > s_mgr->rf_limit.thresh_warn)
warn_thresh_passed = 1;
if (temp > s_mgr->rf_limit.thresh_crit)
crit_thresh_passed = 1;
LOGP(DTEMP, LOGL_DEBUG, "RF temperature is: %d\n", temp);
}
if (is_sbts2050()) {
int temp_pa, temp_board;
rc = sbts2050_uc_check_temp(&temp_pa, &temp_board);
if (rc != 0) {
/* XXX what do here? */
LOGP(DTEMP, LOGL_ERROR,
"Failed to read the temperature! Reboot?!\n");
warn_thresh_passed = 1;
crit_thresh_passed = 1;
} else {
LOGP(DTEMP, LOGL_DEBUG, "SBTS2050 board(%d) PA(%d)\n",
temp_board, temp_pa);
if (temp_pa > s_mgr->pa_limit.thresh_warn)
warn_thresh_passed = 1;
if (temp_pa > s_mgr->pa_limit.thresh_crit)
crit_thresh_passed = 1;
if (temp_board > s_mgr->board_limit.thresh_warn)
warn_thresh_passed = 1;
if (temp_board > s_mgr->board_limit.thresh_crit)
crit_thresh_passed = 1;
}
}
sysmobts_mgr_temp_handle(s_mgr, crit_thresh_passed, warn_thresh_passed);
}
static void temp_ctrl_check_cb(void *unused)
{
temp_ctrl_check();
/* Check every two minutes? XXX make it configurable! */
osmo_timer_schedule(&temp_ctrl_timer, 2 * 60, 0);
}
int sysmobts_mgr_temp_init(struct sysmobts_mgr_instance *mgr)
{
s_mgr = mgr;
temp_ctrl_timer.cb = temp_ctrl_check_cb;
temp_ctrl_check_cb(NULL);
return 0;
}

View File

@@ -0,0 +1,522 @@
/* (C) 2014 by sysmocom - s.f.m.c. GmbH
*
* All Rights Reserved
*
* Author: Alvaro Neira Ayuso <anayuso@sysmocom.de>
*
* 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 <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <ctype.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/misc.h>
#include <osmo-bts/logging.h>
#include "sysmobts_misc.h"
#include "sysmobts_mgr.h"
#include "btsconfig.h"
static struct sysmobts_mgr_instance *s_mgr;
static const char copyright[] =
"(C) 2012 by Harald Welte <laforge@gnumonks.org>\r\n"
"(C) 2014 by Holger Hans Peter Freyther\r\n"
"License AGPLv3+: GNU AGPL version 2 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n";
static enum node_type go_to_parent(struct vty *vty)
{
switch (vty->node) {
case MGR_NODE:
vty->node = CONFIG_NODE;
break;
case ACT_NORM_NODE:
case ACT_WARN_NODE:
case ACT_CRIT_NODE:
case LIMIT_RF_NODE:
case LIMIT_DIGITAL_NODE:
case LIMIT_BOARD_NODE:
case LIMIT_PA_NODE:
vty->node = MGR_NODE;
break;
default:
vty->node = CONFIG_NODE;
}
return vty->node;
}
static int is_config_node(struct vty *vty, int node)
{
switch (node) {
case MGR_NODE:
case ACT_NORM_NODE:
case ACT_WARN_NODE:
case ACT_CRIT_NODE:
case LIMIT_RF_NODE:
case LIMIT_DIGITAL_NODE:
case LIMIT_BOARD_NODE:
case LIMIT_PA_NODE:
return 1;
default:
return 0;
}
}
static struct vty_app_info vty_info = {
.name = "sysmobts-mgr",
.version = PACKAGE_VERSION,
.go_parent_cb = go_to_parent,
.is_config_node = is_config_node,
.copyright = copyright,
};
#define MGR_STR "Configure sysmobts-mgr\n"
static struct cmd_node mgr_node = {
MGR_NODE,
"%s(sysmobts-mgr)# ",
1,
};
static struct cmd_node act_norm_node = {
ACT_NORM_NODE,
"%s(action-normal)# ",
1,
};
static struct cmd_node act_warn_node = {
ACT_WARN_NODE,
"%s(action-warn)# ",
1,
};
static struct cmd_node act_crit_node = {
ACT_CRIT_NODE,
"%s(action-critical)# ",
1,
};
static struct cmd_node limit_rf_node = {
LIMIT_RF_NODE,
"%s(limit-rf)# ",
1,
};
static struct cmd_node limit_digital_node = {
LIMIT_DIGITAL_NODE,
"%s(limit-digital)# ",
1,
};
static struct cmd_node limit_board_node = {
LIMIT_BOARD_NODE,
"%s(limit-board)# ",
1,
};
static struct cmd_node limit_pa_node = {
LIMIT_PA_NODE,
"%s(limit-pa)# ",
1,
};
DEFUN(cfg_mgr, cfg_mgr_cmd,
"sysmobts-mgr",
MGR_STR)
{
vty->node = MGR_NODE;
return CMD_SUCCESS;
}
static void write_temp_limit(struct vty *vty, const char *name,
struct sysmobts_temp_limit *limit)
{
vty_out(vty, " %s%s", name, VTY_NEWLINE);
vty_out(vty, " threshold warning %d%s",
limit->thresh_warn, VTY_NEWLINE);
vty_out(vty, " threshold critical %d%s",
limit->thresh_crit, VTY_NEWLINE);
}
static void write_norm_action(struct vty *vty, const char *name, int actions)
{
vty_out(vty, " %s%s", name, VTY_NEWLINE);
vty_out(vty, " %spa-on%s",
(actions & TEMP_ACT_NORM_PA_ON) ? "" : "no ", VTY_NEWLINE);
vty_out(vty, " %sbts-service-on%s",
(actions & TEMP_ACT_NORM_BTS_SRV_ON) ? "" : "no ", VTY_NEWLINE);
vty_out(vty, " %sslave-on%s",
(actions & TEMP_ACT_NORM_SLAVE_ON) ? "" : "no ", VTY_NEWLINE);
}
static void write_action(struct vty *vty, const char *name, int actions)
{
vty_out(vty, " %s%s", name, VTY_NEWLINE);
#if 0
vty_out(vty, " %spower-control%s",
(actions & TEMP_ACT_PWR_CONTRL) ? "" : "no ", VTY_NEWLINE);
/* only on the sysmobts 2050 */
vty_out(vty, " %smaster-off%s",
(actions & TEMP_ACT_MASTER_OFF) ? "" : "no ", VTY_NEWLINE);
vty_out(vty, " %sslave-off%s",
(actions & TEMP_ACT_MASTER_OFF) ? "" : "no ", VTY_NEWLINE);
#endif
vty_out(vty, " %spa-off%s",
(actions & TEMP_ACT_PA_OFF) ? "" : "no ", VTY_NEWLINE);
vty_out(vty, " %sbts-service-off%s",
(actions & TEMP_ACT_BTS_SRV_OFF) ? "" : "no ", VTY_NEWLINE);
vty_out(vty, " %sslave-off%s",
(actions & TEMP_ACT_SLAVE_OFF) ? "" : "no ", VTY_NEWLINE);
}
static int config_write_mgr(struct vty *vty)
{
vty_out(vty, "sysmobts-mgr%s", VTY_NEWLINE);
write_temp_limit(vty, "limits rf", &s_mgr->rf_limit);
write_temp_limit(vty, "limits digital", &s_mgr->digital_limit);
write_temp_limit(vty, "limits board", &s_mgr->board_limit);
write_temp_limit(vty, "limits pa", &s_mgr->pa_limit);
write_norm_action(vty, "actions normal", s_mgr->action_norm);
write_action(vty, "actions warn", s_mgr->action_warn);
write_action(vty, "actions critical", s_mgr->action_crit);
return CMD_SUCCESS;
}
static int config_write_dummy(struct vty *vty)
{
return CMD_SUCCESS;
}
#define CFG_LIMIT(name, expl, switch_to, variable) \
DEFUN(cfg_limit_##name, cfg_limit_##name##_cmd, \
"limits " #name, \
"Configure Limits\n" expl) \
{ \
vty->node = switch_to; \
vty->index = &s_mgr->variable; \
return CMD_SUCCESS; \
}
CFG_LIMIT(rf, "RF\n", LIMIT_RF_NODE, rf_limit)
CFG_LIMIT(digital, "Digital\n", LIMIT_DIGITAL_NODE, digital_limit)
CFG_LIMIT(board, "Board\n", LIMIT_BOARD_NODE, board_limit)
CFG_LIMIT(pa, "Power Amplifier\n", LIMIT_PA_NODE, pa_limit)
#undef CFG_LIMIT
DEFUN(cfg_limit_warning, cfg_thresh_warning_cmd,
"threshold warning <0-200>",
"Threshold to reach\n" "Warning level\n" "Range\n")
{
struct sysmobts_temp_limit *limit = vty->index;
limit->thresh_warn = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_limit_crit, cfg_thresh_crit_cmd,
"threshold critical <0-200>",
"Threshold to reach\n" "Severe level\n" "Range\n")
{
struct sysmobts_temp_limit *limit = vty->index;
limit->thresh_crit = atoi(argv[0]);
return CMD_SUCCESS;
}
#define CFG_ACTION(name, expl, switch_to, variable) \
DEFUN(cfg_action_##name, cfg_action_##name##_cmd, \
"actions " #name, \
"Configure Actions\n" expl) \
{ \
vty->node = switch_to; \
vty->index = &s_mgr->variable; \
return CMD_SUCCESS; \
}
CFG_ACTION(normal, "Normal Actions\n", ACT_NORM_NODE, action_norm)
CFG_ACTION(warn, "Warning Actions\n", ACT_WARN_NODE, action_warn)
CFG_ACTION(critical, "Critical Actions\n", ACT_CRIT_NODE, action_crit)
#undef CFG_ACTION
DEFUN(cfg_action_pa_on, cfg_action_pa_on_cmd,
"pa-on",
"Switch the Power Amplifier on\n")
{
int *action = vty->index;
*action |= TEMP_ACT_NORM_PA_ON;
return CMD_SUCCESS;
}
DEFUN(cfg_no_action_pa_on, cfg_no_action_pa_on_cmd,
"no pa-on",
NO_STR "Switch the Power Amplifier on\n")
{
int *action = vty->index;
*action &= ~TEMP_ACT_NORM_PA_ON;
return CMD_SUCCESS;
}
DEFUN(cfg_action_bts_srv_on, cfg_action_bts_srv_on_cmd,
"bts-service-on",
"Start the systemd sysmobts.service\n")
{
int *action = vty->index;
*action |= TEMP_ACT_NORM_BTS_SRV_ON;
return CMD_SUCCESS;
}
DEFUN(cfg_no_action_bts_srv_on, cfg_no_action_bts_srv_on_cmd,
"no bts-service-on",
NO_STR "Start the systemd sysmobts.service\n")
{
int *action = vty->index;
*action &= ~TEMP_ACT_NORM_BTS_SRV_ON;
return CMD_SUCCESS;
}
DEFUN(cfg_action_slave_on, cfg_action_slave_on_cmd,
"slave-on",
"Power-on secondary device on sysmoBTS2050\n")
{
int *action = vty->index;
*action |= TEMP_ACT_NORM_SLAVE_ON;
return CMD_SUCCESS;
}
DEFUN(cfg_no_action_slave_on, cfg_no_action_slave_on_cmd,
"no slave-on",
NO_STR "Power-on secondary device on sysmoBTS2050\n")
{
int *action = vty->index;
*action &= ~TEMP_ACT_NORM_SLAVE_ON;
return CMD_SUCCESS;
}
DEFUN(cfg_action_pa_off, cfg_action_pa_off_cmd,
"pa-off",
"Switch the Power Amplifier off\n")
{
int *action = vty->index;
*action |= TEMP_ACT_PA_OFF;
return CMD_SUCCESS;
}
DEFUN(cfg_no_action_pa_off, cfg_no_action_pa_off_cmd,
"no pa-off",
NO_STR "Do not switch off the Power Amplifier\n")
{
int *action = vty->index;
*action &= ~TEMP_ACT_PA_OFF;
return CMD_SUCCESS;
}
DEFUN(cfg_action_bts_srv_off, cfg_action_bts_srv_off_cmd,
"bts-service-off",
"Stop the systemd sysmobts.service\n")
{
int *action = vty->index;
*action |= TEMP_ACT_BTS_SRV_OFF;
return CMD_SUCCESS;
}
DEFUN(cfg_no_action_bts_srv_off, cfg_no_action_bts_srv_off_cmd,
"no bts-service-off",
NO_STR "Stop the systemd sysmobts.service\n")
{
int *action = vty->index;
*action &= ~TEMP_ACT_BTS_SRV_OFF;
return CMD_SUCCESS;
}
DEFUN(cfg_action_slave_off, cfg_action_slave_off_cmd,
"slave-off",
"Power-off secondary device on sysmoBTS2050\n")
{
int *action = vty->index;
*action |= TEMP_ACT_SLAVE_OFF;
return CMD_SUCCESS;
}
DEFUN(cfg_no_action_slave_off, cfg_no_action_slave_off_cmd,
"no slave-off",
NO_STR "Power-off secondary device on sysmoBTS2050\n")
{
int *action = vty->index;
*action &= ~TEMP_ACT_SLAVE_OFF;
return CMD_SUCCESS;
}
DEFUN(show_mgr, show_mgr_cmd, "show manager",
SHOW_STR "Display information about the manager")
{
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);
vty_out(vty, " Digital: %f Celcius%s",
sysmobts_temp_get(SYSMOBTS_TEMP_DIGITAL,
SYSMOBTS_TEMP_INPUT) / 1000.0f,
VTY_NEWLINE);
vty_out(vty, " RF: %f Celcius%s",
sysmobts_temp_get(SYSMOBTS_TEMP_RF,
SYSMOBTS_TEMP_INPUT) / 1000.0f,
VTY_NEWLINE);
if (is_sbts2050()) {
int temp_pa, temp_board;
struct sbts2050_power_status status;
vty_out(vty, " sysmoBTS 2050 is %s%s",
is_sbts2050_master() ? "master" : "slave", VTY_NEWLINE);
sbts2050_uc_check_temp(&temp_pa, &temp_board);
vty_out(vty, " sysmoBTS 2050 PA: %d Celcius%s", temp_pa, VTY_NEWLINE);
vty_out(vty, " sysmoBTS 2050 PA: %d Celcius%s", temp_board, VTY_NEWLINE);
sbts2050_uc_get_status(&status);
vty_out(vty, "Power Status%s", VTY_NEWLINE);
vty_out(vty, " Main Supply :(ON) [(24.00)Vdc, %4.2f A]%s",
status.main_supply_current, VTY_NEWLINE);
vty_out(vty, " Master SF : %s [%6.2f Vdc, %4.2f A]%s",
status.master_enabled ? "ON " : "OFF",
status.master_voltage, status.master_current,
VTY_NEWLINE);
vty_out(vty, " Slave SF : %s [%6.2f Vdc, %4.2f A]%s",
status.slave_enabled ? "ON" : "OFF",
status.slave_voltage, status.slave_current,
VTY_NEWLINE);
vty_out(vty, " Power Amp : %s [%6.2f Vdc, %4.2f A]%s",
status.pa_enabled ? "ON" : "OFF",
status.pa_voltage, status.pa_current,
VTY_NEWLINE);
vty_out(vty, " PA Bias : %s [%6.2f Vdc, ---- A]%s",
status.pa_enabled ? "ON" : "OFF",
status.pa_bias_voltage,
VTY_NEWLINE);
}
return CMD_SUCCESS;
}
static void register_limit(int limit)
{
install_element(limit, &cfg_thresh_warning_cmd);
install_element(limit, &cfg_thresh_crit_cmd);
}
static void register_normal_action(int act)
{
install_element(act, &cfg_action_pa_on_cmd);
install_element(act, &cfg_no_action_pa_on_cmd);
install_element(act, &cfg_action_bts_srv_on_cmd);
install_element(act, &cfg_no_action_bts_srv_on_cmd);
/* these only work on the sysmobts 2050 */
install_element(act, &cfg_action_slave_on_cmd);
install_element(act, &cfg_no_action_slave_on_cmd);
}
static void register_action(int act)
{
#if 0
install_element(act, &cfg_action_pwr_contrl_cmd);
install_element(act, &cfg_no_action_pwr_contrl_cmd);
#endif
install_element(act, &cfg_action_pa_off_cmd);
install_element(act, &cfg_no_action_pa_off_cmd);
install_element(act, &cfg_action_bts_srv_off_cmd);
install_element(act, &cfg_no_action_bts_srv_off_cmd);
/* these only work on the sysmobts 2050 */
install_element(act, &cfg_action_slave_off_cmd);
install_element(act, &cfg_no_action_slave_off_cmd);
}
int sysmobts_mgr_vty_init(void)
{
vty_init(&vty_info);
install_element_ve(&show_mgr_cmd);
install_node(&mgr_node, config_write_mgr);
install_element(CONFIG_NODE, &cfg_mgr_cmd);
vty_install_default(MGR_NODE);
/* install the limit nodes */
install_node(&limit_rf_node, config_write_dummy);
install_element(MGR_NODE, &cfg_limit_rf_cmd);
register_limit(LIMIT_RF_NODE);
vty_install_default(LIMIT_RF_NODE);
install_node(&limit_digital_node, config_write_dummy);
install_element(MGR_NODE, &cfg_limit_digital_cmd);
register_limit(LIMIT_DIGITAL_NODE);
vty_install_default(LIMIT_DIGITAL_NODE);
install_node(&limit_board_node, config_write_dummy);
install_element(MGR_NODE, &cfg_limit_board_cmd);
register_limit(LIMIT_BOARD_NODE);
vty_install_default(LIMIT_BOARD_NODE);
install_node(&limit_pa_node, config_write_dummy);
install_element(MGR_NODE, &cfg_limit_pa_cmd);
register_limit(LIMIT_PA_NODE);
vty_install_default(LIMIT_PA_NODE);
/* install the normal node */
install_node(&act_norm_node, config_write_dummy);
install_element(MGR_NODE, &cfg_action_normal_cmd);
register_normal_action(ACT_NORM_NODE);
/* install the warning and critical node */
install_node(&act_warn_node, config_write_dummy);
install_element(MGR_NODE, &cfg_action_warn_cmd);
register_action(ACT_WARN_NODE);
vty_install_default(ACT_WARN_NODE);
install_node(&act_crit_node, config_write_dummy);
install_element(MGR_NODE, &cfg_action_critical_cmd);
register_action(ACT_CRIT_NODE);
vty_install_default(ACT_CRIT_NODE);
return 0;
}
int sysmobts_mgr_parse_config(struct sysmobts_mgr_instance *manager)
{
int rc;
s_mgr = manager;
rc = vty_read_config_file(s_mgr->config_file, NULL);
if (rc < 0) {
fprintf(stderr, "Failed to parse the config file: '%s'\n",
s_mgr->config_file);
return rc;
}
return 0;
}

View File

@@ -0,0 +1,275 @@
/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <limits.h>
#include <time.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/application.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include "btsconfig.h"
#include "sysmobts_misc.h"
#include "sysmobts_par.h"
#include "sysmobts_mgr.h"
/*********************************************************************
* Temperature handling
*********************************************************************/
#define TEMP_PATH "/sys/class/hwmon/hwmon0/device/temp%u_%s"
static const char *temp_type_str[_NUM_TEMP_TYPES] = {
[SYSMOBTS_TEMP_INPUT] = "input",
[SYSMOBTS_TEMP_LOWEST] = "lowest",
[SYSMOBTS_TEMP_HIGHEST] = "highest",
};
int sysmobts_temp_get(enum sysmobts_temp_sensor sensor,
enum sysmobts_temp_type type)
{
char buf[PATH_MAX];
char tempstr[8];
int fd, rc;
if (sensor < SYSMOBTS_TEMP_DIGITAL ||
sensor > SYSMOBTS_TEMP_RF)
return -EINVAL;
if (type >= ARRAY_SIZE(temp_type_str))
return -EINVAL;
snprintf(buf, sizeof(buf)-1, TEMP_PATH, sensor, temp_type_str[type]);
buf[sizeof(buf)-1] = '\0';
fd = open(buf, O_RDONLY);
if (fd < 0)
return fd;
rc = read(fd, tempstr, sizeof(tempstr));
tempstr[sizeof(tempstr)-1] = '\0';
if (rc < 0) {
close(fd);
return rc;
}
if (rc == 0) {
close(fd);
return -EIO;
}
close(fd);
return atoi(tempstr);
}
static const struct {
const char *name;
enum sysmobts_temp_sensor sensor;
enum sysmobts_par ee_par;
} temp_data[] = {
{
.name = "digital",
.sensor = SYSMOBTS_TEMP_DIGITAL,
.ee_par = SYSMOBTS_PAR_TEMP_DIG_MAX,
}, {
.name = "rf",
.sensor = SYSMOBTS_TEMP_RF,
.ee_par = SYSMOBTS_PAR_TEMP_RF_MAX,
}
};
void sysmobts_check_temp(int no_eeprom_write)
{
int temp_old[ARRAY_SIZE(temp_data)];
int temp_hi[ARRAY_SIZE(temp_data)];
int temp_cur[ARRAY_SIZE(temp_data)];
int i, rc;
for (i = 0; i < ARRAY_SIZE(temp_data); i++) {
int ret;
rc = sysmobts_par_get_int(temp_data[i].ee_par, &ret);
temp_old[i] = ret * 1000;
temp_hi[i] = sysmobts_temp_get(temp_data[i].sensor,
SYSMOBTS_TEMP_HIGHEST);
temp_cur[i] = sysmobts_temp_get(temp_data[i].sensor,
SYSMOBTS_TEMP_INPUT);
if ((temp_cur[i] < 0 && temp_cur[i] > -1000) ||
(temp_hi[i] < 0 && temp_hi[i] > -1000)) {
LOGP(DTEMP, LOGL_ERROR, "Error reading temperature\n");
return;
}
LOGP(DTEMP, LOGL_DEBUG, "Current %s temperature: %d.%d C\n",
temp_data[i].name, temp_cur[i]/1000, temp_cur[i]%1000);
if (temp_hi[i] > temp_old[i]) {
LOGP(DTEMP, LOGL_NOTICE, "New maximum %s "
"temperature: %d.%d C\n", temp_data[i].name,
temp_hi[i]/1000, temp_hi[i]%1000);
if (!no_eeprom_write) {
rc = sysmobts_par_set_int(SYSMOBTS_PAR_TEMP_DIG_MAX,
temp_hi[0]/1000);
if (rc < 0)
LOGP(DTEMP, LOGL_ERROR, "error writing new %s "
"max temp %d (%s)\n", temp_data[i].name,
rc, strerror(errno));
}
}
}
}
/*********************************************************************
* Hours handling
*********************************************************************/
static time_t last_update;
int sysmobts_update_hours(int no_eeprom_write)
{
time_t now = time(NULL);
int rc, op_hrs;
/* first time after start of manager program */
if (last_update == 0) {
last_update = now;
rc = sysmobts_par_get_int(SYSMOBTS_PAR_HOURS, &op_hrs);
if (rc < 0) {
LOGP(DTEMP, LOGL_ERROR, "Unable to read "
"operational hours: %d (%s)\n", rc,
strerror(errno));
return rc;
}
LOGP(DTEMP, LOGL_INFO, "Total hours of Operation: %u\n",
op_hrs);
return 0;
}
if (now >= last_update + 3600) {
rc = sysmobts_par_get_int(SYSMOBTS_PAR_HOURS, &op_hrs);
if (rc < 0) {
LOGP(DTEMP, LOGL_ERROR, "Unable to read "
"operational hours: %d (%s)\n", rc,
strerror(errno));
return rc;
}
/* number of hours to increase */
op_hrs += (now-last_update)/3600;
LOGP(DTEMP, LOGL_INFO, "Total hours of Operation: %u\n",
op_hrs);
if (!no_eeprom_write) {
rc = sysmobts_par_set_int(SYSMOBTS_PAR_HOURS, op_hrs);
if (rc < 0)
return rc;
}
last_update = now;
}
return 0;
}
/*********************************************************************
* Firmware reloading
*********************************************************************/
#define SYSMOBTS_FW_PATH "/lib/firmware"
static const char *fw_names[_NUM_FW] = {
[SYSMOBTS_FW_FPGA] = "sysmobts-v2.bit",
[SYSMOBTS_FW_DSP] = "sysmobts-v2.out",
};
static const char *fw_devs[_NUM_FW] = {
[SYSMOBTS_FW_FPGA] = "/dev/fpgadl_par0",
[SYSMOBTS_FW_DSP] = "/dev/dspdl_dm644x_0",
};
int sysmobts_firmware_reload(enum sysmobts_firmware_type type)
{
char name[PATH_MAX];
uint8_t buf[1024];
int fd_in, fd_out, rc;
if (type >= _NUM_FW)
return -EINVAL;
snprintf(name, sizeof(name)-1, "%s/%s",
SYSMOBTS_FW_PATH, fw_names[type]);
name[sizeof(name)-1] = '\0';
fd_in = open(name, O_RDONLY);
if (fd_in < 0) {
LOGP(DFW, LOGL_ERROR, "unable ot open firmware file %s: %s\n",
name, strerror(errno));
return fd_in;
}
fd_out = open(fw_devs[type], O_WRONLY);
if (fd_out < 0) {
LOGP(DFW, LOGL_ERROR, "unable ot open firmware device %s: %s\n",
fw_devs[type], strerror(errno));
close(fd_in);
return fd_out;
}
while ((rc = read(fd_in, buf, sizeof(buf)))) {
int written;
if (rc < 0) {
LOGP(DFW, LOGL_ERROR, "error %d during read "
"from %s: %s\n", rc, name, strerror(errno));
close(fd_in);
close(fd_out);
return -EIO;
}
written = write(fd_out, buf, rc);
if (written < rc) {
LOGP(DFW, LOGL_ERROR, "short write during "
"fw write to %s\n", fw_devs[type]);
close(fd_in);
close(fd_out);
return -EIO;
}
}
close(fd_in);
close(fd_out);
return 0;
}

View File

@@ -0,0 +1,65 @@
#ifndef _SYSMOBTS_MISC_H
#define _SYSMOBTS_MISC_H
#include <stdint.h>
enum sysmobts_temp_sensor {
SYSMOBTS_TEMP_DIGITAL = 1,
SYSMOBTS_TEMP_RF = 2,
};
enum sysmobts_temp_type {
SYSMOBTS_TEMP_INPUT,
SYSMOBTS_TEMP_LOWEST,
SYSMOBTS_TEMP_HIGHEST,
_NUM_TEMP_TYPES
};
int sysmobts_temp_get(enum sysmobts_temp_sensor sensor,
enum sysmobts_temp_type type);
void sysmobts_check_temp(int no_eeprom_write);
int sysmobts_update_hours(int no_epprom_write);
enum sysmobts_firmware_type {
SYSMOBTS_FW_FPGA,
SYSMOBTS_FW_DSP,
_NUM_FW
};
int sysmobts_firmware_reload(enum sysmobts_firmware_type type);
int sysmobts_bts_type();
int sysmobts_trx_number();
int is_sbts2050(void);
int is_sbts2050_trx(int);
int is_sbts2050_master(void);
struct sbts2050_power_status {
float main_supply_current;
int master_enabled;
float master_voltage;
float master_current;
int slave_enabled;
float slave_voltage;
float slave_current;
int pa_enabled;
float pa_voltage;
float pa_current;
float pa_bias_voltage;
};
int sbts2050_uc_check_temp(int *temp_pa, int *temp_board);
int sbts2050_uc_set_power(int pmaster, int pslave, int ppa);
int sbts2050_uc_get_status(struct sbts2050_power_status *status);
int sbts2050_uc_set_pa_power(int on_off);
int sbts2050_uc_set_slave_power(int on_off);
void sbts2050_uc_initialize();
#endif

View File

@@ -0,0 +1,120 @@
/* Helper for netlink */
/*
* (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 <arpa/inet.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define NLMSG_TAIL(nmsg) \
((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
/**
* In case one binds to 0.0.0.0/INADDR_ANY and wants to know which source
* address will be used when sending a message this function can be used.
* It will ask the routing code of the kernel for the PREFSRC
*/
int source_for_dest(const struct in_addr *dest, struct in_addr *loc_source)
{
int fd, rc;
struct rtmsg *r;
struct rtattr *rta;
struct {
struct nlmsghdr n;
struct rtmsg r;
char buf[1024];
} req;
memset(&req, 0, sizeof(req));
fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE);
if (fd < 0) {
perror("nl socket");
return -1;
}
/* Send a rtmsg and ask for a response */
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
req.n.nlmsg_type = RTM_GETROUTE;
req.n.nlmsg_seq = 1;
/* Prepare the routing request */
req.r.rtm_family = AF_INET;
/* set the dest */
rta = NLMSG_TAIL(&req.n);
rta->rta_type = RTA_DST;
rta->rta_len = RTA_LENGTH(sizeof(*dest));
memcpy(RTA_DATA(rta), dest, sizeof(*dest));
/* update sizes for dest */
req.r.rtm_dst_len = sizeof(*dest) * 8;
req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_ALIGN(rta->rta_len);
rc = send(fd, &req, req.n.nlmsg_len, 0);
if (rc != req.n.nlmsg_len) {
perror("short write");
close(fd);
return -2;
}
/* now receive a response and parse it */
rc = recv(fd, &req, sizeof(req), 0);
if (rc <= 0) {
perror("short read");
close(fd);
return -3;
}
if (!NLMSG_OK(&req.n, rc) || req.n.nlmsg_type != RTM_NEWROUTE) {
close(fd);
return -4;
}
r = NLMSG_DATA(&req.n);
rc -= NLMSG_LENGTH(sizeof(*r));
rta = RTM_RTA(r);
while (RTA_OK(rta, rc)) {
if (rta->rta_type != RTA_PREFSRC) {
rta = RTA_NEXT(rta, rc);
continue;
}
/* we are done */
memcpy(loc_source, RTA_DATA(rta), RTA_PAYLOAD(rta));
close(fd);
return 0;
}
close(fd);
return -5;
}

View File

@@ -0,0 +1,24 @@
/*
* (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/>.
*
*/
#pragma once
struct in_addr;
int source_for_dest(const struct in_addr *dest, struct in_addr *loc_source);

View File

@@ -0,0 +1,291 @@
/* sysmobts - access to hardware related parameters */
/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <osmocom/core/utils.h>
#include "sysmobts_eeprom.h"
#include "sysmobts_par.h"
#include "eeprom.h"
#define EEPROM_PATH "/sys/devices/platform/i2c_davinci.1/i2c-1/1-0050/eeprom"
const struct value_string sysmobts_par_names[_NUM_SYSMOBTS_PAR+1] = {
{ SYSMOBTS_PAR_MAC, "ethaddr" },
{ SYSMOBTS_PAR_CLK_FACTORY, "clk-factory" },
{ SYSMOBTS_PAR_TEMP_DIG_MAX, "temp-dig-max" },
{ SYSMOBTS_PAR_TEMP_RF_MAX, "temp-rf-max" },
{ SYSMOBTS_PAR_SERNR, "serial-nr" },
{ SYSMOBTS_PAR_HOURS, "hours-running" },
{ SYSMOBTS_PAR_BOOTS, "boot-count" },
{ SYSMOBTS_PAR_KEY, "key" },
{ SYSMOBTS_PAR_MODEL_NR, "model-nr" },
{ SYSMOBTS_PAR_MODEL_FLAGS, "model-flags" },
{ SYSMOBTS_PAR_TRX_NR, "trx-nr" },
{ 0, NULL }
};
static struct {
int read;
struct sysmobts_eeprom ee;
} g_ee;
static struct sysmobts_eeprom *get_eeprom(int update_rqd)
{
if (update_rqd || g_ee.read == 0) {
int fd, rc;
fd = open(EEPROM_PATH, O_RDONLY);
if (fd < 0)
return NULL;
rc = read(fd, &g_ee.ee, sizeof(g_ee.ee));
close(fd);
if (rc < sizeof(g_ee.ee))
return NULL;
g_ee.read = 1;
}
return &g_ee.ee;
}
static int set_eeprom(struct sysmobts_eeprom *ee)
{
int fd, rc;
memcpy(&g_ee.ee, ee, sizeof(*ee));
fd = open(EEPROM_PATH, O_WRONLY);
if (fd < 0)
return fd;
rc = write(fd, ee, sizeof(*ee));
if (rc < sizeof(*ee)) {
close(fd);
return -EIO;
}
close(fd);
return 0;
}
int sysmobts_par_is_int(enum sysmobts_par par)
{
switch (par) {
case SYSMOBTS_PAR_CLK_FACTORY:
case SYSMOBTS_PAR_TEMP_DIG_MAX:
case SYSMOBTS_PAR_TEMP_RF_MAX:
case SYSMOBTS_PAR_SERNR:
case SYSMOBTS_PAR_HOURS:
case SYSMOBTS_PAR_BOOTS:
case SYSMOBTS_PAR_MODEL_NR:
case SYSMOBTS_PAR_MODEL_FLAGS:
case SYSMOBTS_PAR_TRX_NR:
return 1;
default:
return 0;
}
}
int sysmobts_par_get_int(enum sysmobts_par par, int *ret)
{
eeprom_RfClockCal_t rf_clk;
eeprom_Error_t err;
struct sysmobts_eeprom *ee = get_eeprom(0);
if (!ee)
return -EIO;
if (par >= _NUM_SYSMOBTS_PAR)
return -ENODEV;
switch (par) {
case SYSMOBTS_PAR_CLK_FACTORY:
err = eeprom_ReadRfClockCal(&rf_clk);
if (err != EEPROM_SUCCESS)
return -EIO;
*ret = rf_clk.iClkCor;
break;
case SYSMOBTS_PAR_TEMP_DIG_MAX:
*ret = ee->temp1_max;
break;
case SYSMOBTS_PAR_TEMP_RF_MAX:
*ret = ee->temp2_max;
break;
case SYSMOBTS_PAR_SERNR:
*ret = ee->serial_nr;
break;
case SYSMOBTS_PAR_HOURS:
*ret = ee->operational_hours;
break;
case SYSMOBTS_PAR_BOOTS:
*ret = ee->boot_count;
break;
case SYSMOBTS_PAR_MODEL_NR:
*ret = ee->model_nr;
break;
case SYSMOBTS_PAR_MODEL_FLAGS:
*ret = ee->model_flags;
break;
case SYSMOBTS_PAR_TRX_NR:
*ret = ee->trx_nr;
break;
default:
return -EINVAL;
}
return 0;
}
int sysmobts_par_set_int(enum sysmobts_par par, int val)
{
eeprom_RfClockCal_t rf_clk;
eeprom_Error_t err;
struct sysmobts_eeprom *ee = get_eeprom(1);
if (!ee)
return -EIO;
if (par >= _NUM_SYSMOBTS_PAR)
return -ENODEV;
switch (par) {
case SYSMOBTS_PAR_CLK_FACTORY:
err = eeprom_ReadRfClockCal(&rf_clk);
if (err != EEPROM_SUCCESS)
return -EIO;
rf_clk.iClkCor = val;
err = eeprom_WriteRfClockCal(&rf_clk);
if (err != EEPROM_SUCCESS)
return -EIO;
break;
case SYSMOBTS_PAR_TEMP_DIG_MAX:
ee->temp1_max = val;
break;
case SYSMOBTS_PAR_TEMP_RF_MAX:
ee->temp2_max = val;
break;
case SYSMOBTS_PAR_SERNR:
ee->serial_nr = val;
break;
case SYSMOBTS_PAR_HOURS:
ee->operational_hours = val;
break;
case SYSMOBTS_PAR_BOOTS:
ee->boot_count = val;
break;
case SYSMOBTS_PAR_MODEL_NR:
ee->model_nr = val;
break;
case SYSMOBTS_PAR_MODEL_FLAGS:
ee->model_flags = val;
break;
case SYSMOBTS_PAR_TRX_NR:
ee->trx_nr = val;
break;
default:
return -EINVAL;
}
set_eeprom(ee);
return 0;
}
int sysmobts_par_get_buf(enum sysmobts_par par, uint8_t *buf,
unsigned int size)
{
uint8_t *ptr;
unsigned int len;
struct sysmobts_eeprom *ee = get_eeprom(0);
if (!ee)
return -EIO;
if (par >= _NUM_SYSMOBTS_PAR)
return -ENODEV;
switch (par) {
case SYSMOBTS_PAR_MAC:
ptr = ee->eth_mac;
len = sizeof(ee->eth_mac);
break;
case SYSMOBTS_PAR_KEY:
ptr = ee->gpg_key;
len = sizeof(ee->gpg_key);
break;
default:
return -EINVAL;
}
if (size < len)
len = size;
memcpy(buf, ptr, len);
return len;
}
int sysmobts_par_set_buf(enum sysmobts_par par, const uint8_t *buf,
unsigned int size)
{
uint8_t *ptr;
unsigned int len;
struct sysmobts_eeprom *ee = get_eeprom(0);
if (!ee)
return -EIO;
if (par >= _NUM_SYSMOBTS_PAR)
return -ENODEV;
switch (par) {
case SYSMOBTS_PAR_MAC:
ptr = ee->eth_mac;
len = sizeof(ee->eth_mac);
break;
case SYSMOBTS_PAR_KEY:
ptr = ee->gpg_key;
len = sizeof(ee->gpg_key);
break;
default:
return -EINVAL;
}
if (len < size)
size = len;
memcpy(ptr, buf, size);
return len;
}

View File

@@ -0,0 +1,32 @@
#ifndef _SYSMOBTS_PAR_H
#define _SYSMOBTS_PAR_H
#include <osmocom/core/utils.h>
enum sysmobts_par {
SYSMOBTS_PAR_MAC,
SYSMOBTS_PAR_CLK_FACTORY,
SYSMOBTS_PAR_TEMP_DIG_MAX,
SYSMOBTS_PAR_TEMP_RF_MAX,
SYSMOBTS_PAR_SERNR,
SYSMOBTS_PAR_HOURS,
SYSMOBTS_PAR_BOOTS,
SYSMOBTS_PAR_KEY,
SYSMOBTS_PAR_MODEL_NR,
SYSMOBTS_PAR_MODEL_FLAGS,
SYSMOBTS_PAR_TRX_NR,
_NUM_SYSMOBTS_PAR
};
extern const struct value_string sysmobts_par_names[_NUM_SYSMOBTS_PAR+1];
int sysmobts_par_get_int(enum sysmobts_par par, int *ret);
int sysmobts_par_set_int(enum sysmobts_par par, int val);
int sysmobts_par_get_buf(enum sysmobts_par par, uint8_t *buf,
unsigned int size);
int sysmobts_par_set_buf(enum sysmobts_par par, const uint8_t *buf,
unsigned int size);
int sysmobts_par_is_int(enum sysmobts_par par);
#endif

View File

@@ -0,0 +1,154 @@
/* sysmobts-util - access to hardware related parameters */
/* (C) 2012-2013 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include "sysmobts_par.h"
enum act {
ACT_GET,
ACT_SET,
};
static enum act action;
static char *write_arg;
static int void_warranty;
static void print_help()
{
const struct value_string *par = sysmobts_par_names;
printf("sysmobts-util [--void-warranty -r | -w value] param_name\n");
printf("Possible param names:\n");
for (; par->str != NULL; par += 1) {
if (!sysmobts_par_is_int(par->value))
continue;
printf(" %s\n", par->str);
}
}
static int parse_options(int argc, char **argv)
{
while (1) {
int option_idx = 0, c;
static const struct option long_options[] = {
{ "help", 0, 0, 'h' },
{ "read", 0, 0, 'r' },
{ "void-warranty", 0, 0, 1000},
{ "write", 1, 0, 'w' },
{ 0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "rw:h",
long_options, &option_idx);
if (c == -1)
break;
switch (c) {
case 'r':
action = ACT_GET;
break;
case 'w':
action = ACT_SET;
write_arg = optarg;
break;
case 'h':
print_help();
return -1;
break;
case 1000:
printf("Will void warranty on write.\n");
void_warranty = 1;
break;
default:
return -1;
}
}
return 0;
}
int main(int argc, char **argv)
{
const char *parname;
enum sysmobts_par par;
int rc, val;
rc = parse_options(argc, argv);
if (rc < 0)
exit(2);
if (optind >= argc) {
fprintf(stderr, "You must specify the parameter name\n");
exit(2);
}
parname = argv[optind];
rc = get_string_value(sysmobts_par_names, parname);
if (rc < 0) {
fprintf(stderr, "`%s' is not a valid parameter\n", parname);
exit(2);
} else
par = rc;
switch (action) {
case ACT_GET:
rc = sysmobts_par_get_int(par, &val);
if (rc < 0) {
fprintf(stderr, "Error %d\n", rc);
goto err;
}
printf("%d\n", val);
break;
case ACT_SET:
rc = sysmobts_par_get_int(par, &val);
if (rc < 0) {
fprintf(stderr, "Error %d\n", rc);
goto err;
}
if (val != 0xFFFF && val != 0xFF && val != 0xFFFFFFFF && !void_warranty) {
fprintf(stderr, "Parameter is already set!\r\n");
goto err;
}
rc = sysmobts_par_set_int(par, atoi(write_arg));
if (rc < 0) {
fprintf(stderr, "Error %d\n", rc);
goto err;
}
printf("Success setting %s=%d\n", parname,
atoi(write_arg));
break;
default:
fprintf(stderr, "Unsupported action\n");
goto err;
}
exit(0);
err:
exit(1);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,129 @@
/* Beginnings of an OML router */
/* (C) 2014 by sysmocom s.f.m.c. GmbH
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "oml_router.h"
#include <osmo-bts/bts.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/oml.h>
#include <osmo-bts/msg_utils.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/select.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
static int oml_router_read_cb(struct osmo_fd *fd, unsigned int what)
{
struct msgb *msg;
int rc;
msg = oml_msgb_alloc();
if (!msg) {
LOGP(DL1C, LOGL_ERROR, "Failed to allocate oml msgb.\n");
return -1;
}
rc = recv(fd->fd, msg->tail, msg->data_len, 0);
if (rc <= 0) {
close(fd->fd);
osmo_fd_unregister(fd);
fd->fd = -1;
goto err;
}
msg->l1h = msgb_put(msg, rc);
rc = msg_verify_ipa_structure(msg);
if (rc < 0) {
LOGP(DL1C, LOGL_ERROR,
"OML Router: Invalid IPA message rc(%d)\n", rc);
goto err;
}
rc = msg_verify_oml_structure(msg);
if (rc < 0) {
LOGP(DL1C, LOGL_ERROR,
"OML Router: Invalid OML message rc(%d)\n", rc);
goto err;
}
/* todo dispatch message */
err:
msgb_free(msg);
return -1;
}
static int oml_router_accept_cb(struct osmo_fd *accept_fd, unsigned int what)
{
int fd;
struct osmo_fd *read_fd = (struct osmo_fd *) accept_fd->data;
/* Accept only one connection at a time. De-register it */
if (read_fd->fd > -1) {
LOGP(DL1C, LOGL_NOTICE,
"New OML router connection. Closing old one.\n");
close(read_fd->fd);
osmo_fd_unregister(read_fd);
read_fd->fd = -1;
}
fd = accept(accept_fd->fd, NULL, NULL);
if (fd < 0) {
LOGP(DL1C, LOGL_ERROR, "Failed to accept. errno: %s.\n",
strerror(errno));
return -1;
}
read_fd->fd = fd;
if (osmo_fd_register(read_fd) != 0) {
LOGP(DL1C, LOGL_ERROR, "Registering the read fd failed.\n");
close(fd);
read_fd->fd = -1;
return -1;
}
return 0;
}
int oml_router_init(struct gsm_bts *bts, const char *path,
struct osmo_fd *accept_fd, struct osmo_fd *read_fd)
{
int rc;
memset(accept_fd, 0, sizeof(*accept_fd));
memset(read_fd, 0, sizeof(*read_fd));
accept_fd->cb = oml_router_accept_cb;
accept_fd->data = read_fd;
read_fd->cb = oml_router_read_cb;
read_fd->data = bts;
read_fd->when = BSC_FD_READ;
read_fd->fd = -1;
rc = osmo_sock_unix_init_ofd(accept_fd, SOCK_SEQPACKET, 0,
path,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_NONBLOCK);
return rc;
}

Some files were not shown because too many files have changed in this diff Show More