Compare commits

..

173 Commits
0.0.1 ... 0.1.0

Author SHA1 Message Date
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
71 changed files with 6599 additions and 981 deletions

18
.gitignore vendored
View File

@@ -15,6 +15,24 @@ missing
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/paging/paging_test
tests/testsuite
tests/testsuite.log
# Possible generated file
doc/vty_reference.xml
# Backups, vi, merges
*.sw?
*.orig
*.sav

View File

@@ -1,3 +1,3 @@
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
SUBDIRS = include src SUBDIRS = include src tests

59
README
View File

@@ -1,10 +1,55 @@
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.
However, the BTS-side A-bis is also going to be needed for other projects, thus Specificallt, this includes
the split. * 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.
It doesn't really build yet, as a lot of dependencies have not yet been Right now, only one hardware and Layer1 are supported: The sysmocom
resolved. 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

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])])
@@ -40,4 +41,6 @@ AC_OUTPUT(
src/osmo-bts-bb/Makefile src/osmo-bts-bb/Makefile
include/Makefile include/Makefile
include/osmo-bts/Makefile include/osmo-bts/Makefile
tests/Makefile
tests/paging/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()

View File

@@ -1,4 +1,6 @@
#!/bin/sh #!/bin/sh
while [ -e /etc/passwd ]; do while [ -e /etc/passwd ]; do
$* cat /lib/firmware/sysmobts-v?.out > /dev/dspdl_dm644x_0
echo "0" > /sys/class/leds/activity_led/brightness
nice -n -20 $*
done done

View File

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

View File

@@ -0,0 +1,447 @@
/* OCXO/TCXO based calibration utility */
/*
* (C) 2012 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/core/utils.h>
#include "sysmobts-layer1.h"
enum actions {
ACTION_SCAN,
ACTION_CALIB,
ACTION_BCCH,
};
static const char *modes[] = {
[ACTION_SCAN] = "scan",
[ACTION_CALIB] = "calibrate",
[ACTION_BCCH] = "bcch",
};
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 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\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) {
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_clock_after_sync(HANDLE *layer1)
{
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(layer1, &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(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(&layer1);
rc = mph_close(layer1);
CHECK_RC_MSG(rc, "MPH-Close");
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");
/* now wait for the PhDataInd */
for (;;) {
uint8_t data[23];
size_t size;
rc = wait_for_data(data, &size);
if (rc == 1)
continue;
CHECK_RC_MSG(rc, "No Data Indication");
printf("Data: %s\n", 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)
return bcch_follow();
else
return calib_clock();
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,771 @@
/* Layer1 handling for the DSP/FPGA */
/*
* (C) 2012 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;
printf("FIVE\n");
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;
}
int follow_bcch(HANDLE layer1)
{
int rc;
GsmL1_Prim_t prim;
/* 1.) Activate BCCH... */
memset(&prim, 0, sizeof(prim));
prim.u.mphActivateReq.hLayer1 = layer1;
prim.u.mphActivateReq.u8Tn = 0;
prim.u.mphActivateReq.sapi = GsmL1_Sapi_Bcch;
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 != GsmL1_Sapi_Bcch) {
printf("Got a connect indication for the wrong type: %d\n",
prim.u.phConnectInd.sapi);
return -6;
}
/* 3.) Wait for PhDataInd... */
printf("Waiting for BCCH data.\n");
rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim);
if (rc != 0) {
printf("Didn't get BCCH data.\n");
return rc;
}
return 0;
}
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(HANDLE *layer1, 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)
{
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;
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,31 @@
#ifndef SYSMOBTS_LAYER_H
#define SYSMOBTS_LAYER_H
#include <sysmocom/femtobts/superfemto.h>
/* older header files don't have this */
#ifndef SUPERFEMTO_API
#define SUPERFEMTO_API(x,y,z) ((x << 16) + (y << 8) + z)
#endif
#ifndef SUPERFEMTO_API_VERSION
#define SUPERFEMTO_API_VERSION SUPERFEMTO_API(2,2,0)
#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(HANDLE *layer1, 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 wait_for_data(uint8_t *data, size_t *size);
#endif

View File

@@ -9,8 +9,6 @@
# 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

11
contrib/sysmobts.service Normal file
View File

@@ -0,0 +1,11 @@
[Unit]
Description=sysmocom sysmoBTS
[Service]
Type=simple
ExecStartPre=/bin/sh -c 'echo 0 > /sys/class/leds/activity_led/brightness'
ExecStartPre=/bin/sh -c 'cat /lib/firmware/sysmobts-v?.out > /dev/dspdl_dm644x_0'
ExecStart=/usr/bin/sysmobts -s -c /etc/osmocom/osmo-bts.cfg
ExecStartPost=/bin/sh -c 'echo 0 > /sys/class/leds/activity_led/brightness'
Restart=always
RestartSec=2

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

View File

@@ -1,2 +1,2 @@
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

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

@@ -0,0 +1,14 @@
#ifndef _OSMO_BTS_AMR_H
#define _OSMO_BTS_AMR_H
#include <osmo-bts/gsm_data.h>
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

@@ -18,13 +18,12 @@ 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);
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, struct gsm_time *g_time);
int lchan_init_lapdm(struct gsm_lchan *lchan);
#endif /* _BTS_H */ #endif /* _BTS_H */

View File

@@ -30,10 +30,16 @@ 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_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);
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);
#endif #endif

View File

@@ -7,9 +7,13 @@
#include <osmo-bts/paging.h> #include <osmo-bts/paging.h>
struct pcu_sock_state;
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,6 +40,10 @@ 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;
@@ -44,6 +52,21 @@ struct gsm_bts_role_bts {
struct paging_state *paging_state; struct paging_state *paging_state;
char *bsc_oml_host; char *bsc_oml_host;
char *rtp_bind_host; char *rtp_bind_host;
unsigned int rtp_jitter_buf_ms;
struct {
uint8_t ciphers;
} support;
struct {
uint8_t tc4_ctr;
} si;
};
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)
@@ -57,4 +80,7 @@ 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);
#endif /* _GSM_DATA_H */ #endif /* _GSM_DATA_H */

View File

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

View File

@@ -9,6 +9,7 @@ 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);
/* 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);

View File

@@ -6,9 +6,11 @@
#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);
void paging_reset(struct paging_state *ps); void paging_reset(struct paging_state *ps);
@@ -20,7 +22,17 @@ 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);
/* 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);
#endif #endif

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

@@ -0,0 +1,17 @@
#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);
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(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,152 @@
#ifndef _PCUIF_PROTO_H
#define _PCUIF_PROTO_H
#define PCU_IF_VERSION 0x04
/* 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;
} __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

@@ -8,12 +8,16 @@ 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_rf_rel_ack(struct gsm_lchan *lchan); int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan);
/* 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

@@ -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

@@ -4,4 +4,5 @@ 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

View File

@@ -43,7 +43,6 @@
#include <osmo-bts/rsl.h> #include <osmo-bts/rsl.h>
#include <osmo-bts/oml.h> #include <osmo-bts/oml.h>
extern char *software_version;
extern uint8_t abis_mac[6]; extern uint8_t abis_mac[6];
/* /*
@@ -55,7 +54,7 @@ extern uint8_t abis_mac[6];
/* send message to BSC */ /* send message to BSC */
int abis_tx(struct ipabis_link *link, struct msgb *msg) int abis_tx(struct ipabis_link *link, struct msgb *msg)
{ {
if (link->state != LINK_STATE_CONNECT) { if (!link || link->state != LINK_STATE_CONNECT) {
LOGP(DABIS, LOGL_NOTICE, "Link down, dropping message.\n"); LOGP(DABIS, LOGL_NOTICE, "Link down, dropping message.\n");
msgb_free(msg); msgb_free(msg);
return -EIO; return -EIO;
@@ -174,7 +173,7 @@ static int abis_rx_ipa_id_get(struct ipabis_link *link, uint8_t *data, int len)
break; break;
case IPAC_IDTAG_EQUIPVERS: case IPAC_IDTAG_EQUIPVERS:
case IPAC_IDTAG_SWVERSION: case IPAC_IDTAG_SWVERSION:
strcpy(str, software_version); strcpy(str, PACKAGE_VERSION);
break; break;
case IPAC_IDTAG_UNITNAME: case IPAC_IDTAG_UNITNAME:
sprintf(str, "osmoBTS-%02x-%02x-%02x-%02x-%02x-%02x", sprintf(str, "osmoBTS-%02x-%02x-%02x-%02x-%02x-%02x",
@@ -368,7 +367,7 @@ static int abis_sock_cb(struct osmo_fd *bfd, unsigned int what)
if ((what & BSC_FD_READ)) { if ((what & BSC_FD_READ)) {
if (!link->rx_msg) { if (!link->rx_msg) {
link->rx_msg = msgb_alloc(ABIS_ALLOC_SIZE, "Abis/IP"); link->rx_msg = abis_msgb_alloc(128);
if (!link->rx_msg) if (!link->rx_msg)
return -ENOMEM; return -ENOMEM;
} }

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

@@ -57,6 +57,9 @@ int bts_init(struct gsm_bts *bts)
struct gsm_bts_trx *trx; struct gsm_bts_trx *trx;
int rc; int rc;
/* 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);
@@ -65,9 +68,18 @@ int bts_init(struct gsm_bts *bts)
/* FIXME: make those parameters configurable */ /* FIXME: make those parameters configurable */
btsb->paging_state = paging_init(btsb, 200, 0); btsb->paging_state = paging_init(btsb, 200, 0);
btsb->load.ccch.load_ind_period = 60*100;
load_timer_start(bts);
btsb->rtp_jitter_buf_ms = 100;
btsb->max_ta = 63;
/* set BTS to dependency */ /* set BTS to dependency */
oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_DEPENDENCY); oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_DEPENDENCY);
oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_DEPENDENCY);
oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_DEPENDENCY);
oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_DEPENDENCY);
oml_mo_state_chg(&bts->gprs.nsvc[1].mo, -1, NM_AVSTATE_DEPENDENCY);
/* 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) {
@@ -86,11 +98,11 @@ int bts_init(struct gsm_bts *bts)
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++;
return rc; return rc;
@@ -121,227 +133,6 @@ void bts_shutdown(struct gsm_bts *bts, const char *reason)
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)
{ {
@@ -353,6 +144,12 @@ int bts_link_estab(struct gsm_bts *bts)
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);
@@ -387,36 +184,6 @@ int trx_link_estab(struct gsm_bts_trx *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;
@@ -431,24 +198,17 @@ 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 gsm_bts_role_bts *btsb = bts_role_bts(bts);; struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
/* FIXME: implement max queue length */ /* FIXME: implement max queue length */
llist_add_tail(&msg->list, &btsb->agch_queue); msgb_enqueue(&btsb->agch_queue, msg);
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;
if (llist_empty(&btsb->agch_queue)) return msgb_dequeue(&btsb->agch_queue);
return NULL;
msg = llist_entry(btsb->agch_queue.next, struct msgb, list);
llist_del(&msg->list);
return msg;
} }

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,56 @@ 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);
} }
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,12 @@ 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,
},
#if 0 #if 0
[DNS] = { [DNS] = {
.name = "DNS", .name = "DNS",

View File

@@ -216,6 +216,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:

View File

@@ -37,6 +37,8 @@
#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 = {
@@ -89,6 +91,7 @@ static struct tlv_definition abis_nm_att_tlvdef_ipa = {
/* ip.access nanoBTS specific commands */ /* ip.access nanoBTS specific commands */
static const char ipaccess_magic[] = "com.ipaccess"; static const char ipaccess_magic[] = "com.ipaccess";
static int oml_ipa_set_attr(struct gsm_bts *bts, struct msgb *msg);
/* /*
* support * support
@@ -245,6 +248,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 +277,19 @@ 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_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 +304,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;
@@ -378,8 +399,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(*value);
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);
@@ -430,10 +456,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))
@@ -514,8 +538,9 @@ 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
@@ -571,6 +596,10 @@ static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts)
lchan->type = GSM_LCHAN_SDCCH; 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 +636,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 */ /* FIXME: Send NACK */
return rc; return 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);
@@ -702,7 +735,7 @@ static int oml_rx_chg_adm_state(struct gsm_bts *bts, struct msgb *msg)
if (mo->nm_state.administrative == adm_state) { if (mo->nm_state.administrative == adm_state) {
DEBUGP(DOML, "... automatic ACK, ADM state already was %s\n", DEBUGP(DOML, "... automatic ACK, ADM state already was %s\n",
get_value_string(abis_nm_adm_state_names, adm_state)); get_value_string(abis_nm_adm_state_names, adm_state));
return oml_fom_ack_nack(msg, 0); return oml_mo_statechg_ack(mo);
} }
/* Step 3: Ask BTS driver to apply the state chg */ /* Step 3: Ask BTS driver to apply the state chg */
@@ -749,6 +782,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,6 +798,170 @@ static int down_fom(struct gsm_bts *bts, struct msgb *msg)
* manufacturer related messages * manufacturer related messages
*/ */
#define TLVP_PRES_LEN(tp, tag, min_len) \
(TLVP_PRESENT(tp, tag) && TLVP_LEN(tp, tag) >= min_len)
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(*(uint16_t *) TLVP_VAL(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;
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 =
ntohs(*(uint16_t *)TLVP_VAL(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);
rlcc->parameter[T_DL_TBF_EXT] = ntohs(*(uint16_t *)cur) * 10;
cur += 2;
rlcc->parameter[T_UL_TBF_EXT] = ntohs(*(uint16_t *)cur) * 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(*(uint16_t *)TLVP_VAL(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);
nsvc->remote_port = ntohs(*(uint16_t *)cur);
cur += 2;
nsvc->remote_ip = ntohl(*(uint32_t *)cur);
cur += 4;
nsvc->local_port = ntohs(*(uint16_t *)cur);
}
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)
@@ -847,6 +1047,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,34 @@
#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
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;
@@ -128,6 +144,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 +167,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 +182,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 +192,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,6 +204,42 @@ 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 fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv,
@@ -266,7 +330,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;
@@ -295,10 +359,22 @@ 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)
{ {
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;
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");
@@ -306,15 +382,24 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
NULL, 0); NULL, 0);
} 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 +407,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 +451,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(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(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 +482,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",
@@ -418,16 +522,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;
@@ -460,3 +566,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;
}

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

@@ -0,0 +1,921 @@
/* 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)
{
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;
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(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;
}
memcpy(msgb_put(msg, data_req->len), 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;
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)
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 has connection with external "
"call control application\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;
}

View File

@@ -38,6 +38,7 @@
#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>
#include <osmo-bts/amr.h>
#include <osmo-bts/signal.h> #include <osmo-bts/signal.h>
#include <osmo-bts/bts_model.h> #include <osmo-bts/bts_model.h>
#include <osmo-bts/measurement.h> #include <osmo-bts/measurement.h>
@@ -121,89 +122,18 @@ static void lchan_tchmode_from_cmode(struct gsm_lchan *lchan,
* support * support
*/ */
static void log_mr_conf(int ss, int logl, const char *pfx, /**
struct amr_multirate_conf *amr_mrc) * Handle GSM 08.58 7 Error Handling for the given input. This method will
* send either a CHANNEL ACTIVATION NACK, MODE MODIFY NACK or ERROR REPORT
* depending on the input of the method.
*
* TODO: actually make the decision
*/
static int report_error(struct gsm_bts_trx *trx)
{ {
int i; return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT);
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 */
static int 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;
}
#warning merge lchan_lookup with OpenBSC #warning merge lchan_lookup with OpenBSC
/* determine logical channel based on TRX and channel number IE */ /* determine logical channel based on TRX and channel number IE */
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)
@@ -409,16 +339,38 @@ int rsl_tx_ccch_load_ind_pch(struct gsm_bts *bts, uint16_t paging_avail)
{ {
struct msgb *msg; struct msgb *msg;
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr)); msg = rsl_msgb_alloc(sizeof(struct abis_rsl_cchan_hdr));
if (!msg) if (!msg)
return -ENOMEM; return -ENOMEM;
rsl_trx_push_hdr(msg, RSL_MT_CCCH_LOAD_IND); rsl_cch_push_hdr(msg, RSL_MT_CCCH_LOAD_IND, RSL_CHAN_PCH_AGCH);
msgb_tv16_put(msg, RSL_IE_PAGING_LOAD, paging_avail); msgb_tv16_put(msg, RSL_IE_PAGING_LOAD, paging_avail);
msg->trx = bts->c0; msg->trx = bts->c0;
return abis_rsl_sendmsg(msg); return abis_rsl_sendmsg(msg);
} }
/* 8.5.2 CCCH Load Indication (RACH) */
int rsl_tx_ccch_load_ind_rach(struct gsm_bts *bts, uint16_t total,
uint16_t busy, uint16_t access)
{
struct msgb *msg;
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_cchan_hdr));
if (!msg)
return -ENOMEM;
rsl_cch_push_hdr(msg, RSL_MT_CCCH_LOAD_IND, RSL_CHAN_RACH);
/* tag and length */
msgb_tv_put(msg, RSL_IE_RACH_LOAD, 6);
/* content of the IE */
msgb_put_u16(msg, total);
msgb_put_u16(msg, busy);
msgb_put_u16(msg, access);
msg->trx = bts->c0;
return abis_rsl_sendmsg(msg);
}
/* 8.5.5 PAGING COMMAND */ /* 8.5.5 PAGING COMMAND */
static int rsl_rx_paging_cmd(struct gsm_bts_trx *trx, struct msgb *msg) static int rsl_rx_paging_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
{ {
@@ -446,30 +398,11 @@ static int rsl_rx_paging_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
/* FIXME: notfiy the BSC somehow ?*/ /* FIXME: notfiy the BSC somehow ?*/
} }
pcu_tx_pag_req(identity_lv, chan_needed);
return 0; return 0;
} }
int rsl_tx_ccch_load_ind_rach(struct gsm_bts *bts, uint16_t rach_slots,
uint16_t rach_busy, uint16_t rach_access)
{
struct msgb *msg;
uint16_t payload[3];
payload[0] = htons(rach_slots);
payload[1] = htons(rach_busy);
payload[2] = htons(rach_access);
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr));
if (!msg)
return -ENOMEM;
msgb_tlv_put(msg, RSL_IE_RACH_LOAD, 6, (uint8_t *)payload);
rsl_trx_push_hdr(msg, RSL_MT_CCCH_LOAD_IND);
msg->trx = bts->c0;
return abis_rsl_sendmsg(msg);
}
/* 8.6.2 SACCH FILLING */ /* 8.6.2 SACCH FILLING */
static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg) static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg)
{ {
@@ -499,8 +432,8 @@ static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg)
if (len > sizeof(sysinfo_buf_t)-2) if (len > sizeof(sysinfo_buf_t)-2)
len = sizeof(sysinfo_buf_t)-2; len = sizeof(sysinfo_buf_t)-2;
bts->si_valid |= (1 << osmo_si); bts->si_valid |= (1 << osmo_si);
bts->si_buf[osmo_si][0] = 0x00; bts->si_buf[osmo_si][0] = 0x03; /* C/R + EA */
bts->si_buf[osmo_si][1] = 0x03; bts->si_buf[osmo_si][1] = 0x03; /* UI frame */
memcpy(bts->si_buf[osmo_si]+2, memcpy(bts->si_buf[osmo_si]+2,
TLVP_VAL(&tp, RSL_IE_L3_INFO), len); TLVP_VAL(&tp, RSL_IE_L3_INFO), len);
LOGP(DRSL, LOGL_INFO, " Rx RSL SACCH FILLING (SI%s)\n", LOGP(DRSL, LOGL_INFO, " Rx RSL SACCH FILLING (SI%s)\n",
@@ -544,14 +477,18 @@ static int rsl_rx_imm_ass(struct gsm_bts_trx *trx, struct msgb *msg)
* dedicated channel related messages * dedicated channel related messages
*/ */
/* 8.4.19 sebdubg RF CHANnel RELease ACKnowledge */ /* 8.4.19 sending RF CHANnel RELease ACKnowledge */
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan) int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
{ {
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
LOGP(DRSL, LOGL_NOTICE, "%s Tx RF CHAN REL ACK\n", gsm_lchan_name(lchan)); LOGP(DRSL, LOGL_NOTICE, "%s Tx RF CHAN REL ACK\n", gsm_lchan_name(lchan));
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
rsl_dch_push_hdr(msg, RSL_MT_RF_CHAN_REL_ACK, chan_nr); rsl_dch_push_hdr(msg, RSL_MT_RF_CHAN_REL_ACK, chan_nr);
msg->trx = lchan->ts->trx; msg->trx = lchan->ts->trx;
@@ -561,12 +498,16 @@ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
/* 8.4.2 sending CHANnel ACTIVation ACKnowledge */ /* 8.4.2 sending CHANnel ACTIVation ACKnowledge */
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime) int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime)
{ {
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
uint8_t ie[2]; uint8_t ie[2];
LOGP(DRSL, LOGL_NOTICE, "%s Tx CHAN ACT ACK\n", gsm_lchan_name(lchan)); LOGP(DRSL, LOGL_NOTICE, "%s Tx CHAN ACT ACK\n", gsm_lchan_name(lchan));
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
gsm48_gen_starting_time(ie, gtime); gsm48_gen_starting_time(ie, gtime);
msgb_tv_fixed_put(msg, RSL_IE_FRAME_NUMBER, 2, ie); msgb_tv_fixed_put(msg, RSL_IE_FRAME_NUMBER, 2, ie);
rsl_dch_push_hdr(msg, RSL_MT_CHAN_ACTIV_ACK, chan_nr); rsl_dch_push_hdr(msg, RSL_MT_CHAN_ACTIV_ACK, chan_nr);
@@ -579,20 +520,21 @@ int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime)
} }
/* 8.4.3 sending CHANnel ACTIVation Negative ACK */ /* 8.4.3 sending CHANnel ACTIVation Negative ACK */
static int rsl_tx_chan_nack(struct gsm_bts_trx *trx, struct msgb *msg, uint8_t cause) int rsl_tx_chan_act_nack(struct gsm_lchan *lchan, uint8_t cause)
{ {
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); struct msgb *msg;
uint8_t chan_nr = dch->chan_nr; uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
LOGP(DRSL, LOGL_NOTICE, "Sending Channel Activated NACK: cause = 0x%02x\n", cause); LOGP(DRSL, LOGL_NOTICE, "Sending Channel Activated NACK: cause = 0x%02x\n", cause);
msg->len = 0; msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
msg->data = msg->tail = msg->l3h; if (!msg)
return -ENOMEM;
/* 9.3.26 Cause */ /* 9.3.26 Cause */
msgb_tlv_put(msg, RSL_IE_CAUSE, 1, &cause); msgb_tlv_put(msg, RSL_IE_CAUSE, 1, &cause);
rsl_dch_push_hdr(msg, RSL_MT_CHAN_ACTIV_NACK, chan_nr); rsl_dch_push_hdr(msg, RSL_MT_CHAN_ACTIV_NACK, chan_nr);
msg->trx = trx; msg->trx = lchan->ts->trx;
return abis_rsl_sendmsg(msg); return abis_rsl_sendmsg(msg);
} }
@@ -650,6 +592,12 @@ static void copy_sacch_si_to_lchan(struct gsm_lchan *lchan)
static int encr_info2lchan(struct gsm_lchan *lchan, static int encr_info2lchan(struct gsm_lchan *lchan,
const uint8_t *val, uint8_t len) const uint8_t *val, uint8_t len)
{ {
struct gsm_bts_role_bts *btsb = bts_role_bts(lchan->ts->trx->bts);
/* check if the encryption algorithm sent by BSC is supported! */
if (!((1 << *val) & btsb->support.ciphers))
return -ENOTSUP;
/* length can be '1' in case of no ciphering */ /* length can be '1' in case of no ciphering */
if (len < 1) if (len < 1)
return -EINVAL; return -EINVAL;
@@ -672,21 +620,26 @@ static int rsl_rx_chan_activ(struct msgb *msg)
struct tlv_parsed tp; struct tlv_parsed tp;
uint8_t type; uint8_t type;
if (lchan->state != LCHAN_S_NONE) {
LOGP(DRSL, LOGL_ERROR,
"%s: error lchan is not available state: %s.\n",
gsm_lchan_name(lchan), gsm_lchans_name(lchan->state));
return rsl_tx_chan_act_nack(lchan, RSL_ERR_EQUIPMENT_FAIL);
}
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
/* 9.3.3 Activation Type */ /* 9.3.3 Activation Type */
if (!TLVP_PRESENT(&tp, RSL_IE_ACT_TYPE)) { if (!TLVP_PRESENT(&tp, RSL_IE_ACT_TYPE)) {
LOGP(DRSL, LOGL_NOTICE, "missing Activation Type\n"); LOGP(DRSL, LOGL_NOTICE, "missing Activation Type\n");
msgb_free(msg); return rsl_tx_chan_act_nack(lchan, RSL_ERR_MAND_IE_ERROR);
return rsl_tx_chan_nack(msg->trx, msg, RSL_ERR_MAND_IE_ERROR);
} }
type = *TLVP_VAL(&tp, RSL_IE_ACT_TYPE); type = *TLVP_VAL(&tp, RSL_IE_ACT_TYPE);
/* 9.3.6 Channel Mode */ /* 9.3.6 Channel Mode */
if (!TLVP_PRESENT(&tp, RSL_IE_CHAN_MODE)) { if (!TLVP_PRESENT(&tp, RSL_IE_CHAN_MODE)) {
LOGP(DRSL, LOGL_NOTICE, "missing Channel Mode\n"); LOGP(DRSL, LOGL_NOTICE, "missing Channel Mode\n");
msgb_free(msg); return rsl_tx_chan_act_nack(lchan, RSL_ERR_MAND_IE_ERROR);
return rsl_tx_chan_nack(msg->trx, msg, RSL_ERR_MAND_IE_ERROR);
} }
cm = (struct rsl_ie_chan_mode *) TLVP_VAL(&tp, RSL_IE_CHAN_MODE); cm = (struct rsl_ie_chan_mode *) TLVP_VAL(&tp, RSL_IE_CHAN_MODE);
lchan_tchmode_from_cmode(lchan, cm); lchan_tchmode_from_cmode(lchan, cm);
@@ -698,7 +651,8 @@ static int rsl_rx_chan_activ(struct msgb *msg)
if (encr_info2lchan(lchan, val, len) < 0) if (encr_info2lchan(lchan, val, len) < 0)
return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT); return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT);
} } else
memset(&lchan->encr, 0, sizeof(lchan->encr));
/* 9.3.9 Handover Reference */ /* 9.3.9 Handover Reference */
@@ -765,10 +719,10 @@ static int rsl_rx_chan_activ(struct msgb *msg)
} }
memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG), memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG), amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan), amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
&lchan->tch.amr_mr); &lchan->tch.amr_mr);
} }
/* 9.3.53 MultiRate Control */ /* 9.3.53 MultiRate Control */
/* 9.3.54 Supported Codec Types */ /* 9.3.54 Supported Codec Types */
@@ -806,11 +760,15 @@ static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan)
static int tx_ciph_mod_compl_hack(struct gsm_lchan *lchan, uint8_t link_id, static int tx_ciph_mod_compl_hack(struct gsm_lchan *lchan, uint8_t link_id,
const char *imeisv) const char *imeisv)
{ {
struct msgb *fake_msg = rsl_msgb_alloc(128); struct msgb *fake_msg;
struct gsm48_hdr *g48h; struct gsm48_hdr *g48h;
uint8_t mid_buf[11]; uint8_t mid_buf[11];
int rc; int rc;
fake_msg = rsl_msgb_alloc(128);
if (!fake_msg)
return -ENOMEM;
/* generate 04.08 RR message */ /* generate 04.08 RR message */
g48h = (struct gsm48_hdr *) msgb_put(fake_msg, sizeof(*g48h)); g48h = (struct gsm48_hdr *) msgb_put(fake_msg, sizeof(*g48h));
g48h->proto_discr = GSM48_PDISC_RR; g48h->proto_discr = GSM48_PDISC_RR;
@@ -887,8 +845,6 @@ static int rsl_rx_encr_cmd(struct msgb *msg)
return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT); return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT);
} }
/* FIXME: check if the encryption algorithm sent by BSC is supported! */
/* 9.3.2 Link Identifier */ /* 9.3.2 Link Identifier */
link_id = *TLVP_VAL(&tp, RSL_IE_LINK_IDENT); link_id = *TLVP_VAL(&tp, RSL_IE_LINK_IDENT);
@@ -935,12 +891,16 @@ static int rsl_rx_encr_cmd(struct msgb *msg)
/* 8.4.11 MODE MODIFY NEGATIVE ACKNOWLEDGE */ /* 8.4.11 MODE MODIFY NEGATIVE ACKNOWLEDGE */
static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause) static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause)
{ {
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
LOGP(DRSL, LOGL_NOTICE, "%s Tx MODE MODIFY NACK (cause = 0x%02x)\n", LOGP(DRSL, LOGL_NOTICE, "%s Tx MODE MODIFY NACK (cause = 0x%02x)\n",
gsm_lchan_name(lchan), cause); gsm_lchan_name(lchan), cause);
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
msg->len = 0; msg->len = 0;
msg->data = msg->tail = msg->l3h; msg->data = msg->tail = msg->l3h;
@@ -955,11 +915,15 @@ static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause)
/* 8.4.10 MODE MODIFY ACK */ /* 8.4.10 MODE MODIFY ACK */
static int rsl_tx_mode_modif_ack(struct gsm_lchan *lchan) static int rsl_tx_mode_modif_ack(struct gsm_lchan *lchan)
{ {
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
LOGP(DRSL, LOGL_INFO, "%s Tx MODE MODIF ACK\n", gsm_lchan_name(lchan)); LOGP(DRSL, LOGL_INFO, "%s Tx MODE MODIF ACK\n", gsm_lchan_name(lchan));
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
rsl_dch_push_hdr(msg, RSL_MT_MODE_MODIFY_ACK, chan_nr); rsl_dch_push_hdr(msg, RSL_MT_MODE_MODIFY_ACK, chan_nr);
msg->trx = lchan->ts->trx; msg->trx = lchan->ts->trx;
@@ -1004,10 +968,10 @@ static int rsl_rx_mode_modif(struct msgb *msg)
} }
memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG), memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG), amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan), amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
&lchan->tch.amr_mr); &lchan->tch.amr_mr);
} }
/* 9.3.53 MultiRate Control */ /* 9.3.53 MultiRate Control */
/* 9.3.54 Supported Codec Types */ /* 9.3.54 Supported Codec Types */
@@ -1082,12 +1046,12 @@ int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause)
LOGP(DRSL, LOGL_NOTICE, "%s Sending RTP delete indication: cause=%d\n", LOGP(DRSL, LOGL_NOTICE, "%s Sending RTP delete indication: cause=%d\n",
gsm_lchan_name(lchan), cause); gsm_lchan_name(lchan), cause);
nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr)); nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!nmsg) if (!nmsg)
return -ENOMEM; return -ENOMEM;
msgb_tlv_put(nmsg, RSL_IE_CAUSE, 1, &cause); msgb_tlv_put(nmsg, RSL_IE_CAUSE, 1, &cause);
rsl_trx_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND); rsl_ipa_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND, gsm_lchan2chan_nr(lchan));
nmsg->trx = lchan->ts->trx; nmsg->trx = lchan->ts->trx;
@@ -1098,7 +1062,7 @@ int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause)
static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2, static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
uint8_t orig_msgt) uint8_t orig_msgt)
{ {
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
uint32_t *att_ip; uint32_t *att_ip;
const char *name; const char *name;
@@ -1117,6 +1081,11 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
LOGPC(DRSL, LOGL_INFO, "remote %s:%u)\n", LOGPC(DRSL, LOGL_INFO, "remote %s:%u)\n",
inet_ntoa(ia), lchan->abis_ip.connect_port); inet_ntoa(ia), lchan->abis_ip.connect_port);
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
/* Connection ID */ /* Connection ID */
msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, htons(lchan->abis_ip.conn_id)); msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, htons(lchan->abis_ip.conn_id));
@@ -1144,12 +1113,16 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
static int rsl_tx_ipac_dlcx_ack(struct gsm_lchan *lchan, int inc_conn_id) static int rsl_tx_ipac_dlcx_ack(struct gsm_lchan *lchan, int inc_conn_id)
{ {
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
LOGP(DRSL, LOGL_INFO, "%s RSL Tx IPAC_DLCX_ACK\n", LOGP(DRSL, LOGL_INFO, "%s RSL Tx IPAC_DLCX_ACK\n",
gsm_lchan_name(lchan)); gsm_lchan_name(lchan));
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
if (inc_conn_id) if (inc_conn_id)
msgb_tv_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id); msgb_tv_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
@@ -1162,12 +1135,16 @@ static int rsl_tx_ipac_dlcx_ack(struct gsm_lchan *lchan, int inc_conn_id)
static int rsl_tx_ipac_dlcx_nack(struct gsm_lchan *lchan, int inc_conn_id, static int rsl_tx_ipac_dlcx_nack(struct gsm_lchan *lchan, int inc_conn_id,
uint8_t cause) uint8_t cause)
{ {
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
LOGP(DRSL, LOGL_INFO, "%s RSL Tx IPAC_DLCX_NACK\n", LOGP(DRSL, LOGL_INFO, "%s RSL Tx IPAC_DLCX_NACK\n",
gsm_lchan_name(lchan)); gsm_lchan_name(lchan));
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
if (inc_conn_id) if (inc_conn_id)
msgb_tv_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id); msgb_tv_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
@@ -1185,13 +1162,17 @@ static int rsl_tx_ipac_dlcx_nack(struct gsm_lchan *lchan, int inc_conn_id,
static int tx_ipac_XXcx_nack(struct gsm_lchan *lchan, uint8_t cause, static int tx_ipac_XXcx_nack(struct gsm_lchan *lchan, uint8_t cause,
int inc_ipport, uint8_t orig_msgtype) int inc_ipport, uint8_t orig_msgtype)
{ {
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
/* FIXME: allocate new msgb and copy old over */ /* FIXME: allocate new msgb and copy old over */
LOGP(DRSL, LOGL_NOTICE, "%s RSL Tx IPAC_BIND_NACK\n", LOGP(DRSL, LOGL_NOTICE, "%s RSL Tx IPAC_BIND_NACK\n",
gsm_lchan_name(lchan)); gsm_lchan_name(lchan));
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
if (inc_ipport) { if (inc_ipport) {
uint32_t *att_ip; uint32_t *att_ip;
/* remote IP */ /* remote IP */
@@ -1273,6 +1254,9 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL, return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
inc_ip_port, dch->c.msg_type); inc_ip_port, dch->c.msg_type);
} }
osmo_rtp_socket_set_param(lchan->abis_ip.rtp_socket,
OSMO_RTP_P_JITBUF,
btsb->rtp_jitter_buf_ms);
lchan->abis_ip.rtp_socket->priv = lchan; lchan->abis_ip.rtp_socket->priv = lchan;
lchan->abis_ip.rtp_socket->rx_cb = &bts_model_rtp_rx_cb; lchan->abis_ip.rtp_socket->rx_cb = &bts_model_rtp_rx_cb;
@@ -1359,7 +1343,6 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
static int rsl_rx_ipac_dlcx(struct msgb *msg) static int rsl_rx_ipac_dlcx(struct msgb *msg)
{ {
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
struct tlv_parsed tp; struct tlv_parsed tp;
struct gsm_lchan *lchan = msg->lchan; struct gsm_lchan *lchan = msg->lchan;
int rc, inc_conn_id = 0; int rc, inc_conn_id = 0;
@@ -1398,7 +1381,8 @@ static int rsl_rx_rll(struct gsm_bts_trx *trx, struct msgb *msg)
if (!lchan) { if (!lchan) {
LOGP(DRLL, LOGL_NOTICE, "Rx RLL %s for unknown lchan\n", LOGP(DRLL, LOGL_NOTICE, "Rx RLL %s for unknown lchan\n",
rsl_msg_name(rh->c.msg_type)); rsl_msg_name(rh->c.msg_type));
return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR); msgb_free(msg);
return report_error(trx);
} }
DEBUGP(DRLL, "%s Rx RLL %s Abis -> LAPDm\n", gsm_lchan_name(lchan), DEBUGP(DRLL, "%s Rx RLL %s Abis -> LAPDm\n", gsm_lchan_name(lchan),
@@ -1454,11 +1438,15 @@ static int rslms_is_meas_rep(struct msgb *msg)
/* 8.4.8 MEASUREMENT RESult */ /* 8.4.8 MEASUREMENT RESult */
static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len) static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len)
{ {
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
LOGP(DRSL, LOGL_NOTICE, "%s Tx MEAS RES\n", gsm_lchan_name(lchan)); LOGP(DRSL, LOGL_NOTICE, "%s Tx MEAS RES\n", gsm_lchan_name(lchan));
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr++); msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr++);
if (lchan->meas.flags & LC_UL_M_F_RES_VALID) { if (lchan->meas.flags & LC_UL_M_F_RES_VALID) {
uint8_t meas_res[16]; uint8_t meas_res[16];
@@ -1525,7 +1513,8 @@ static int rsl_rx_cchan(struct gsm_bts_trx *trx, struct msgb *msg)
if (!msg->lchan) { if (!msg->lchan) {
LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n", LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n",
rsl_msg_name(cch->c.msg_type)); rsl_msg_name(cch->c.msg_type));
//return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR); msgb_free(msg);
return report_error(trx);
} }
LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan), LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan),
@@ -1576,7 +1565,8 @@ static int rsl_rx_dchan(struct gsm_bts_trx *trx, struct msgb *msg)
if (!msg->lchan) { if (!msg->lchan) {
LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n", LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n",
rsl_msg_name(dch->c.msg_type)); rsl_msg_name(dch->c.msg_type));
//return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR); msgb_free(msg);
return report_error(trx);
} }
LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan), LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan),
@@ -1666,7 +1656,8 @@ static int rsl_rx_ipaccess(struct gsm_bts_trx *trx, struct msgb *msg)
if (!msg->lchan) { if (!msg->lchan) {
LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n", LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n",
rsl_msg_name(dch->c.msg_type)); rsl_msg_name(dch->c.msg_type));
//return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR); msgb_free(msg);
return report_error(trx);
} }
LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan), LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan),

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);

View File

@@ -29,6 +29,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>
@@ -45,6 +47,13 @@
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 +63,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 +75,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 +102,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)
@@ -126,9 +144,37 @@ static struct cmd_node bts_node = {
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)
@@ -138,9 +184,18 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
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 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);
bts_model_config_write_bts(vty, bts);
llist_for_each_entry(trx, &bts->trx_list, list) {
vty_out(vty, " trx %u%s", trx->nr, 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 +206,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 +240,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 +257,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,10 +297,12 @@ DEFUN(cfg_bts_oml_ip,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
#define RTP_STR "RTP parameters\n"
DEFUN(cfg_bts_rtp_bind_ip, DEFUN(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")
{ {
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);
@@ -242,6 +314,20 @@ DEFUN(cfg_bts_rtp_bind_ip,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
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_role_bts *btsb = bts_role_bts(bts);
btsb->rtp_jitter_buf_ms = atoi(argv[0]);
return CMD_SUCCESS;
}
/* ====================================================================== /* ======================================================================
* SHOW * SHOW
* ======================================================================*/ * ======================================================================*/
@@ -316,6 +402,70 @@ 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;
}
int bts_vty_init(const struct log_info *cat) int bts_vty_init(const struct log_info *cat)
{ {
@@ -329,7 +479,17 @@ 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);
/* 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(ENABLE_NODE, &bts_t_t_l_jitter_buf_cmd);
return 0;
} }

View File

@@ -2,9 +2,11 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) -lortp LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_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
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
COMMON_SOURCES = main.c femtobts.c l1_if.c oml.c sysmobts_vty.c tch.c hw_misc.c calib_file.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 $(LDADD)
@@ -14,3 +16,5 @@ sysmobts_remote_LDADD = $(top_builddir)/src/common/libbts.a $(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 $(LDADD)
sysmobts_mgr_SOURCES = misc/sysmobts_mgr.c misc/sysmobts_misc.c misc/sysmobts_par.c

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,274 @@
/* 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 <osmocom/core/utils.h>
#include <osmo-bts/logging.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1const.h>
#include "l1_if.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
};
static float read_float(FILE *in)
{
float f;
fscanf(in, "%f\n", &f);
return f;
}
static int read_int(FILE *in)
{
int i;
fscanf(in, "%d\n", &i);
return i;
}
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)
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;
}
/* iteratively download the calibration data into the L1 */
struct calib_send_state {
struct femtol1_hdl *fl1h;
const char *path;
int last_file_idx;
};
static int calib_send_compl_cb(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, void *state)
{
struct msgb *msg;
int rc;
msg = sysp_msgb_alloc();
rc = calib_file_read(fl1h->calib_path, desc, msgb_sysprim(msg));
if (rc < 0) {
msgb_free(msg);
return rc;
}
return l1if_req_compl(fl1h, msg, 1, calib_send_compl_cb, state);
}
/* completion callback after every SetCalibTbl is confirmed */
static int calib_send_compl_cb(struct msgb *l1_msg, void *data)
{
struct calib_send_state *st = data;
LOGP(DL1C, LOGL_DEBUG, "L1 calibration table %s loaded\n",
calib_files[st->last_file_idx].fname);
st->last_file_idx++;
if (st->last_file_idx < ARRAY_SIZE(calib_files))
return calib_file_send(st->fl1h,
&calib_files[st->last_file_idx], st);
LOGP(DL1C, LOGL_INFO, "L1 calibration table loading complete!\n");
return 0;
}
int calib_load(struct femtol1_hdl *fl1h)
{
static struct calib_send_state st;
memset(&st, 0, sizeof(st));
st.fl1h = fl1h;
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
return -1;
#else
return calib_file_send(fl1h, &calib_files[0], &st);
#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

@@ -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,48 +91,64 @@ 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,
}; };
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
{ 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
}; };
const struct value_string femtobts_l1sapi_names[GsmL1_Sapi_NUM+1] = { const struct value_string femtobts_l1sapi_names[GsmL1_Sapi_NUM+1] = {
@@ -220,10 +236,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 +284,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,78 @@
#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 L1_HAS_RTP_MODE
/* This is temporarily disabled, as AMR has some bugs in RTP mode */
//#define USE_L1_RTP_MODE /* Tell L1 to use RTP mode */
#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_REQ, L1P_T_REQ,
L1P_T_CONF, L1P_T_CONF,
L1P_T_IND, L1P_T_IND,
}; };
#if 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,8 +156,8 @@ 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);
@@ -165,9 +166,11 @@ int main(int argc, char **argv)
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 +179,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);

View File

@@ -32,6 +32,8 @@
#include <osmocom/core/select.h> #include <osmocom/core/select.h>
#include <osmocom/core/timer.h> #include <osmocom/core/timer.h>
#include <osmocom/core/write_queue.h> #include <osmocom/core/write_queue.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/gsm/gsm_utils.h> #include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/lapdm.h> #include <osmocom/gsm/lapdm.h>
@@ -43,8 +45,9 @@
#include <osmo-bts/gsm_data.h> #include <osmo-bts/gsm_data.h>
#include <osmo-bts/paging.h> #include <osmo-bts/paging.h>
#include <osmo-bts/measurement.h> #include <osmo-bts/measurement.h>
#include <osmo-bts/pcu_if.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>
@@ -52,11 +55,104 @@
#include "femtobts.h" #include "femtobts.h"
#include "l1_if.h" #include "l1_if.h"
#include "l1_transp.h" #include "l1_transp.h"
#include "hw_misc.h"
extern int pcu_direct;
/* FIXME: make threshold configurable */ /* FIXME: make threshold configurable */
#define MIN_QUAL_RACH 5.0f /* at least 5 dB C/I */ #define MIN_QUAL_RACH 5.0f /* at least 5 dB C/I */
#define MIN_QUAL_NORM -0.5f /* at least -1 dB C/I */ #define MIN_QUAL_NORM -0.5f /* at least -1 dB C/I */
/* mapping from femtbts L1 SAPI to GSMTAP channel type */
static const uint8_t l1sapi2gsmtap_cht[GsmL1_Sapi_NUM] = {
[GsmL1_Sapi_Idle] = 255,
[GsmL1_Sapi_Fcch] = 255,
[GsmL1_Sapi_Sch] = 255,
[GsmL1_Sapi_Sacch] = GSMTAP_CHANNEL_SDCCH | GSMTAP_CHANNEL_ACCH,
[GsmL1_Sapi_Sdcch] = GSMTAP_CHANNEL_SDCCH,
[GsmL1_Sapi_Bcch] = GSMTAP_CHANNEL_BCCH,
[GsmL1_Sapi_Pch] = GSMTAP_CHANNEL_PCH,
[GsmL1_Sapi_Agch] = GSMTAP_CHANNEL_AGCH,
[GsmL1_Sapi_Cbch] = GSMTAP_CHANNEL_CBCH51,
[GsmL1_Sapi_Rach] = GSMTAP_CHANNEL_RACH,
[GsmL1_Sapi_TchF] = 255,
[GsmL1_Sapi_FacchF] = GSMTAP_CHANNEL_TCH_F,
[GsmL1_Sapi_TchH] = 255,
[GsmL1_Sapi_FacchH] = GSMTAP_CHANNEL_TCH_H,
[GsmL1_Sapi_Nch] = GSMTAP_CHANNEL_CCCH,
[GsmL1_Sapi_Pdtch] = GSMTAP_CHANNEL_PACCH,
[GsmL1_Sapi_Pacch] = GSMTAP_CHANNEL_PACCH,
[GsmL1_Sapi_Pbcch] = 255,
[GsmL1_Sapi_Pagch] = 255,
[GsmL1_Sapi_Ppch] = 255,
[GsmL1_Sapi_Pnch] = 255,
[GsmL1_Sapi_Ptcch] = GSMTAP_CHANNEL_PTCCH,
[GsmL1_Sapi_Prach] = 255,
};
static void tx_to_gsmtap(struct femtol1_hdl *fl1h, struct msgb *msg)
{
struct gsm_bts_trx *trx = fl1h->priv;
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
if (fl1h->gsmtap) {
uint8_t ss, chan_type;
if (data_req->subCh == 0x1f)
ss = 0;
else
ss = data_req->subCh;
if (!(fl1h->gsmtap_sapi_mask & (1 << data_req->sapi)))
return;
chan_type = l1sapi2gsmtap_cht[data_req->sapi];
if (chan_type == 255)
return;
gsmtap_send(fl1h->gsmtap, trx->arfcn, data_req->u8Tn,
chan_type, ss, data_req->u32Fn, 0, 0,
data_req->msgUnitParam.u8Buffer,
data_req->msgUnitParam.u8Size);
}
}
static void ul_to_gsmtap(struct femtol1_hdl *fl1h, struct msgb *msg)
{
struct gsm_bts_trx *trx = fl1h->priv;
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
GsmL1_PhDataInd_t *data_ind = &l1p->u.phDataInd;
int skip = 0;
if (fl1h->gsmtap) {
uint8_t ss, chan_type;
if (data_ind->subCh == 0x1f)
ss = 0;
else
ss = data_ind->subCh;
if (!(fl1h->gsmtap_sapi_mask & (1 << data_ind->sapi)))
return;
chan_type = l1sapi2gsmtap_cht[data_ind->sapi];
if (chan_type == 255)
return;
if (chan_type == GSMTAP_CHANNEL_PACCH
|| chan_type == GSMTAP_CHANNEL_PDCH) {
if (data_ind->msgUnitParam.u8Buffer[0]
!= GsmL1_PdtchPlType_Full)
return;
skip = 1;
}
gsmtap_send(fl1h->gsmtap, trx->arfcn | GSMTAP_ARFCN_F_UPLINK,
data_ind->u8Tn, chan_type, ss, data_ind->u32Fn,
0, 0, data_ind->msgUnitParam.u8Buffer + skip,
data_ind->msgUnitParam.u8Size - skip);
}
}
struct wait_l1_conf { struct wait_l1_conf {
struct llist_head list; /* internal linked list */ struct llist_head list; /* internal linked list */
struct osmo_timer_list timer; /* timer for L1 timeout */ struct osmo_timer_list timer; /* timer for L1 timeout */
@@ -116,7 +212,7 @@ int l1if_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
wqueue = &fl1h->write_q[MQ_L1_WRITE]; wqueue = &fl1h->write_q[MQ_L1_WRITE];
timeout_secs = 30; timeout_secs = 30;
} else { } else {
FemtoBts_Prim_t *sysp = msgb_sysprim(msg); SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
LOGP(DL1C, LOGL_INFO, "Tx SYS prim %s\n", LOGP(DL1C, LOGL_INFO, "Tx SYS prim %s\n",
get_value_string(femtobts_sysprim_names, sysp->id)); get_value_string(femtobts_sysprim_names, sysp->id));
@@ -156,13 +252,13 @@ struct msgb *l1p_msgb_alloc(void)
return msg; return msg;
} }
/* allocate a msgb containing a FemtoBts_Prim_t */ /* allocate a msgb containing a SuperFemto_Prim_t */
struct msgb *sysp_msgb_alloc(void) struct msgb *sysp_msgb_alloc(void)
{ {
struct msgb *msg = msgb_alloc(sizeof(FemtoBts_Prim_t), "sys_prim"); struct msgb *msg = msgb_alloc(sizeof(SuperFemto_Prim_t), "sys_prim");
if (msg) if (msg)
msg->l1h = msgb_put(msg, sizeof(FemtoBts_Prim_t)); msg->l1h = msgb_put(msg, sizeof(SuperFemto_Prim_t));
return msg; return msg;
} }
@@ -217,8 +313,41 @@ get_lapdm_chan_by_hl2(struct gsm_bts_trx *trx, uint32_t hLayer2)
return &lchan->lapdm_ch; return &lchan->lapdm_ch;
} }
/* check if the message is a GSM48_MT_RR_CIPH_M_CMD, and if yes, enable
* uni-directional de-cryption on the uplink. We need this ugly layering
* violation as we have no way of passing down L3 metadata (RSL CIPHERING CMD)
* to this point in L1 */
static int check_for_ciph_cmd(struct femtol1_hdl *fl1h,
struct msgb *msg, struct gsm_lchan *lchan)
{
/* only do this if we are in the right state */
switch (lchan->ciph_state) {
case LCHAN_CIPH_NONE:
case LCHAN_CIPH_RX_REQ:
break;
default:
return 0;
}
/* First byte (Address Field) of LAPDm header) */
if (msg->data[0] != 0x03)
return 0;
/* First byte (protocol discriminator) of RR */
if ((msg->data[3] & 0xF) != GSM48_PDISC_RR)
return 0;
/* 2nd byte (msg type) of RR */
if ((msg->data[4] & 0x3F) != GSM48_MT_RR_CIPH_M_CMD)
return 0;
lchan->ciph_state = LCHAN_CIPH_RX_REQ;
l1if_set_ciphering(fl1h, lchan, 0);
return 1;
}
static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = { static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = {
0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x01, 0x03, 0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
0x2B, 0x2B, 0x2B 0x2B, 0x2B, 0x2B
}; };
@@ -232,7 +361,6 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
struct msgb *resp_msg; struct msgb *resp_msg;
GsmL1_PhDataReq_t *data_req; GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param; GsmL1_MsgUnitParam_t *msu_param;
struct lapdm_channel *lc;
struct lapdm_entity *le; struct lapdm_entity *le;
struct gsm_lchan *lchan; struct gsm_lchan *lchan;
struct gsm_time g_time; struct gsm_time g_time;
@@ -259,7 +387,7 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
if (!lchan) if (!lchan)
break; break;
if (lchan->abis_ip.rtp_socket) { if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket); osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
/* FIXME: we _assume_ that we never miss TDMA /* FIXME: we _assume_ that we never miss TDMA
* frames and that we always get to this point * frames and that we always get to this point
@@ -287,6 +415,13 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
/* actually transmit it */ /* actually transmit it */
goto tx; goto tx;
break; break;
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
return pcu_tx_rts_req(&trx->ts[rts_ind->u8Tn], 0,
rts_ind->u32Fn, rts_ind->u16Arfcn, rts_ind->u8BlockNbr);
case GsmL1_Sapi_Ptcch:
return pcu_tx_rts_req(&trx->ts[rts_ind->u8Tn], 1,
rts_ind->u32Fn, rts_ind->u16Arfcn, rts_ind->u8BlockNbr);
default: default:
break; break;
} }
@@ -340,13 +475,15 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
break; break;
case GsmL1_Sapi_Sdcch: case GsmL1_Sapi_Sdcch:
/* resolve the L2 entity using rts_ind->hLayer2 */ /* resolve the L2 entity using rts_ind->hLayer2 */
lc = get_lapdm_chan_by_hl2(trx, rts_ind->hLayer2); lchan = l1if_hLayer2_to_lchan(trx, rts_ind->hLayer2);
le = &lc->lapdm_dcch; le = &lchan->lapdm_ch.lapdm_dcch;
rc = lapdm_phsap_dequeue_prim(le, &pp); rc = lapdm_phsap_dequeue_prim(le, &pp);
if (rc < 0) if (rc < 0)
memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN); memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
else { else {
memcpy(msu_param->u8Buffer, pp.oph.msg->data, GSM_MACBLOCK_LEN); memcpy(msu_param->u8Buffer, pp.oph.msg->data, GSM_MACBLOCK_LEN);
/* check if it is a RR CIPH MODE CMD. if yes, enable RX ciphering */
check_for_ciph_cmd(fl1, pp.oph.msg, lchan);
msgb_free(pp.oph.msg); msgb_free(pp.oph.msg);
} }
break; break;
@@ -374,22 +511,29 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
case GsmL1_Sapi_FacchF: case GsmL1_Sapi_FacchF:
case GsmL1_Sapi_FacchH: case GsmL1_Sapi_FacchH:
/* resolve the L2 entity using rts_ind->hLayer2 */ /* resolve the L2 entity using rts_ind->hLayer2 */
lc = get_lapdm_chan_by_hl2(trx, rts_ind->hLayer2); lchan = l1if_hLayer2_to_lchan(trx, rts_ind->hLayer2);
le = &lc->lapdm_dcch; le = &lchan->lapdm_ch.lapdm_dcch;
rc = lapdm_phsap_dequeue_prim(le, &pp); rc = lapdm_phsap_dequeue_prim(le, &pp);
if (rc < 0) if (rc < 0)
goto empty_frame; goto empty_frame;
else { else {
memcpy(msu_param->u8Buffer, pp.oph.msg->data, GSM_MACBLOCK_LEN); memcpy(msu_param->u8Buffer, pp.oph.msg->data, GSM_MACBLOCK_LEN);
/* check if it is a RR CIPH MODE CMD. if yes, enable RX ciphering */
check_for_ciph_cmd(fl1, pp.oph.msg, lchan);
msgb_free(pp.oph.msg); msgb_free(pp.oph.msg);
} }
break; break;
/* we should never receive a request here */ case GsmL1_Sapi_Prach:
goto empty_frame;
break;
default: default:
memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN); memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
break; break;
} }
tx: tx:
tx_to_gsmtap(fl1, resp_msg);
/* transmit */ /* transmit */
osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], resp_msg); osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], resp_msg);
@@ -405,6 +549,15 @@ empty_frame:
static int handle_mph_time_ind(struct femtol1_hdl *fl1, static int handle_mph_time_ind(struct femtol1_hdl *fl1,
GsmL1_MphTimeInd_t *time_ind) GsmL1_MphTimeInd_t *time_ind)
{ {
struct gsm_bts_trx *trx = fl1->priv;
struct gsm_bts *bts = trx->bts;
struct gsm_bts_role_bts *btsb = bts->role;
int frames_expired = time_ind->u32Fn - fl1->gsm_time.fn;
/* update time on PCU interface */
pcu_tx_time_ind(time_ind->u32Fn);
/* Update our data structures with the current GSM time */ /* Update our data structures with the current GSM time */
gsm_fn2gsmtime(&fl1->gsm_time, time_ind->u32Fn); gsm_fn2gsmtime(&fl1->gsm_time, time_ind->u32Fn);
@@ -415,6 +568,19 @@ static int handle_mph_time_ind(struct femtol1_hdl *fl1,
/* increment the primitive count for the alive timer */ /* increment the primitive count for the alive timer */
fl1->alive_prim_cnt++; fl1->alive_prim_cnt++;
/* increment number of RACH slots that have passed by since the
* last time indication */
if (trx == bts->c0) {
unsigned int num_rach_per_frame;
/* 27 / 51 taken from TS 05.01 Figure 3 */
if (bts->c0->ts[0].pchan == GSM_PCHAN_CCCH_SDCCH4)
num_rach_per_frame = 27;
else
num_rach_per_frame = 51;
btsb->load.rach.total += frames_expired * num_rach_per_frame;
}
return 0; return 0;
} }
@@ -450,6 +616,11 @@ static int process_meas_res(struct gsm_lchan *lchan, GsmL1_MeasParam_t *m)
{ {
struct bts_ul_meas ulm; struct bts_ul_meas ulm;
/* in the GPRS case we are not interested in measurement
* processing. The PCU will take care of it */
if (lchan->type == GSM_LCHAN_PDTCH)
return 0;
ulm.ta_offs_qbits = m->i16BurstTiming; ulm.ta_offs_qbits = m->i16BurstTiming;
ulm.ber10k = (unsigned int) (m->fBer * 100); ulm.ber10k = (unsigned int) (m->fBer * 100);
ulm.inv_rssi = (uint8_t) (m->fRssi * -1); ulm.inv_rssi = (uint8_t) (m->fRssi * -1);
@@ -460,11 +631,14 @@ static int process_meas_res(struct gsm_lchan *lchan, GsmL1_MeasParam_t *m)
static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_ind, static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_ind,
struct msgb *l1p_msg) struct msgb *l1p_msg)
{ {
struct gsm_bts_trx *trx = fl1->priv;
struct osmo_phsap_prim pp; struct osmo_phsap_prim pp;
struct gsm_lchan *lchan; struct gsm_lchan *lchan;
struct lapdm_entity *le; struct lapdm_entity *le;
struct msgb *msg; struct msgb *msg;
int rc; int rc = 0;
ul_to_gsmtap(fl1, l1p_msg);
lchan = l1if_hLayer2_to_lchan(fl1->priv, data_ind->hLayer2); lchan = l1if_hLayer2_to_lchan(fl1->priv, data_ind->hLayer2);
if (!lchan) { if (!lchan) {
@@ -496,6 +670,19 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
case GsmL1_Sapi_Sdcch: case GsmL1_Sapi_Sdcch:
case GsmL1_Sapi_FacchF: case GsmL1_Sapi_FacchF:
case GsmL1_Sapi_FacchH: case GsmL1_Sapi_FacchH:
/* if this is the first valid message after enabling Rx
* decryption, we have to enable Tx encryption */
if (lchan->ciph_state == LCHAN_CIPH_RX_CONF) {
/* HACK: check if it's an I frame, in order to
* ignore some still buffered/queued UI frames received
* before decryption was enabled */
if (data_ind->msgUnitParam.u8Buffer[0] == 0x01 &&
(data_ind->msgUnitParam.u8Buffer[1] & 0x01) == 0) {
l1if_set_ciphering(fl1, lchan, 1);
lchan->ciph_state = LCHAN_CIPH_TXRX_REQ;
}
}
/* SDCCH, SACCH and FACCH all go to LAPDm */ /* SDCCH, SACCH and FACCH all go to LAPDm */
le = le_by_l1_sapi(&lchan->lapdm_ch, data_ind->sapi); le = le_by_l1_sapi(&lchan->lapdm_ch, data_ind->sapi);
/* allocate and fill LAPDm primitive */ /* allocate and fill LAPDm primitive */
@@ -520,6 +707,27 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
/* TCH speech frame handling */ /* TCH speech frame handling */
rc = l1if_tch_rx(lchan, l1p_msg); rc = l1if_tch_rx(lchan, l1p_msg);
break; break;
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
/* drop incomplete UL block */
if (data_ind->msgUnitParam.u8Buffer[0]
!= GsmL1_PdtchPlType_Full)
break;
/* PDTCH / PACCH frame handling */
rc = pcu_tx_data_ind(&trx->ts[data_ind->u8Tn], 0,
data_ind->u32Fn, data_ind->u16Arfcn,
data_ind->u8BlockNbr,
data_ind->msgUnitParam.u8Buffer + 1,
data_ind->msgUnitParam.u8Size - 1);
break;
case GsmL1_Sapi_Ptcch:
/* PTCCH frame handling */
rc = pcu_tx_data_ind(&trx->ts[data_ind->u8Tn], 1,
data_ind->u32Fn, data_ind->u16Arfcn,
data_ind->u8BlockNbr,
data_ind->msgUnitParam.u8Buffer,
data_ind->msgUnitParam.u8Size);
break;
default: default:
LOGP(DL1C, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n", LOGP(DL1C, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n",
get_value_string(femtobts_l1sapi_names, data_ind->sapi)); get_value_string(femtobts_l1sapi_names, data_ind->sapi));
@@ -532,12 +740,25 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
static int handle_ph_ra_ind(struct femtol1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind) static int handle_ph_ra_ind(struct femtol1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind)
{ {
struct gsm_bts_trx *trx = fl1->priv;
struct gsm_bts *bts = trx->bts;
struct gsm_bts_role_bts *btsb = bts->role;
struct osmo_phsap_prim pp; struct osmo_phsap_prim pp;
struct lapdm_channel *lc; struct lapdm_channel *lc;
uint8_t acc_delay;
/* increment number of busy RACH slots, if required */
if (trx == bts->c0 &&
ra_ind->measParam.fRssi >= btsb->load.rach.busy_thresh)
btsb->load.rach.busy++;
if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH) if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
return 0; return 0;
/* increment number of RACH slots with valid RACH burst */
if (trx == bts->c0)
btsb->load.rach.access++;
DEBUGP(DL1C, "Rx PH-RA.ind"); DEBUGP(DL1C, "Rx PH-RA.ind");
dump_meas_res(&ra_ind->measParam); dump_meas_res(&ra_ind->measParam);
@@ -547,17 +768,31 @@ static int handle_ph_ra_ind(struct femtol1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind)
return -ENODEV; return -ENODEV;
} }
/* check for under/overflow / sign */
if (ra_ind->measParam.i16BurstTiming < 0)
acc_delay = 0;
else
acc_delay = ra_ind->measParam.i16BurstTiming >> 2;
if (acc_delay > btsb->max_ta) {
LOGP(DL1C, LOGL_INFO, "ignoring RACH request %u > max_ta(%u)\n",
acc_delay, btsb->max_ta);
return 0;
}
/* check for packet access */
if (trx == bts->c0
&& (ra_ind->msgUnitParam.u8Buffer[0] & 0xf0) == 0x70) {
LOGP(DL1C, LOGL_INFO, "RACH for packet access\n");
return pcu_tx_rach_ind(bts, ra_ind->measParam.i16BurstTiming,
ra_ind->msgUnitParam.u8Buffer[0], ra_ind->u32Fn);
}
osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_RACH, osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_RACH,
PRIM_OP_INDICATION, NULL); PRIM_OP_INDICATION, NULL);
pp.u.rach_ind.ra = ra_ind->msgUnitParam.u8Buffer[0]; pp.u.rach_ind.ra = ra_ind->msgUnitParam.u8Buffer[0];
pp.u.rach_ind.fn = ra_ind->u32Fn; pp.u.rach_ind.fn = ra_ind->u32Fn;
/* FIXME: check for under/overflow / sign */ pp.u.rach_ind.acc_delay = acc_delay;
if (ra_ind->measParam.i16BurstTiming <= 0 ||
ra_ind->measParam.i16BurstTiming > 63 * 4)
pp.u.rach_ind.acc_delay = 0;
else
pp.u.rach_ind.acc_delay = ra_ind->measParam.i16BurstTiming >> 2;
return lapdm_phsap_up(&pp.oph, &lc->lapdm_dcch); return lapdm_phsap_up(&pp.oph, &lc->lapdm_dcch);
} }
@@ -596,7 +831,7 @@ static int l1if_handle_ind(struct femtol1_hdl *fl1, struct msgb *msg)
return rc; return rc;
} }
int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg) int l1if_handle_l1prim(int wq, struct femtol1_hdl *fl1h, struct msgb *msg)
{ {
GsmL1_Prim_t *l1p = msgb_l1prim(msg); GsmL1_Prim_t *l1p = msgb_l1prim(msg);
struct wait_l1_conf *wlc; struct wait_l1_conf *wlc;
@@ -607,8 +842,8 @@ int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg)
/* silent, don't clog the log file */ /* silent, don't clog the log file */
break; break;
default: default:
LOGP(DL1P, LOGL_DEBUG, "Rx L1 prim %s\n", LOGP(DL1P, LOGL_DEBUG, "Rx L1 prim %s on queue %d\n",
get_value_string(femtobts_l1prim_names, l1p->id)); get_value_string(femtobts_l1prim_names, l1p->id), wq);
} }
/* check if this is a resposne to a sync-waiting request */ /* check if this is a resposne to a sync-waiting request */
@@ -617,7 +852,10 @@ int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg)
* sending the same primitive */ * sending the same primitive */
if (wlc->is_sys_prim == 0 && l1p->id == wlc->conf_prim_id) { if (wlc->is_sys_prim == 0 && l1p->id == wlc->conf_prim_id) {
llist_del(&wlc->list); llist_del(&wlc->list);
rc = wlc->cb(msg, wlc->cb_data); if (wlc->cb)
rc = wlc->cb(msg, wlc->cb_data);
else
rc = 0;
release_wlc(wlc); release_wlc(wlc);
return rc; return rc;
} }
@@ -629,7 +867,7 @@ int l1if_handle_l1prim(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)
{ {
FemtoBts_Prim_t *sysp = msgb_sysprim(msg); SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
struct wait_l1_conf *wlc; struct wait_l1_conf *wlc;
int rc; int rc;
@@ -642,7 +880,10 @@ int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg)
* sending the same primitive */ * sending the same primitive */
if (wlc->is_sys_prim && sysp->id == wlc->conf_prim_id) { if (wlc->is_sys_prim && sysp->id == wlc->conf_prim_id) {
llist_del(&wlc->list); llist_del(&wlc->list);
rc = wlc->cb(msg, wlc->cb_data); if (wlc->cb)
rc = wlc->cb(msg, wlc->cb_data);
else
rc = 0;
release_wlc(wlc); release_wlc(wlc);
return rc; return rc;
} }
@@ -669,14 +910,14 @@ int sysinfo_has_changed(struct gsm_bts *bts, int si)
static int activate_rf_compl_cb(struct msgb *resp, void *data) static int activate_rf_compl_cb(struct msgb *resp, void *data)
{ {
FemtoBts_Prim_t *sysp = msgb_sysprim(resp); SuperFemto_Prim_t *sysp = msgb_sysprim(resp);
struct femtol1_hdl *fl1h = data; struct femtol1_hdl *fl1h = data;
struct gsm_bts_trx *trx = fl1h->priv; struct gsm_bts_trx *trx = fl1h->priv;
GsmL1_Status_t status; GsmL1_Status_t status;
int on = 0; int on = 0;
unsigned int i; unsigned int i;
if (sysp->id == FemtoBts_PrimId_ActivateRfCnf) if (sysp->id == SuperFemto_PrimId_ActivateRfCnf)
on = 1; on = 1;
if (on) if (on)
@@ -693,7 +934,9 @@ static int activate_rf_compl_cb(struct msgb *resp, void *data)
LOGP(DL1C, LOGL_FATAL, "RF-ACT.conf with status %s\n", LOGP(DL1C, LOGL_FATAL, "RF-ACT.conf with status %s\n",
get_value_string(femtobts_l1status_names, status)); get_value_string(femtobts_l1status_names, status));
bts_shutdown(trx->bts, "RF-ACT failure"); bts_shutdown(trx->bts, "RF-ACT failure");
} } else
sysmobts_led_set(LED_RF_ACTIVE, 1);
/* signal availability */ /* signal availability */
oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OK); oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OK);
oml_mo_tx_sw_act_rep(&trx->mo); oml_mo_tx_sw_act_rep(&trx->mo);
@@ -703,6 +946,7 @@ static int activate_rf_compl_cb(struct msgb *resp, void *data)
for (i = 0; i < ARRAY_SIZE(trx->ts); i++) for (i = 0; i < ARRAY_SIZE(trx->ts); i++)
oml_mo_state_chg(&trx->ts[i].mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY); oml_mo_state_chg(&trx->ts[i].mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY);
} else { } else {
sysmobts_led_set(LED_RF_ACTIVE, 0);
oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE); oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
oml_mo_state_chg(&trx->bb_transc.mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE); oml_mo_state_chg(&trx->bb_transc.mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
} }
@@ -716,23 +960,99 @@ static int activate_rf_compl_cb(struct msgb *resp, void *data)
int l1if_activate_rf(struct femtol1_hdl *hdl, int on) int l1if_activate_rf(struct femtol1_hdl *hdl, int on)
{ {
struct msgb *msg = sysp_msgb_alloc(); struct msgb *msg = sysp_msgb_alloc();
FemtoBts_Prim_t *sysp = msgb_sysprim(msg); SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
if (on) { if (on) {
sysp->id = FemtoBts_PrimId_ActivateRfReq; sysp->id = SuperFemto_PrimId_ActivateRfReq;
sysp->u.activateRfReq.u12ClkVc = 0xFFFF; #ifdef HW_SYSMOBTS_V1
sysp->u.activateRfReq.u12ClkVc = hdl->clk_cal;
#else
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(0,2,0)
sysp->u.activateRfReq.timing.u8TimSrc = 1; /* Master */
#endif /* 0.2.0 */
sysp->u.activateRfReq.msgq.u8UseTchMsgq = 0;
sysp->u.activateRfReq.msgq.u8UsePdtchMsgq = pcu_direct;
/* Use clock from OCXO or whatever source is configured */
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,1,0)
sysp->u.activateRfReq.rfTrx.u8ClkSrc = hdl->clk_src;
#else
sysp->u.activateRfReq.rfTrx.clkSrc = hdl->clk_src;
#endif /* 2.1.0 */
sysp->u.activateRfReq.rfTrx.iClkCor = hdl->clk_cal;
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,1,0)
sysp->u.activateRfReq.rfRx.u8ClkSrc = hdl->clk_src;
#else
sysp->u.activateRfReq.rfRx.clkSrc = hdl->clk_src;
#endif /* 2.1.0 */
sysp->u.activateRfReq.rfRx.iClkCor = hdl->clk_cal;
#endif /* API 2.4.0 */
#endif /* !HW_SYSMOBTS_V1 */
} else { } else {
sysp->id = FemtoBts_PrimId_DeactivateRfReq; sysp->id = SuperFemto_PrimId_DeactivateRfReq;
} }
return l1if_req_compl(hdl, msg, 1, activate_rf_compl_cb, hdl); return l1if_req_compl(hdl, msg, 1, activate_rf_compl_cb, hdl);
} }
/* call-back on arrival of DSP+FPGA version + band capability */
static int info_compl_cb(struct msgb *resp, void *data)
{
SuperFemto_Prim_t *sysp = msgb_sysprim(resp);
SuperFemto_SystemInfoCnf_t *sic = &sysp->u.systemInfoCnf;
struct femtol1_hdl *fl1h = data;
struct gsm_bts_trx *trx = fl1h->priv;
fl1h->hw_info.dsp_version[0] = sic->dspVersion.major;
fl1h->hw_info.dsp_version[1] = sic->dspVersion.minor;
fl1h->hw_info.dsp_version[2] = sic->dspVersion.build;
fl1h->hw_info.fpga_version[0] = sic->fpgaVersion.major;
fl1h->hw_info.fpga_version[1] = sic->fpgaVersion.minor;
fl1h->hw_info.fpga_version[2] = sic->fpgaVersion.build;
LOGP(DL1C, LOGL_INFO, "DSP v%u.%u.%u, FPGA v%u.%u.%u\n",
sic->dspVersion.major, sic->dspVersion.minor,
sic->dspVersion.build, sic->fpgaVersion.major,
sic->fpgaVersion.minor, sic->fpgaVersion.build);
#ifdef HW_SYSMOBTS_V1
if (sic->rfBand.gsm850)
fl1h->hw_info.band_support |= GSM_BAND_850;
if (sic->rfBand.gsm900)
fl1h->hw_info.band_support |= GSM_BAND_900;
if (sic->rfBand.dcs1800)
fl1h->hw_info.band_support |= GSM_BAND_1800;
if (sic->rfBand.pcs1900)
fl1h->hw_info.band_support |= GSM_BAND_1900;
#else
fl1h->hw_info.band_support |= GSM_BAND_850 | GSM_BAND_900 | GSM_BAND_1800 | GSM_BAND_1900;
#endif
if (!(fl1h->hw_info.band_support & trx->bts->band))
LOGP(DL1C, LOGL_FATAL, "BTS band %s not supported by hw\n",
gsm_band_name(trx->bts->band));
/* FIXME: clock related */
return 0;
}
/* request DSP+FPGA code versions + band capability */
static int l1if_get_info(struct femtol1_hdl *hdl)
{
struct msgb *msg = sysp_msgb_alloc();
SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
sysp->id = SuperFemto_PrimId_SystemInfoReq;
return l1if_req_compl(hdl, msg, 1, info_compl_cb, hdl);
}
static int reset_compl_cb(struct msgb *resp, void *data) static int reset_compl_cb(struct msgb *resp, void *data)
{ {
struct femtol1_hdl *fl1h = data; struct femtol1_hdl *fl1h = data;
struct gsm_bts_trx *trx = fl1h->priv; struct gsm_bts_trx *trx = fl1h->priv;
FemtoBts_Prim_t *sysp = msgb_sysprim(resp); SuperFemto_Prim_t *sysp = msgb_sysprim(resp);
GsmL1_Status_t status = sysp->u.layer1ResetCnf.status; GsmL1_Status_t status = sysp->u.layer1ResetCnf.status;
LOGP(DL1C, LOGL_NOTICE, "Rx L1-RESET.conf (status=%s)\n", LOGP(DL1C, LOGL_NOTICE, "Rx L1-RESET.conf (status=%s)\n",
@@ -751,6 +1071,17 @@ static int reset_compl_cb(struct msgb *resp, void *data)
* set them to zero (or whatever dsp_trace_f has been initialized to */ * set them to zero (or whatever dsp_trace_f has been initialized to */
l1if_set_trace_flags(fl1h, fl1h->dsp_trace_f); l1if_set_trace_flags(fl1h, fl1h->dsp_trace_f);
/* obtain version information on DSP/FPGA and band capabilities */
l1if_get_info(fl1h);
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
/* load calibration tables (if we know their path) */
if (fl1h->calib_path)
calib_load(fl1h);
else
#endif
LOGP(DL1C, LOGL_NOTICE, "Operating without calibration tables!\n");
/* otherwise, request activation of RF board */ /* otherwise, request activation of RF board */
l1if_activate_rf(fl1h, 1); l1if_activate_rf(fl1h, 1);
@@ -760,8 +1091,8 @@ static int reset_compl_cb(struct msgb *resp, void *data)
int l1if_reset(struct femtol1_hdl *hdl) int l1if_reset(struct femtol1_hdl *hdl)
{ {
struct msgb *msg = sysp_msgb_alloc(); struct msgb *msg = sysp_msgb_alloc();
FemtoBts_Prim_t *sysp = msgb_sysprim(msg); SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
sysp->id = FemtoBts_PrimId_Layer1ResetReq; sysp->id = SuperFemto_PrimId_Layer1ResetReq;
return l1if_req_compl(hdl, msg, 1, reset_compl_cb, hdl); return l1if_req_compl(hdl, msg, 1, reset_compl_cb, hdl);
} }
@@ -770,12 +1101,12 @@ int l1if_reset(struct femtol1_hdl *hdl)
int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags) int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags)
{ {
struct msgb *msg = sysp_msgb_alloc(); struct msgb *msg = sysp_msgb_alloc();
FemtoBts_Prim_t *sysp = msgb_sysprim(msg); SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
LOGP(DL1C, LOGL_INFO, "Tx SET-TRACE-FLAGS.req (0x%08x)\n", LOGP(DL1C, LOGL_INFO, "Tx SET-TRACE-FLAGS.req (0x%08x)\n",
flags); flags);
sysp->id = FemtoBts_PrimId_SetTraceFlagsReq; sysp->id = SuperFemto_PrimId_SetTraceFlagsReq;
sysp->u.setTraceFlagsReq.u32Tf = flags; sysp->u.setTraceFlagsReq.u32Tf = flags;
hdl->dsp_trace_f = flags; hdl->dsp_trace_f = flags;
@@ -784,28 +1115,93 @@ int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags)
return osmo_wqueue_enqueue(&hdl->write_q[MQ_SYS_WRITE], msg); return osmo_wqueue_enqueue(&hdl->write_q[MQ_SYS_WRITE], msg);
} }
/* send packet data request to L1 */
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)
{
struct gsm_bts_trx *trx = ts->trx;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
struct msgb *msg;
GsmL1_Prim_t *l1p;
GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param;
struct gsm_time g_time;
gsm_fn2gsmtime(&g_time, fn);
DEBUGP(DL1P, "TX packet data %02u/%02u/%02u is_ptcch=%d trx=%d ts=%d "
"block_nr=%d, arfcn=%d, len=%d\n", g_time.t1, g_time.t2,
g_time.t3, is_ptcch, ts->trx->nr, ts->nr, block_nr, arfcn, len);
msg = l1p_msgb_alloc();
l1p = msgb_l1prim(msg);
l1p->id = GsmL1_PrimId_PhDataReq;
data_req = &l1p->u.phDataReq;
data_req->hLayer1 = fl1h->hLayer1;
data_req->sapi = (is_ptcch) ? GsmL1_Sapi_Ptcch : GsmL1_Sapi_Pdtch;
data_req->subCh = GsmL1_SubCh_NA;
data_req->u8BlockNbr = block_nr;
data_req->u8Tn = ts->nr;
data_req->u32Fn = fn;
msu_param = &data_req->msgUnitParam;
msu_param->u8Size = len;
memcpy(msu_param->u8Buffer, data, len);
tx_to_gsmtap(fl1h, msg);
/* transmit */
osmo_wqueue_enqueue(&fl1h->write_q[MQ_L1_WRITE], msg);
return 0;
}
struct femtol1_hdl *l1if_open(void *priv) struct femtol1_hdl *l1if_open(void *priv)
{ {
struct femtol1_hdl *fl1h; struct femtol1_hdl *fl1h;
int rc; int rc;
LOGP(DL1C, LOGL_INFO, "sysmoBTS L1IF compiled against API headers "
"v%u.%u.%u\n", SUPERFEMTO_API_VERSION >> 16,
(SUPERFEMTO_API_VERSION >> 8) & 0xff,
SUPERFEMTO_API_VERSION & 0xff);
fl1h = talloc_zero(priv, struct femtol1_hdl); fl1h = talloc_zero(priv, struct femtol1_hdl);
if (!fl1h) if (!fl1h)
return NULL; return NULL;
INIT_LLIST_HEAD(&fl1h->wlc_list); INIT_LLIST_HEAD(&fl1h->wlc_list);
fl1h->priv = priv; fl1h->priv = priv;
fl1h->clk_cal = 0;
/* default clock source: OCXO */
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
fl1h->clk_src = SuperFemto_ClkSrcId_Ocxo;
#else
fl1h->clk_src = SF_CLKSRC_OCXO;
#endif
rc = l1if_transport_open(fl1h); rc = l1if_transport_open(MQ_SYS_WRITE, fl1h);
if (rc < 0) { if (rc < 0) {
talloc_free(fl1h); talloc_free(fl1h);
return NULL; return NULL;
} }
rc = l1if_transport_open(MQ_L1_WRITE, fl1h);
if (rc < 0) {
l1if_transport_close(MQ_SYS_WRITE, fl1h);
talloc_free(fl1h);
return NULL;
}
fl1h->gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 1);
if (fl1h->gsmtap)
gsmtap_source_add_sink(fl1h->gsmtap);
return fl1h; return fl1h;
} }
int l1if_close(struct femtol1_hdl *fl1h) int l1if_close(struct femtol1_hdl *fl1h)
{ {
return l1if_transport_close(fl1h); l1if_transport_close(MQ_L1_WRITE, fl1h);
l1if_transport_close(MQ_SYS_WRITE, fl1h);
return 0;
} }

View File

@@ -3,17 +3,27 @@
#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>
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
}; };
@@ -21,8 +31,14 @@ 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;
int clk_cal;
uint8_t clk_src;
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,10 +46,16 @@ 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 {
uint8_t dsp_version[3];
uint8_t fpga_version[3];
uint32_t band_support; /* bitmask of GSM_BAND_* */
} hw_info;
}; };
#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 msgb *l1_msg, void *data);
@@ -46,6 +68,7 @@ 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);
struct msgb *l1p_msgb_alloc(void); struct msgb *l1p_msgb_alloc(void);
struct msgb *sysp_msgb_alloc(void); struct msgb *sysp_msgb_alloc(void);
@@ -56,5 +79,6 @@ struct gsm_lchan *l1if_hLayer2_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);
#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,31 +47,57 @@
#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, prim)
osmo_static_assert(sizeof(SuperFemto_Prim_t) + 128 <= SYSMOBTS_PRIM_SIZE, prim)
/* 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 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(); //struct msgb *msg = l1p_msgb_alloc();
struct msgb *msg = msgb_alloc_headroom(2048, 128, "1l_fd"); struct msgb *msg = msgb_alloc_headroom(SYSMOBTS_PRIM_SIZE, 128, "1l_fd");
struct femtol1_hdl *fl1h = ofd->data; struct femtol1_hdl *fl1h = ofd->data;
int rc; int rc;
msg->l1h = msg->data; msg->l1h = msg->data;
rc = read(ofd->fd, msg->l1h, sizeof(GsmL1_Prim_t)); rc = read(ofd->fd, msg->l1h, msgb_tailroom(msg));
if (rc < 0) { if (rc < 0) {
if (rc != -1) if (rc != -1)
LOGP(DL1C, LOGL_ERROR, "error reading from L1 msg_queue: %s\n", LOGP(DL1C, LOGL_ERROR, "error reading from L1 msg_queue: %s\n",
@@ -80,10 +107,28 @@ static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
} }
msgb_put(msg, rc); msgb_put(msg, rc);
if (ofd->priv_nr == MQ_L1_WRITE) switch (ofd->priv_nr) {
return l1if_handle_l1prim(fl1h, msg); case MQ_SYS_WRITE:
else if (rc != sizeof(SuperFemto_Prim_t))
LOGP(DL1C, LOGL_NOTICE, "%u != "
"sizeof(SuperFemto_Prim_t)\n", rc);
return l1if_handle_sysprim(fl1h, msg); return l1if_handle_sysprim(fl1h, msg);
case MQ_L1_WRITE:
#ifndef HW_SYSMOBTS_V1
case MQ_TCH_WRITE:
case MQ_PDTCH_WRITE:
#endif
if (rc != sizeof(GsmL1_Prim_t))
LOGP(DL1C, LOGL_NOTICE, "%u != "
"sizeof(GsmL1_Prim_t)\n", rc);
return l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
default:
/* The compiler can't know that priv_nr is an enum. Assist. */
LOGP(DL1C, LOGL_FATAL, "writing on a wrong queue: %d\n",
ofd->priv_nr);
assert(false);
break;
}
}; };
/* 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 */
@@ -105,88 +150,73 @@ 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->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

@@ -24,7 +24,10 @@
#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 <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
@@ -39,18 +42,20 @@
#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>
#define SYSMOBTS_RF_LOCK_PATH "/var/lock/bts_rf_lock"
#include "l1_if.h" #include "l1_if.h"
/* FIXME: read from real hardware */ /* FIXME: read from real hardware */
const uint8_t abis_mac[6] = { 0,1,2,3,4,5 }; const uint8_t abis_mac[6] = { 0,1,2,3,4,5 };
/* FIXME: generate from git */ int pcu_direct = 0;
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;
int bts_model_init(struct gsm_bts *bts) int bts_model_init(struct gsm_bts *bts)
{ {
@@ -68,7 +73,7 @@ int bts_model_init(struct gsm_bts *bts)
l1if_reset(fl1h); l1if_reset(fl1h);
femtol1_vty_init(bts); bts_model_vty_init(bts);
return 0; return 0;
} }
@@ -102,10 +107,22 @@ 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"
" -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 +139,12 @@ 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' },
{ 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:M",
long_options, &option_idx); long_options, &option_idx);
if (c == -1) if (c == -1)
break; break;
@@ -150,6 +169,9 @@ static void handle_options(int argc, char **argv)
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 +182,10 @@ 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;
default: default:
break; break;
} }
@@ -187,8 +213,28 @@ 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;
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
struct stat st;
struct gsm_bts_role_bts *btsb; struct gsm_bts_role_bts *btsb;
struct ipabis_link *link; struct ipabis_link *link;
void *tall_msgb_ctx; void *tall_msgb_ctx;
@@ -203,14 +249,15 @@ 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);
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 to open bts\n");
exit(1); exit(1);
} }
btsb = bts_role_bts(bts); btsb = bts_role_bts(bts);
btsb->support.ciphers = (1 << 0) | (1 << 1) | (1 << 2);
handle_options(argc, argv);
rc = vty_read_config_file(config_file, NULL); rc = vty_read_config_file(config_file, NULL);
if (rc < 0) { if (rc < 0) {
@@ -219,12 +266,23 @@ int main(int argc, char **argv)
exit(1); exit(1);
} }
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");
rc = telnet_init(tall_bts_ctx, NULL, 4241); rc = telnet_init(tall_bts_ctx, NULL, 4241);
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);

View File

@@ -0,0 +1,19 @@
#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 clk_cal_fact; /* 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 */
uint8_t _pad1[89]; /* 32-127 */
uint8_t gpg_key[128]; /* 121-249 */
} __attribute__((packed));
#endif

View File

@@ -0,0 +1,163 @@
/* Main program for SysmoBTS management daemon */
/* (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 <errno.h>
#include <getopt.h>
#include <limits.h>
#include <sys/signal.h>
#include <sys/types.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 "misc/sysmobts_misc.h"
#include "misc/sysmobts_mgr.h"
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)
static struct osmo_timer_list temp_timer;
static void check_temp_timer_cb(void *unused)
{
sysmobts_check_temp();
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();
osmo_timer_schedule(&hours_timer, HOURS_TIMER_SECS, 0);
}
static void signal_handler(int signal)
{
fprintf(stderr, "signal %u received\n", signal);
switch (signal) {
case SIGINT:
sysmobts_check_temp();
sysmobts_update_hours();
exit(0);
break;
case SIGABRT:
case SIGUSR1:
case SIGUSR2:
talloc_report_full(tall_mgr_ctx, stderr);
break;
default:
break;
}
}
#include <osmocom/core/logging.h>
#include <osmocom/core/application.h>
#include <osmocom/core/utils.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/logging.h>
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,
},
};
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(const char *category_mask)
{
osmo_init_logging(&mgr_log_info);
if (category_mask)
log_parse_category_mask(osmo_stderr_target, category_mask);
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);
//handle_options(argc, argv);
mgr_log_init(NULL);
signal(SIGINT, &signal_handler);
//signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
perror("Error during daemonize");
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);
while (1) {
log_reset_context();
osmo_select_main(0);
}
}

View File

@@ -0,0 +1,9 @@
#ifndef _SYSMOBTS_MGR_H
#define _SYSMOBTS_MGR_H
enum {
DTEMP,
DFW,
};
#endif

View File

@@ -0,0 +1,268 @@
/* (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/application.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include "sysmobts_misc.h"
#include "sysmobts_par.h"
#include "sysmobts_mgr.h"
/*********************************************************************
* Temperature handling
*********************************************************************/
#define TEMP_PATH "/sys/class/hwmon/hwmon0/device%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 > _NUM_TEMP_TYPES)
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(void)
{
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++) {
temp_old[i] = sysmobts_par_get_int(temp_data[i].ee_par) * 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);
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(void)
{
time_t now = time(NULL);
int op_hrs;
/* first time after start of manager program */
if (last_update == 0) {
last_update = now;
op_hrs = sysmobts_par_get_int(SYSMOBTS_PAR_HOURS);
if (op_hrs < 0) {
LOGP(DTEMP, LOGL_ERROR, "Unable to read "
"operational hours: %d (%s)\n", op_hrs,
strerror(errno));
return op_hrs;
}
LOGP(DTEMP, LOGL_INFO, "Total hours of Operation: %u\n",
op_hrs);
return 0;
}
if (now >= last_update + 3600) {
int rc;
op_hrs = sysmobts_par_get_int(SYSMOBTS_PAR_HOURS);
if (op_hrs < 0) {
LOGP(DTEMP, LOGL_ERROR, "Unable to read "
"operational hours: %d (%s)\n", op_hrs,
strerror(errno));
return op_hrs;
}
/* number of hours to increase */
op_hrs += (now-last_update)/3600;
LOGP(DTEMP, LOGL_INFO, "Total hours of Operation: %u\n",
op_hrs);
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,31 @@
#ifndef _SYSMOBTS_MISC_H
#define _SYSMOBTS_MISC_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(void);
int sysmobts_update_hours(void);
enum sysmobts_firmware_type {
SYSMOBTS_FW_FPGA,
SYSMOBTS_FW_DSP,
_NUM_FW
};
int sysmobts_firmware_reload(enum sysmobts_firmware_type type);
#endif

View File

@@ -0,0 +1,226 @@
/* 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 "sysmobts_eeprom.h"
#include "sysmobts_par.h"
#define EEPROM_PATH "/sys/devices/platform/i2c_davinci.1/i2c-1/1-0050/eeprom"
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_get_int(enum sysmobts_par par)
{
int ret;
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:
ret = ee->clk_cal_fact;
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;
default:
return -EINVAL;
}
return ret;
}
int sysmobts_par_set_int(enum sysmobts_par par, unsigned int val)
{
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:
ee->clk_cal_fact = val;
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;
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,26 @@
#ifndef _SYSMOBTS_PAR_H
#define _SYSMOBTS_PAR_H
#include <stdint.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,
_NUM_SYSMOBTS_PAR
};
int sysmobts_par_get_int(enum sysmobts_par par);
int sysmobts_par_set_int(enum sysmobts_par par, unsigned 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);
#endif

View File

@@ -18,6 +18,7 @@
*/ */
#include <stdint.h> #include <stdint.h>
#include <errno.h>
#include <osmocom/core/talloc.h> #include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h> #include <osmocom/core/utils.h>
@@ -29,6 +30,10 @@
#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/oml.h> #include <osmo-bts/oml.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/amr.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/bts_model.h>
#include "l1_if.h" #include "l1_if.h"
#include "femtobts.h" #include "femtobts.h"
@@ -226,7 +231,7 @@ static int trx_init_compl_cb(struct msgb *l1_msg, void *data)
return opstart_compl_cb(l1_msg, &trx->mo); return opstart_compl_cb(l1_msg, &trx->mo);
} }
int gsm_abis_mo_check_attr(const struct gsm_abis_mo *mo, uint8_t *attr_ids, int gsm_abis_mo_check_attr(const struct gsm_abis_mo *mo, const uint8_t *attr_ids,
unsigned int num_attr_ids) unsigned int num_attr_ids)
{ {
unsigned int i; unsigned int i;
@@ -250,7 +255,6 @@ static int trx_init(struct gsm_bts_trx *trx)
struct msgb *msg; struct msgb *msg;
GsmL1_MphInitReq_t *mi_req; GsmL1_MphInitReq_t *mi_req;
GsmL1_DeviceParam_t *dev_par; GsmL1_DeviceParam_t *dev_par;
enum gsm_band osmo_band;
int femto_band; int femto_band;
if (!gsm_abis_mo_check_attr(&trx->mo, trx_rqd_attr, if (!gsm_abis_mo_check_attr(&trx->mo, trx_rqd_attr,
@@ -261,11 +265,10 @@ static int trx_init(struct gsm_bts_trx *trx)
//return oml_mo_opstart_nack(&trx->mo, NM_NACK_CANT_PERFORM); //return oml_mo_opstart_nack(&trx->mo, NM_NACK_CANT_PERFORM);
} }
osmo_band = gsm_arfcn2band(trx->arfcn); femto_band = band_osmo2femto(trx->bts->band);
femto_band = band_osmo2femto(osmo_band);
if (femto_band < 0) { if (femto_band < 0) {
LOGP(DL1C, LOGL_ERROR, "Unsupported GSM band %s\n", LOGP(DL1C, LOGL_ERROR, "Unsupported GSM band %s\n",
gsm_band_name(osmo_band)); gsm_band_name(trx->bts->band));
} }
msg = l1p_msgb_alloc(); msg = l1p_msgb_alloc();
@@ -286,6 +289,30 @@ static int trx_init(struct gsm_bts_trx *trx)
return l1if_req_compl(fl1h, msg, 0, trx_init_compl_cb, fl1h); return l1if_req_compl(fl1h, msg, 0, trx_init_compl_cb, fl1h);
} }
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
return fl1h->hLayer1;
}
static int trx_close_compl_cb(struct msgb *l1_msg, void *data)
{
return 0;
}
static int trx_close(struct gsm_bts_trx *trx)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
struct msgb *msg;
msg = l1p_msgb_alloc();
prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphCloseReq, fl1h);
LOGP(DL1C, LOGL_NOTICE, "Close TRX %u\n", trx->nr);
return l1if_req_compl(fl1h, msg, 0, trx_close_compl_cb, fl1h);
}
static int ts_connect(struct gsm_bts_trx_ts *ts) static int ts_connect(struct gsm_bts_trx_ts *ts)
{ {
struct msgb *msg = l1p_msgb_alloc(); struct msgb *msg = l1p_msgb_alloc();
@@ -353,20 +380,39 @@ static const struct sapi_dir ccch_sapis[] = {
#define DIR_BOTH (GsmL1_Dir_TxDownlink|GsmL1_Dir_RxUplink) #define DIR_BOTH (GsmL1_Dir_TxDownlink|GsmL1_Dir_RxUplink)
static const struct sapi_dir tchf_sapis[] = { static const struct sapi_dir tchf_sapis[] = {
{ GsmL1_Sapi_TchF, DIR_BOTH }, { GsmL1_Sapi_TchF, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_FacchF, DIR_BOTH }, { GsmL1_Sapi_TchF, GsmL1_Dir_RxUplink },
{ GsmL1_Sapi_Sacch, DIR_BOTH }, { GsmL1_Sapi_FacchF, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_FacchF, GsmL1_Dir_RxUplink },
{ GsmL1_Sapi_Sacch, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_Sacch, GsmL1_Dir_RxUplink },
}; };
static const struct sapi_dir tchh_sapis[] = { static const struct sapi_dir tchh_sapis[] = {
{ GsmL1_Sapi_TchH, DIR_BOTH }, { GsmL1_Sapi_TchH, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_FacchH, DIR_BOTH }, { GsmL1_Sapi_TchH, GsmL1_Dir_RxUplink },
{ GsmL1_Sapi_Sacch, DIR_BOTH }, { GsmL1_Sapi_FacchH, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_FacchH, GsmL1_Dir_RxUplink },
{ GsmL1_Sapi_Sacch, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_Sacch, GsmL1_Dir_RxUplink },
}; };
static const struct sapi_dir sdcch_sapis[] = { static const struct sapi_dir sdcch_sapis[] = {
{ GsmL1_Sapi_Sdcch, DIR_BOTH }, { GsmL1_Sapi_Sdcch, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_Sacch, DIR_BOTH }, { GsmL1_Sapi_Sdcch, GsmL1_Dir_RxUplink },
{ GsmL1_Sapi_Sacch, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_Sacch, GsmL1_Dir_RxUplink },
};
static const struct sapi_dir pdtch_sapis[] = {
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_RxUplink },
{ GsmL1_Sapi_Ptcch, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_Prach, GsmL1_Dir_RxUplink },
#if 0
{ GsmL1_Sapi_Ptcch, GsmL1_Dir_RxUplink },
{ GsmL1_Sapi_Pacch, GsmL1_Dir_TxDownlink },
#endif
}; };
struct lchan_sapis { struct lchan_sapis {
@@ -391,31 +437,48 @@ static const struct lchan_sapis sapis_for_lchan[_GSM_LCHAN_MAX] = {
.sapis = ccch_sapis, .sapis = ccch_sapis,
.num_sapis = ARRAY_SIZE(ccch_sapis), .num_sapis = ARRAY_SIZE(ccch_sapis),
}, },
[GSM_LCHAN_PDTCH] = {
.sapis = pdtch_sapis,
.num_sapis = ARRAY_SIZE(pdtch_sapis),
},
}; };
static int lchan_act_compl_cb(struct msgb *l1_msg, void *data) static int lchan_act_compl_cb(struct msgb *l1_msg, void *data)
{ {
struct gsm_time *time;
struct gsm_lchan *lchan = data; struct gsm_lchan *lchan = data;
GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg); GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
GsmL1_MphActivateCnf_t *ic = &l1p->u.mphActivateCnf; GsmL1_MphActivateCnf_t *ic = &l1p->u.mphActivateCnf;
LOGP(DL1C, LOGL_INFO, "%s MPH-ACTIVATE.conf\n", gsm_lchan_name(lchan)); LOGP(DL1C, LOGL_INFO, "%s MPH-ACTIVATE.conf (%s ",
gsm_lchan_name(lchan),
get_value_string(femtobts_l1sapi_names, ic->sapi));
LOGPC(DL1C, LOGL_INFO, "%s)\n",
get_value_string(femtobts_dir_names, ic->dir));
if (ic->status == GsmL1_Status_Success) { if (ic->status == GsmL1_Status_Success) {
DEBUGP(DL1C, "Successful activation of L1 SAPI %s on TS %u\n", DEBUGP(DL1C, "Successful activation of L1 SAPI %s on TS %u\n",
get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn); get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn);
lchan->state = LCHAN_S_ACTIVE; lchan_set_state(lchan, LCHAN_S_ACTIVE);
} else { } else {
LOGP(DL1C, LOGL_ERROR, "Error activating L1 SAPI %s on TS %u: %s\n", LOGP(DL1C, LOGL_ERROR, "Error activating L1 SAPI %s on TS %u: %s\n",
get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn, get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn,
get_value_string(femtobts_l1status_names, ic->status)); get_value_string(femtobts_l1status_names, ic->status));
lchan->state = LCHAN_S_NONE; lchan_set_state(lchan, LCHAN_S_NONE);
} }
switch (ic->sapi) { switch (ic->sapi) {
case GsmL1_Sapi_Sdcch: case GsmL1_Sapi_Sdcch:
case GsmL1_Sapi_TchF: case GsmL1_Sapi_TchF:
/* FIXME: Send RSL CHAN ACT */ case GsmL1_Sapi_TchH:
time = bts_model_get_time(lchan->ts->trx->bts);
if (lchan->state == LCHAN_S_ACTIVE) {
/* Hack: we simply only use one direction to
* avoid sending two ACKs for one activate */
if (ic->dir == GsmL1_Dir_TxDownlink)
rsl_tx_chan_act_ack(lchan, time);
} else
rsl_tx_chan_act_nack(lchan, RSL_ERR_EQUIPMENT_FAIL);
break; break;
default: default:
break; break;
@@ -466,28 +529,62 @@ static void alive_timer_cb(void *data)
osmo_timer_schedule(&fl1h->alive_timer, 5, 0); osmo_timer_schedule(&fl1h->alive_timer, 5, 0);
} }
static void clear_amr_params(GsmL1_LogChParam_t *lch_par)
{
int j;
/* common for the SIGN, V1 and EFR: */
lch_par->tch.amrCmiPhase = GsmL1_AmrCmiPhase_NA;
lch_par->tch.amrInitCodecMode = GsmL1_AmrCodecMode_Unset;
for (j = 0; j < ARRAY_SIZE(lch_par->tch.amrActiveCodecSet); j++)
lch_par->tch.amrActiveCodecSet[j] = GsmL1_AmrCodec_Unset;
}
static void set_payload_format(GsmL1_LogChParam_t *lch_par)
{
#ifdef L1_HAS_RTP_MODE
#ifdef USE_L1_RTP_MODE
lch_par->tch.tchPlFmt = GsmL1_TchPlFmt_Rtp;
#else
lch_par->tch.tchPlFmt = GsmL1_TchPlFmt_If2;
#endif /* USE_L1_RTP_MODE */
#endif /* L1_HAS_RTP_MODE */
}
static void lchan2lch_par(GsmL1_LogChParam_t *lch_par, struct gsm_lchan *lchan) static void lchan2lch_par(GsmL1_LogChParam_t *lch_par, struct gsm_lchan *lchan)
{ {
int j; int j;
LOGPC(DL1C, LOGL_INFO, "%s: %s tch_mode=0x%02x\n", LOGP(DL1C, LOGL_INFO, "%s: %s tch_mode=0x%02x\n",
gsm_lchan_name(lchan), __FUNCTION__, lchan->tch_mode); gsm_lchan_name(lchan), __FUNCTION__, lchan->tch_mode);
switch (lchan->tch_mode) { switch (lchan->tch_mode) {
case GSM48_CMODE_SIGN: case GSM48_CMODE_SIGN:
/* we have to set some TCH payload type even if we don't
* know yet what codec we will use later on */
if (lchan->type == GSM_LCHAN_TCH_F)
lch_par->tch.tchPlType = GsmL1_TchPlType_Fr;
else
lch_par->tch.tchPlType = GsmL1_TchPlType_Hr;
clear_amr_params(lch_par);
break;
case GSM48_CMODE_SPEECH_V1: case GSM48_CMODE_SPEECH_V1:
if (lchan->type == GSM_LCHAN_TCH_F)
lch_par->tch.tchPlType = GsmL1_TchPlType_Fr;
else
lch_par->tch.tchPlType = GsmL1_TchPlType_Hr;
set_payload_format(lch_par);
clear_amr_params(lch_par);
break;
case GSM48_CMODE_SPEECH_EFR: case GSM48_CMODE_SPEECH_EFR:
lch_par->tch.amrCmiPhase = GsmL1_AmrCmiPhase_NA; lch_par->tch.tchPlType = GsmL1_TchPlType_Efr;
lch_par->tch.amrInitCodecMode = GsmL1_AmrCodecMode_Unset; set_payload_format(lch_par);
for (j = 0; j < ARRAY_SIZE(lch_par->tch.amrActiveCodecSet); j++) clear_amr_params(lch_par);
lch_par->tch.amrActiveCodecSet[j] = GsmL1_AmrCodec_Unset;
break; break;
case GSM48_CMODE_SPEECH_AMR: case GSM48_CMODE_SPEECH_AMR:
lch_par->tch.tchPlType = GsmL1_TchPlType_Amr;
set_payload_format(lch_par);
lch_par->tch.amrCmiPhase = GsmL1_AmrCmiPhase_Odd; /* FIXME? */ lch_par->tch.amrCmiPhase = GsmL1_AmrCmiPhase_Odd; /* FIXME? */
if (lchan->mr_conf.icmi) lch_par->tch.amrInitCodecMode = amr_get_initial_mode(lchan);
lch_par->tch.amrInitCodecMode = lchan->mr_conf.smod;
/* else: FIXME (implicit rule by TS 05.09 ?!?) */
/* initialize to clean state */ /* initialize to clean state */
for (j = 0; j < ARRAY_SIZE(lch_par->tch.amrActiveCodecSet); j++) for (j = 0; j < ARRAY_SIZE(lch_par->tch.amrActiveCodecSet); j++)
@@ -531,6 +628,13 @@ static void lchan2lch_par(GsmL1_LogChParam_t *lch_par, struct gsm_lchan *lchan)
if (lchan->mr_conf.m12_2) if (lchan->mr_conf.m12_2)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_12_2; lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_12_2;
break; break;
case GSM48_CMODE_DATA_14k5:
case GSM48_CMODE_DATA_12k0:
case GSM48_CMODE_DATA_6k0:
case GSM48_CMODE_DATA_3k6:
LOGP(DL1C, LOGL_ERROR, "%s: CSD not supported!\n",
gsm_lchan_name(lchan));
break;
} }
} }
@@ -571,23 +675,45 @@ int lchan_activate(struct gsm_lchan *lchan)
case GsmL1_Sapi_Sacch: case GsmL1_Sapi_Sacch:
/* Only if we use manual MS power control */ /* Only if we use manual MS power control */
//act_req->logChPrm.sacch.u8MsPowerLevel = FIXME; //act_req->logChPrm.sacch.u8MsPowerLevel = FIXME;
/* enable bad frame indication from >= -100dBm on SACCH */
act_req->fBFILevel = -100.0;
break; break;
case GsmL1_Sapi_TchH: case GsmL1_Sapi_TchH:
case GsmL1_Sapi_TchF: case GsmL1_Sapi_TchF:
lchan2lch_par(lch_par, lchan); lchan2lch_par(lch_par, lchan);
break; break;
case GsmL1_Sapi_Ptcch:
lch_par->ptcch.u8Bsic = lchan->ts->trx->bts->bsic;
break;
case GsmL1_Sapi_Prach:
lch_par->prach.u8Bsic = lchan->ts->trx->bts->bsic;
break;
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
/* Be sure that every packet is received, even if it
* fails. In this case the length might be lower or 0.
*/
act_req->fBFILevel = -200.0;
break;
default: default:
break; break;
} }
LOGP(DL1C, LOGL_INFO, "%s MPH-ACTIVATE.req (hL2=0x%08x)\n", LOGP(DL1C, LOGL_INFO, "%s MPH-ACTIVATE.req (hL2=0x%08x, %s ",
gsm_lchan_name(lchan), act_req->hLayer2); gsm_lchan_name(lchan), act_req->hLayer2,
get_value_string(femtobts_l1sapi_names, act_req->sapi));
LOGPC(DL1C, LOGL_INFO, "%s)\n",
get_value_string(femtobts_dir_names, act_req->dir));
/* send the primitive for all GsmL1_Sapi_* that match the LCHAN */ /* send the primitive for all GsmL1_Sapi_* that match the LCHAN */
l1if_req_compl(fl1h, msg, 0, lchan_act_compl_cb, lchan); l1if_req_compl(fl1h, msg, 0, lchan_act_compl_cb, lchan);
} }
lchan->state = LCHAN_S_ACT_REQ; lchan_set_state(lchan, LCHAN_S_ACT_REQ);
/* set the initial ciphering parameters for both directions */
l1if_set_ciphering(fl1h, lchan, 0);
l1if_set_ciphering(fl1h, lchan, 1);
lchan_init_lapdm(lchan); lchan_init_lapdm(lchan);
@@ -629,6 +755,8 @@ static void dump_lch_par(int logl, GsmL1_LogChParam_t *lch_par, GsmL1_Sapi_t sap
} }
break; break;
/* FIXME: PRACH / PTCCH */ /* FIXME: PRACH / PTCCH */
default:
break;
} }
LOGPC(DL1C, logl, ")\n"); LOGPC(DL1C, logl, ")\n");
} }
@@ -649,6 +777,29 @@ static int chmod_modif_compl_cb(struct msgb *l1_msg, void *data)
&cc->cfgParams.setLogChParams.logChParams, &cc->cfgParams.setLogChParams.logChParams,
cc->cfgParams.setLogChParams.sapi); cc->cfgParams.setLogChParams.sapi);
break; break;
case GsmL1_ConfigParamId_SetTxPowerLevel:
LOGPC(DL1C, LOGL_INFO, "setTxPower %f dBm\n",
cc->cfgParams.setTxPowerLevel.fTxPowerLevel);
break;
case GsmL1_ConfigParamId_SetCipheringParams:
switch (lchan->ciph_state) {
case LCHAN_CIPH_RX_REQ:
LOGPC(DL1C, LOGL_INFO, "RX_REQ -> RX_CONF\n");
lchan->ciph_state = LCHAN_CIPH_RX_CONF;
break;
case LCHAN_CIPH_TXRX_REQ:
LOGPC(DL1C, LOGL_INFO, "TX_REQ -> TX_CONF\n");
lchan->ciph_state = LCHAN_CIPH_TXRX_CONF;
break;
default:
LOGPC(DL1C, LOGL_INFO, "unhandled state %u\n", lchan->ciph_state);
break;
}
break;
case GsmL1_ConfigParamId_SetNbTsc:
default:
LOGPC(DL1C, LOGL_INFO, "\n");
break;
} }
msgb_free(l1_msg); msgb_free(l1_msg);
@@ -683,7 +834,7 @@ static int tx_confreq_logchpar(struct gsm_lchan *lchan, uint8_t direction)
gsm_lchan_name(lchan), gsm_lchan_name(lchan),
get_value_string(femtobts_l1sapi_names, get_value_string(femtobts_l1sapi_names,
conf_req->cfgParams.setLogChParams.sapi)); conf_req->cfgParams.setLogChParams.sapi));
LOGP(DL1C, LOGL_INFO, "cfgParams Tn=%u, subCh=%u, dir=0x%x ", LOGPC(DL1C, LOGL_INFO, "cfgParams Tn=%u, subCh=%u, dir=0x%x ",
conf_req->cfgParams.setLogChParams.u8Tn, conf_req->cfgParams.setLogChParams.u8Tn,
conf_req->cfgParams.setLogChParams.subCh, conf_req->cfgParams.setLogChParams.subCh,
conf_req->cfgParams.setLogChParams.dir); conf_req->cfgParams.setLogChParams.dir);
@@ -694,10 +845,63 @@ static int tx_confreq_logchpar(struct gsm_lchan *lchan, uint8_t direction)
return l1if_req_compl(fl1h, msg, 0, chmod_modif_compl_cb, lchan); return l1if_req_compl(fl1h, msg, 0, chmod_modif_compl_cb, lchan);
} }
int l1if_set_txpower(struct femtol1_hdl *fl1h, float tx_power)
{
struct msgb *msg = l1p_msgb_alloc();
GsmL1_MphConfigReq_t *conf_req;
conf_req = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphConfigReq, fl1h);
conf_req->cfgParamId = GsmL1_ConfigParamId_SetTxPowerLevel;
conf_req->cfgParams.setTxPowerLevel.fTxPowerLevel = tx_power;
return l1if_req_compl(fl1h, msg, 0, NULL, NULL);
}
const enum GsmL1_CipherId_t rsl2l1_ciph[] = {
[0] = GsmL1_CipherId_A50,
[1] = GsmL1_CipherId_A50,
[2] = GsmL1_CipherId_A51,
[3] = GsmL1_CipherId_A52,
[4] = GsmL1_CipherId_A53,
};
int l1if_set_ciphering(struct femtol1_hdl *fl1h,
struct gsm_lchan *lchan,
int dir_downlink)
{
struct msgb *msg = l1p_msgb_alloc();
struct GsmL1_MphConfigReq_t *cfgr;
cfgr = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphConfigReq, fl1h);
cfgr->cfgParamId = GsmL1_ConfigParamId_SetCipheringParams;
cfgr->cfgParams.setCipheringParams.u8Tn = lchan->ts->nr;
cfgr->cfgParams.setCipheringParams.subCh = lchan_to_GsmL1_SubCh_t(lchan);
if (dir_downlink)
cfgr->cfgParams.setCipheringParams.dir = GsmL1_Dir_TxDownlink;
else
cfgr->cfgParams.setCipheringParams.dir = GsmL1_Dir_RxUplink;
if (lchan->encr.alg_id >= ARRAY_SIZE(rsl2l1_ciph))
return -EINVAL;
cfgr->cfgParams.setCipheringParams.cipherId = rsl2l1_ciph[lchan->encr.alg_id];
LOGP(DL1C, LOGL_NOTICE, "%s SET_CIPHERING (ALG=%u %s)\n",
gsm_lchan_name(lchan),
cfgr->cfgParams.setCipheringParams.cipherId,
get_value_string(femtobts_dir_names,
cfgr->cfgParams.setCipheringParams.dir));
memcpy(cfgr->cfgParams.setCipheringParams.u8Kc,
lchan->encr.key, lchan->encr.key_len);
return l1if_req_compl(fl1h, msg, 0, chmod_modif_compl_cb, lchan);
}
int bts_model_rsl_mode_modify(struct gsm_lchan *lchan) int bts_model_rsl_mode_modify(struct gsm_lchan *lchan)
{ {
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
/* channel mode, encryption and/or multirate have changed */ /* channel mode, encryption and/or multirate have changed */
/* update multi-rate config */ /* update multi-rate config */
@@ -715,25 +919,29 @@ static int lchan_deact_compl_cb(struct msgb *l1_msg, void *data)
GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg); GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
GsmL1_MphDeactivateCnf_t *ic = &l1p->u.mphDeactivateCnf; GsmL1_MphDeactivateCnf_t *ic = &l1p->u.mphDeactivateCnf;
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.conf (%s)\n", LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.conf (%s ",
gsm_lchan_name(lchan), gsm_lchan_name(lchan),
get_value_string(femtobts_l1sapi_names, ic->sapi)); get_value_string(femtobts_l1sapi_names, ic->sapi));
LOGPC(DL1C, LOGL_INFO, "%s)\n",
get_value_string(femtobts_dir_names, ic->dir));
if (ic->status == GsmL1_Status_Success) { if (ic->status == GsmL1_Status_Success) {
DEBUGP(DL1C, "Successful deactivation of L1 SAPI %s on TS %u\n", DEBUGP(DL1C, "Successful deactivation of L1 SAPI %s on TS %u\n",
get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn); get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn);
lchan->state = LCHAN_S_ACTIVE; lchan_set_state(lchan, LCHAN_S_NONE);
} else { } else {
LOGP(DL1C, LOGL_ERROR, "Error deactivating L1 SAPI %s on TS %u: %s\n", LOGP(DL1C, LOGL_ERROR, "Error deactivating L1 SAPI %s on TS %u: %s\n",
get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn, get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn,
get_value_string(femtobts_l1status_names, ic->status)); get_value_string(femtobts_l1status_names, ic->status));
lchan->state = LCHAN_S_NONE; lchan_set_state(lchan, LCHAN_S_REL_ERR);
} }
switch (ic->sapi) { switch (ic->sapi) {
case GsmL1_Sapi_Sdcch: case GsmL1_Sapi_Sdcch:
case GsmL1_Sapi_TchF: case GsmL1_Sapi_TchF:
/* FIXME: Send RSL CHAN REL ACK */ case GsmL1_Sapi_TchH:
if (ic->dir == GsmL1_Dir_TxDownlink)
rsl_tx_rf_rel_ack(lchan);
break; break;
default: default:
break; break;
@@ -751,18 +959,29 @@ int lchan_deactivate(struct gsm_lchan *lchan)
int i; int i;
for (i = s4l->num_sapis-1; i >= 0; i--) { for (i = s4l->num_sapis-1; i >= 0; i--) {
struct msgb *msg = l1p_msgb_alloc(); struct msgb *msg;
GsmL1_MphDeactivateReq_t *deact_req; GsmL1_MphDeactivateReq_t *deact_req;
if (s4l->sapis[i].sapi == GsmL1_Sapi_Sacch && lchan->sacch_deact) {
LOGP(DL1C, LOGL_INFO, "%s SACCH already deactivated.\n",
gsm_lchan_name(lchan));
continue;
}
msg = l1p_msgb_alloc();
deact_req = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphDeactivateReq, fl1h); deact_req = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphDeactivateReq, fl1h);
deact_req->u8Tn = lchan->ts->nr; deact_req->u8Tn = lchan->ts->nr;
deact_req->subCh = lchan_to_GsmL1_SubCh_t(lchan); deact_req->subCh = lchan_to_GsmL1_SubCh_t(lchan);
deact_req->dir = s4l->sapis[i].dir; deact_req->dir = s4l->sapis[i].dir;
deact_req->sapi = s4l->sapis[i].sapi; deact_req->sapi = s4l->sapis[i].sapi;
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.req (%s)\n", LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.req (%s ",
gsm_lchan_name(lchan), gsm_lchan_name(lchan),
get_value_string(femtobts_l1sapi_names, deact_req->sapi)); get_value_string(femtobts_l1sapi_names, deact_req->sapi));
LOGPC(DL1C, LOGL_INFO, "%s)\n",
get_value_string(femtobts_dir_names, deact_req->dir));
/* Stop the alive timer once we deactivate the SCH */ /* Stop the alive timer once we deactivate the SCH */
if (deact_req->sapi == GsmL1_Sapi_Sch) if (deact_req->sapi == GsmL1_Sapi_Sch)
@@ -772,12 +991,13 @@ int lchan_deactivate(struct gsm_lchan *lchan)
l1if_req_compl(fl1h, msg, 0, lchan_deact_compl_cb, lchan); l1if_req_compl(fl1h, msg, 0, lchan_deact_compl_cb, lchan);
} }
lchan->state = LCHAN_S_ACT_REQ; lchan_set_state(lchan, LCHAN_S_REL_REQ);
lchan->ciph_state = 0; /* FIXME: do this in common/\*.c */
return 0; return 0;
} }
int lchan_deactivate_sacch(struct gsm_lchan *lchan) static int lchan_deactivate_sacch(struct gsm_lchan *lchan)
{ {
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx); struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
struct msgb *msg = l1p_msgb_alloc(); struct msgb *msg = l1p_msgb_alloc();
@@ -789,8 +1009,11 @@ int lchan_deactivate_sacch(struct gsm_lchan *lchan)
deact_req->dir = DIR_BOTH; deact_req->dir = DIR_BOTH;
deact_req->sapi = GsmL1_Sapi_Sacch; deact_req->sapi = GsmL1_Sapi_Sacch;
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.req (SACCH)\n", lchan->sacch_deact = 1;
gsm_lchan_name(lchan));
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.req (SACCH %s)\n",
gsm_lchan_name(lchan),
get_value_string(femtobts_dir_names, deact_req->dir));
/* send the primitive for all GsmL1_Sapi_* that match the LCHAN */ /* send the primitive for all GsmL1_Sapi_* that match the LCHAN */
return l1if_req_compl(fl1h, msg, 0, lchan_deact_compl_cb, lchan); return l1if_req_compl(fl1h, msg, 0, lchan_deact_compl_cb, lchan);
@@ -837,6 +1060,9 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
case NM_OC_BTS: case NM_OC_BTS:
case NM_OC_SITE_MANAGER: case NM_OC_SITE_MANAGER:
case NM_OC_BASEB_TRANSC: case NM_OC_BASEB_TRANSC:
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1); oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
rc = oml_mo_opstart_ack(mo); rc = oml_mo_opstart_ack(mo);
break; break;
@@ -851,5 +1077,36 @@ int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
{ {
/* blindly accept all state changes */ /* blindly accept all state changes */
mo->nm_state.administrative = adm_state; mo->nm_state.administrative = adm_state;
return oml_mo_fom_ack_nack(mo, NM_MT_CHG_ADM_STATE, 0); return oml_mo_statechg_ack(mo);
}
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->sacch_deact = 0;
lchan_activate(lchan);
return 0;
}
int bts_model_rsl_chan_rel(struct gsm_lchan *lchan)
{
/* A duplicate RF Release Request, ignore it */
if (lchan->state == LCHAN_S_REL_REQ)
return 0;
lchan_deactivate(lchan);
return 0;
}
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);
trx_close(trx);
return l1if_activate_rf(fl1, 0);
} }

View File

@@ -1,6 +1,7 @@
/* VTY interface for sysmoBTS */ /* VTY interface for sysmoBTS */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org> /* (C) 2011 by Harald Welte <laforge@gnumonks.org>
* (C) 2012 by Holger Hans Peter Freyther
* *
* All Rights Reserved * All Rights Reserved
* *
@@ -23,6 +24,7 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <stdint.h> #include <stdint.h>
#include <ctype.h>
#include <arpa/inet.h> #include <arpa/inet.h>
@@ -35,68 +37,155 @@
#include <osmocom/vty/vty.h> #include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h> #include <osmocom/vty/command.h>
#include <osmocom/vty/misc.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/vty.h>
#include "femtobts.h" #include "femtobts.h"
#include "l1_if.h" #include "l1_if.h"
#define TRX_STR "Transceiver related commands\n" "TRX number\n"
#define SHOW_TRX_STR \
SHOW_STR \
TRX_STR
#define DSP_TRACE_F_STR "DSP Trace Flag\n"
static struct gsm_bts *vty_bts; static struct gsm_bts *vty_bts;
/* This generates the logging command string for VTY. */ /* configuration */
const char *vty_cmd_string_from_valstr(const struct value_string *vals,
const char *prefix) DEFUN(cfg_trx_gsmtap_sapi, cfg_trx_gsmtap_sapi_cmd,
"HIDDEN", "HIDDEN")
{ {
int len = 0, offset = 0, ret, i, rem; struct gsm_bts_trx *trx = vty->index;
int size = strlen(prefix); struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
const struct value_string *vs; int sapi;
char *str;
for (vs = vals; vs->value || vs->str; vs++) sapi = get_string_value(femtobts_l1sapi_names, argv[0]);
size += strlen(vs->str) + 1;
rem = size; fl1h->gsmtap_sapi_mask |= (1 << sapi);
str = talloc_zero_size(vty_bts, size);
if (!str)
return NULL;
ret = snprintf(str + offset, rem, prefix); return CMD_SUCCESS;
if (ret < 0)
goto err;
OSMO_SNPRINTF_RET(ret, rem, offset, len);
for (vs = vals; vs->value || vs->str; vs++) {
if (vs->str) {
int j, name_len = strlen(vs->str)+1;
char name[name_len];
for (j = 0; j < name_len; j++)
name[j] = tolower(vs->str[j]);
name[name_len-1] = '\0';
ret = snprintf(str + offset, rem, "%s|", name);
if (ret < 0)
goto err;
OSMO_SNPRINTF_RET(ret, rem, offset, len);
}
}
offset--; /* to remove the trailing | */
rem++;
ret = snprintf(str + offset, rem, ")");
if (ret < 0)
goto err;
OSMO_SNPRINTF_RET(ret, rem, offset, len);
err:
str[size-1] = '\0';
return str;
} }
DEFUN(cfg_trx_no_gsmtap_sapi, cfg_trx_no_gsmtap_sapi_cmd,
"HIDDEN", "HIDDEN")
{
struct gsm_bts_trx *trx = vty->index;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
int sapi;
sapi = get_string_value(femtobts_l1sapi_names, argv[0]);
fl1h->gsmtap_sapi_mask &= ~(1 << sapi);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_clkcal_def, cfg_trx_clkcal_def_cmd,
"clock-calibration default",
"Set the clock calibration value\n" "Default Clock DAC value\n")
{
struct gsm_bts_trx *trx = vty->index;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
fl1h->clk_cal = 0xffff;
return CMD_SUCCESS;
}
#ifdef HW_SYSMOBTS_V1
DEFUN(cfg_trx_clkcal, cfg_trx_clkcal_cmd,
"clock-calibration <0-4095>",
"Set the clock calibration value\n" "Clock DAC value\n")
{
unsigned int clkcal = atoi(argv[0]);
struct gsm_bts_trx *trx = vty->index;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
fl1h->clk_cal = clkcal & 0xfff;
return CMD_SUCCESS;
}
#else
DEFUN(cfg_trx_clkcal, cfg_trx_clkcal_cmd,
"clock-calibration <-4095-4095>",
"Set the clock calibration value\n" "Offset in PPB\n")
{
int clkcal = atoi(argv[0]);
struct gsm_bts_trx *trx = vty->index;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
fl1h->clk_cal = clkcal;
return CMD_SUCCESS;
}
#endif
DEFUN(cfg_trx_clksrc, cfg_trx_clksrc_cmd,
"clock-source (tcxo|ocxo|ext|gps)",
"Set the clock source value\n"
"Use the TCXO\n"
"Use the OCXO\n"
"Use an external clock\n"
"Use the GPS pps\n")
{
struct gsm_bts_trx *trx = vty->index;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
int rc;
rc = get_string_value(femtobts_clksrc_names, argv[0]);
if (rc < 0)
return CMD_WARNING;
fl1h->clk_src = rc;
return CMD_SUCCESS;
}
DEFUN(cfg_trx_cal_path, cfg_trx_cal_path_cmd,
"trx-calibration-path PATH",
"Set the path name to TRX calibration data\n" "Path name\n")
{
struct gsm_bts_trx *trx = vty->index;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
if (fl1h->calib_path)
talloc_free(fl1h->calib_path);
fl1h->calib_path = talloc_strdup(fl1h, argv[0]);
return CMD_SUCCESS;
}
/* runtime */
DEFUN(show_trx_clksrc, show_trx_clksrc_cmd,
"show trx <0-0> clock-source",
SHOW_TRX_STR "Display the clock source for this TRX")
{
int trx_nr = atoi(argv[0]);
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
struct femtol1_hdl *fl1h;
if (!trx)
return CMD_WARNING;
fl1h = trx_femtol1_hdl(trx);
vty_out(vty, "TRX Clock Source: %s%s",
get_value_string(femtobts_clksrc_names, fl1h->clk_src),
VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN(show_dsp_trace_f, show_dsp_trace_f_cmd, DEFUN(show_dsp_trace_f, show_dsp_trace_f_cmd,
"show trx <0-0> dsp-trace-flags", "show trx <0-0> dsp-trace-flags",
SHOW_STR "Display the current setting of the DSP trace flags") SHOW_TRX_STR "Display the current setting of the DSP trace flags")
{ {
int trx_nr = atoi(argv[0]); int trx_nr = atoi(argv[0]);
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr); struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
@@ -130,7 +219,7 @@ DEFUN(show_dsp_trace_f, show_dsp_trace_f_cmd,
} }
DEFUN(dsp_trace_f, dsp_trace_f_cmd, "HIDDEN", "HIDDEN") DEFUN(dsp_trace_f, dsp_trace_f_cmd, "HIDDEN", TRX_STR)
{ {
int trx_nr = atoi(argv[0]); int trx_nr = atoi(argv[0]);
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr); struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
@@ -150,7 +239,7 @@ DEFUN(dsp_trace_f, dsp_trace_f_cmd, "HIDDEN", "HIDDEN")
return CMD_SUCCESS; return CMD_SUCCESS;
} }
DEFUN(no_dsp_trace_f, no_dsp_trace_f_cmd, "HIDDEN", "HIDDEN") DEFUN(no_dsp_trace_f, no_dsp_trace_f_cmd, "HIDDEN", NO_STR TRX_STR)
{ {
int trx_nr = atoi(argv[0]); int trx_nr = atoi(argv[0]);
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr); struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
@@ -170,20 +259,222 @@ DEFUN(no_dsp_trace_f, no_dsp_trace_f_cmd, "HIDDEN", "HIDDEN")
return CMD_SUCCESS; return CMD_SUCCESS;
} }
DEFUN(show_sys_info, show_sys_info_cmd,
"show trx <0-0> system-information",
SHOW_TRX_STR "Display information about system\n")
{
int trx_nr = atoi(argv[0]);
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
struct femtol1_hdl *fl1h;
int i;
int femtol1_vty_init(struct gsm_bts *bts) if (!trx) {
vty_out(vty, "Cannot find TRX number %u%s",
trx_nr, VTY_NEWLINE);
return CMD_WARNING;
}
fl1h = trx_femtol1_hdl(trx);
vty_out(vty, "DSP Version: %u.%u.%u, FPGA Version: %u.%u.%u%s",
fl1h->hw_info.dsp_version[0],
fl1h->hw_info.dsp_version[1],
fl1h->hw_info.dsp_version[2],
fl1h->hw_info.fpga_version[0],
fl1h->hw_info.fpga_version[1],
fl1h->hw_info.fpga_version[2], VTY_NEWLINE);
vty_out(vty, "GSM Band Support: ");
for (i = 0; i < sizeof(fl1h->hw_info.band_support); i++) {
if (fl1h->hw_info.band_support & (1 << i))
vty_out(vty, "%s ", gsm_band_name(1 << i));
}
vty_out(vty, "%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN(activate_lchan, activate_lchan_cmd,
"trx <0-0> <0-7> (activate|deactivate) <0-7>",
TRX_STR
"Timeslot number\n"
"Activate Logical Channel\n"
"Deactivate Logical Channel\n"
"Logical Channel Number\n" )
{
int trx_nr = atoi(argv[0]);
int ts_nr = atoi(argv[1]);
int lchan_nr = atoi(argv[3]);
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
struct gsm_lchan *lchan = &ts->lchan[lchan_nr];
if (!strcmp(argv[2], "activate"))
lchan_activate(lchan);
else
lchan_deactivate(lchan);
return CMD_SUCCESS;
}
DEFUN(set_tx_power, set_tx_power_cmd,
"trx <0-0> tx-power <-110-23>",
TRX_STR
"Set transmit power (override BSC)\n"
"Transmit power in dBm\n")
{
int trx_nr = atoi(argv[0]);
int power = atoi(argv[1]);
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
l1if_set_txpower(fl1h, (float) power);
return CMD_SUCCESS;
}
DEFUN(loopback, loopback_cmd,
"trx <0-0> <0-7> loopback <0-1>",
TRX_STR
"Timeslot number\n"
"Set TCH loopback\n"
"Logical Channel Number\n")
{
int trx_nr = atoi(argv[0]);
int ts_nr = atoi(argv[1]);
int lchan_nr = atoi(argv[2]);
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
struct gsm_lchan *lchan = &ts->lchan[lchan_nr];
lchan->loopback = 1;
return CMD_SUCCESS;
}
DEFUN(no_loopback, no_loopback_cmd,
"no trx <0-0> <0-7> loopback <0-1>",
NO_STR TRX_STR
"Timeslot number\n"
"Set TCH loopback\n"
"Logical Channel Number\n")
{
int trx_nr = atoi(argv[0]);
int ts_nr = atoi(argv[1]);
int lchan_nr = atoi(argv[2]);
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
struct gsm_lchan *lchan = &ts->lchan[lchan_nr];
lchan->loopback = 0;
return CMD_SUCCESS;
}
void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts)
{
}
/* FIXME: move to libosmocore ? */
static char buf_casecnvt[256];
char *osmo_str_tolower(const char *in)
{
int len, i;
if (!in)
return NULL;
len = strlen(in);
if (len > sizeof(buf_casecnvt))
len = sizeof(buf_casecnvt);
for (i = 0; i < len; i++) {
buf_casecnvt[i] = tolower(in[i]);
if (in[i] == '\0')
break;
}
if (i < sizeof(buf_casecnvt))
buf_casecnvt[i] = '\0';
/* just to make sure we're always zero-terminated */
buf_casecnvt[sizeof(buf_casecnvt)-1] = '\0';
return buf_casecnvt;
}
void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
int i;
vty_out(vty, " clock-calibration %d%s", fl1h->clk_cal,
VTY_NEWLINE);
vty_out(vty, " trx-calibration-path %s%s", fl1h->calib_path,
VTY_NEWLINE);
vty_out(vty, " clock-source %s%s",
get_value_string(femtobts_clksrc_names, fl1h->clk_src),
VTY_NEWLINE);
for (i = 0; i < 32; i++) {
if (fl1h->gsmtap_sapi_mask & (1 << i)) {
const char *name = get_value_string(femtobts_l1sapi_names, i);
vty_out(vty, " gsmtap-sapi %s%s", osmo_str_tolower(name),
VTY_NEWLINE);
}
}
}
int bts_model_vty_init(struct gsm_bts *bts)
{ {
vty_bts = bts; vty_bts = bts;
/* runtime-patch the command strings with debug levels */ /* runtime-patch the command strings with debug levels */
dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(femtobts_tracef_names, dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_tracef_names,
"trx <0-0> dsp-trace-flag ("); "trx <0-0> dsp-trace-flag (",
no_dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(femtobts_tracef_names, "|",")", VTY_DO_LOWER);
"no trx <0-0> dsp-trace-flag ("); dsp_trace_f_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_tracef_docs,
TRX_STR DSP_TRACE_F_STR,
"\n", "", 0);
no_dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_tracef_names,
"no trx <0-0> dsp-trace-flag (",
"|",")", VTY_DO_LOWER);
no_dsp_trace_f_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_tracef_docs,
NO_STR TRX_STR DSP_TRACE_F_STR,
"\n", "", 0);
cfg_trx_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
"gsmtap-sapi (",
"|",")", VTY_DO_LOWER);
cfg_trx_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
"GSMTAP SAPI\n",
"\n", "", 0);
cfg_trx_no_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
"no gsmtap-sapi (",
"|",")", VTY_DO_LOWER);
cfg_trx_no_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
NO_STR "GSMTAP SAPI\n",
"\n", "", 0);
install_element_ve(&show_dsp_trace_f_cmd); install_element_ve(&show_dsp_trace_f_cmd);
install_element_ve(&show_sys_info_cmd);
install_element_ve(&show_trx_clksrc_cmd);
install_element_ve(&dsp_trace_f_cmd); install_element_ve(&dsp_trace_f_cmd);
install_element_ve(&no_dsp_trace_f_cmd); install_element_ve(&no_dsp_trace_f_cmd);
install_element(ENABLE_NODE, &activate_lchan_cmd);
install_element(ENABLE_NODE, &set_tx_power_cmd);
install_element(ENABLE_NODE, &loopback_cmd);
install_element(ENABLE_NODE, &no_loopback_cmd);
install_element(TRX_NODE, &cfg_trx_clkcal_cmd);
install_element(TRX_NODE, &cfg_trx_clkcal_def_cmd);
install_element(TRX_NODE, &cfg_trx_clksrc_cmd);
install_element(TRX_NODE, &cfg_trx_cal_path_cmd);
install_element(TRX_NODE, &cfg_trx_gsmtap_sapi_cmd);
install_element(TRX_NODE, &cfg_trx_no_gsmtap_sapi_cmd);
return 0; return 0;
} }

View File

@@ -1,6 +1,6 @@
/* Traffic channel support for Sysmocom BTS L1 */ /* Traffic channel support for Sysmocom BTS L1 */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org> /* (C) 2011-2012 by Harald Welte <laforge@gnumonks.org>
* *
* All Rights Reserved * All Rights Reserved
* *
@@ -40,7 +40,7 @@
#include <osmo-bts/gsm_data.h> #include <osmo-bts/gsm_data.h>
#include <osmo-bts/measurement.h> #include <osmo-bts/measurement.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>
@@ -97,9 +97,18 @@ void osmo_nibble_shift_left_unal(uint8_t *out, const uint8_t *in,
static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len) static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len)
{ {
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP"); struct msgb *msg;
uint8_t *cur; uint8_t *cur;
msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
if (!msg)
return NULL;
#ifdef USE_L1_RTP_MODE
/* new L1 can deliver bits like we need them */
cur = msgb_put(msg, GSM_FR_BYTES);
memcpy(cur, l1_payload, GSM_FR_BYTES);
#else
/* step1: reverse the bit-order of each payload byte */ /* step1: reverse the bit-order of each payload byte */
osmo_revbytebits_buf(l1_payload, payload_len); osmo_revbytebits_buf(l1_payload, payload_len);
@@ -109,6 +118,7 @@ static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len
osmo_nibble_shift_right(cur, l1_payload, GSM_FR_BITS/4); osmo_nibble_shift_right(cur, l1_payload, GSM_FR_BITS/4);
cur[0] |= 0xD0; cur[0] |= 0xD0;
#endif /* USE_L1_RTP_MODE */
return msg; return msg;
} }
@@ -119,24 +129,37 @@ static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len
* \param[in] payload_len length of \a rtp_payload * \param[in] payload_len length of \a rtp_payload
* \returns number of \a l1_payload bytes filled * \returns number of \a l1_payload bytes filled
*/ */
static int rtppayload_to_l1_fr(uint8_t *l1_payload, uint8_t *rtp_payload, static int rtppayload_to_l1_fr(uint8_t *l1_payload, const uint8_t *rtp_payload,
unsigned int payload_len) unsigned int payload_len)
{ {
#ifdef USE_L1_RTP_MODE
/* new L1 can deliver bits like we need them */
memcpy(l1_payload, rtp_payload, GSM_FR_BYTES);
#else
/* step2: we need to shift the RTP payload left by one nibble*/ /* step2: we need to shift the RTP payload left by one nibble*/
osmo_nibble_shift_left_unal(l1_payload, rtp_payload, GSM_FR_BITS/4); osmo_nibble_shift_left_unal(l1_payload, rtp_payload, GSM_FR_BITS/4);
/* step1: reverse the bit-order of each payload byte */ /* step1: reverse the bit-order of each payload byte */
osmo_revbytebits_buf(l1_payload, payload_len); osmo_revbytebits_buf(l1_payload, payload_len);
#endif /* USE_L1_RTP_MODE */
return GSM_FR_BYTES; return GSM_FR_BYTES;
} }
#ifdef GsmL1_TchPlType_Efr #if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload, uint8_t payload_len) static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload, uint8_t payload_len)
{ {
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP"); struct msgb *msg;
uint8_t *cur; uint8_t *cur;
msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
if (!msg)
return NULL;
#ifdef USE_L1_RTP_MODE
/* new L1 can deliver bits like we need them */
cur = msgb_put(msg, GSM_EFR_BYTES);
memcpy(cur, l1_payload, GSM_EFR_BYTES);
#else
/* step1: reverse the bit-order of each payload byte */ /* step1: reverse the bit-order of each payload byte */
osmo_revbytebits_buf(l1_payload, payload_len); osmo_revbytebits_buf(l1_payload, payload_len);
@@ -146,18 +169,34 @@ static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload, uint8_t payload_le
osmo_nibble_shift_right(cur, l1_payload, GSM_EFR_BITS/4); osmo_nibble_shift_right(cur, l1_payload, GSM_EFR_BITS/4);
cur[0] |= 0xC0; cur[0] |= 0xC0;
#endif /* USE_L1_RTP_MODE */
return msg; return msg;
} }
static int rtppayload_to_l1_efr(uint8_t *l1_payload, const uint8_t *rtp_payload,
unsigned int payload_len)
{
#ifndef USE_L1_RTP_MODE
#error We don't support EFR with L1 that doesn't support RTP mode!
#else
memcpy(l1_payload, rtp_payload, payload_len);
return payload_len;
#endif
}
#else #else
#warning No EFR support in L1 #warning No EFR support in L1
#endif #endif /* L1_HAS_EFR */
static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len) static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len)
{ {
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP"); struct msgb *msg;
uint8_t *cur; uint8_t *cur;
msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
if (!msg)
return NULL;
if (payload_len != GSM_HR_BYTES) { if (payload_len != GSM_HR_BYTES) {
LOGP(DL1C, LOGL_ERROR, "L1 HR frame length %u != expected %u\n", LOGP(DL1C, LOGL_ERROR, "L1 HR frame length %u != expected %u\n",
payload_len, GSM_HR_BYTES); payload_len, GSM_HR_BYTES);
@@ -167,8 +206,10 @@ static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len
cur = msgb_put(msg, GSM_HR_BYTES); cur = msgb_put(msg, GSM_HR_BYTES);
memcpy(cur, l1_payload, GSM_HR_BYTES); memcpy(cur, l1_payload, GSM_HR_BYTES);
#ifndef USE_L1_RTP_MODE
/* reverse the bit-order of each payload byte */ /* reverse the bit-order of each payload byte */
osmo_revbytebits_buf(cur, GSM_HR_BYTES); osmo_revbytebits_buf(cur, GSM_HR_BYTES);
#endif /* USE_L1_RTP_MODE */
return msg; return msg;
} }
@@ -179,7 +220,7 @@ static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len
* \param[in] payload_len length of \a rtp_payload * \param[in] payload_len length of \a rtp_payload
* \returns number of \a l1_payload bytes filled * \returns number of \a l1_payload bytes filled
*/ */
static int rtppayload_to_l1_hr(uint8_t *l1_payload, uint8_t *rtp_payload, static int rtppayload_to_l1_hr(uint8_t *l1_payload, const uint8_t *rtp_payload,
unsigned int payload_len) unsigned int payload_len)
{ {
@@ -191,8 +232,10 @@ static int rtppayload_to_l1_hr(uint8_t *l1_payload, uint8_t *rtp_payload,
memcpy(l1_payload, rtp_payload, GSM_HR_BYTES); memcpy(l1_payload, rtp_payload, GSM_HR_BYTES);
#ifndef USE_L1_RTP_MODE
/* reverse the bit-order of each payload byte */ /* reverse the bit-order of each payload byte */
osmo_revbytebits_buf(l1_payload, GSM_HR_BYTES); osmo_revbytebits_buf(l1_payload, GSM_HR_BYTES);
#endif /* USE_L1_RTP_MODE */
return GSM_HR_BYTES; return GSM_HR_BYTES;
} }
@@ -203,14 +246,19 @@ static int rtppayload_to_l1_hr(uint8_t *l1_payload, uint8_t *rtp_payload,
static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_len, static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_len,
struct amr_multirate_conf *amr_mrc) struct amr_multirate_conf *amr_mrc)
{ {
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP"); struct msgb *msg;
uint8_t *cur; uint8_t *cur;
u_int8_t cmr; u_int8_t cmr;
uint8_t ft = l1_payload[2] & 0xF; uint8_t ft = l1_payload[2] & 0xF;
uint8_t cmr_idx = l1_payload[1];
uint8_t amr_if2_len = payload_len - 2; uint8_t amr_if2_len = payload_len - 2;
msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
if (!msg)
return NULL;
#if 0 #if 0
uint8_t cmr_idx = l1_payload[1];
/* CMR == Unset means CMR was not transmitted at this TDMA */ /* CMR == Unset means CMR was not transmitted at this TDMA */
if (cmr_idx >= GsmL1_AmrCodecMode_Unset) if (cmr_idx >= GsmL1_AmrCodecMode_Unset)
cmr = AMR_CMR_NONE; cmr = AMR_CMR_NONE;
@@ -225,6 +273,10 @@ static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_le
cmr = AMR_CMR_NONE; cmr = AMR_CMR_NONE;
#endif #endif
#ifdef USE_L1_RTP_MODE
cur = msgb_put(msg, amr_if2_len);
memcpy(cur, l1_payload+2, amr_if2_len);
#else
/* RFC 3267 4.4.1 Payload Header */ /* RFC 3267 4.4.1 Payload Header */
msgb_put_u8(msg, (cmr << 4)); msgb_put_u8(msg, (cmr << 4));
@@ -239,6 +291,8 @@ static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_le
/* step2: shift everything left by one nibble */ /* step2: shift everything left by one nibble */
osmo_nibble_shift_left_unal(cur, l1_payload+2, amr_if2_len*2 -1); osmo_nibble_shift_left_unal(cur, l1_payload+2, amr_if2_len*2 -1);
#endif /* USE_L1_RTP_MODE */
return msg; return msg;
} }
@@ -262,7 +316,7 @@ int get_amr_mode_idx(const struct amr_multirate_conf *amr_mrc, uint8_t cmi)
* \param[in] payload_len length of \a rtp_payload * \param[in] payload_len length of \a rtp_payload
* \returns number of \a l1_payload bytes filled * \returns number of \a l1_payload bytes filled
*/ */
static int rtppayload_to_l1_amr(uint8_t *l1_payload, uint8_t *rtp_payload, static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
uint8_t payload_len, uint8_t payload_len,
struct gsm_lchan *lchan) struct gsm_lchan *lchan)
{ {
@@ -275,12 +329,19 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, uint8_t *rtp_payload,
uint8_t amr_if2_core_len = payload_len - 2; uint8_t amr_if2_core_len = payload_len - 2;
int rc; int rc;
#ifdef USE_L1_RTP_MODE
memcpy(l1_payload+2, rtp_payload, payload_len);
#else
/* step1: shift everything right one nibble; make space for FT */ /* step1: shift everything right one nibble; make space for FT */
osmo_nibble_shift_right(l1_payload+2, rtp_payload+2, amr_if2_core_len*2); osmo_nibble_shift_right(l1_payload+2, rtp_payload+2, amr_if2_core_len*2);
/* step2: reverse the bit-order within every byte of the IF2 /* step2: reverse the bit-order within every byte of the IF2
* core frame contained in the RTP payload */ * core frame contained in the RTP payload */
osmo_revbytebits_buf(l1_payload+2, amr_if2_core_len+1); osmo_revbytebits_buf(l1_payload+2, amr_if2_core_len+1);
/* lower 4 bit of first FR2 byte contains FT */
l1_payload[2] |= ft;
#endif /* USE_L1_RTP_MODE */
/* CMI in downlink tells the L1 encoder which encoding function /* CMI in downlink tells the L1 encoder which encoding function
* it will use, so we have to use the frame type */ * it will use, so we have to use the frame type */
switch (ft) { switch (ft) {
@@ -341,9 +402,6 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, uint8_t *rtp_payload,
} }
#endif #endif
/* lower 4 bit of first FR2 byte contains FT */
l1_payload[2] |= ft;
if (ft == AMR_FT_SID_AMR) { if (ft == AMR_FT_SID_AMR) {
/* store the last SID frame in lchan context */ /* store the last SID frame in lchan context */
unsigned int copy_len; unsigned int copy_len;
@@ -370,21 +428,38 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, uint8_t *rtp_payload,
* yet, as things like the frame number, etc. are unknown at the time we * yet, as things like the frame number, etc. are unknown at the time we
* pre-fill the primtive. * pre-fill the primtive.
*/ */
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)
{ {
struct gsm_lchan *lchan = rs->priv; struct gsm_lchan *lchan = rs->priv;
struct msgb *msg = l1p_msgb_alloc(); struct msgb *msg;
GsmL1_Prim_t *l1p = msgb_l1prim(msg); GsmL1_Prim_t *l1p;
GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq; GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param = &data_req->msgUnitParam; GsmL1_MsgUnitParam_t *msu_param;
uint8_t *payload_type = &msu_param->u8Buffer[0]; uint8_t *payload_type;
uint8_t *l1_payload = &msu_param->u8Buffer[1]; uint8_t *l1_payload;
int rc; int rc;
DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan), DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan),
osmo_hexdump(rtp_pl, rtp_pl_len)); osmo_hexdump(rtp_pl, rtp_pl_len));
/* skip processing of incoming RTP frames if we are in loopback mode */
if (lchan->loopback)
return;
msg = l1p_msgb_alloc();
if (!msg) {
LOGP(DRTP, LOGL_ERROR, "%s: Failed to allocate Rx payload.\n",
gsm_lchan_name(lchan));
return;
}
l1p = msgb_l1prim(msg);
data_req = &l1p->u.phDataReq;
msu_param = &data_req->msgUnitParam;
payload_type = &msu_param->u8Buffer[0];
l1_payload = &msu_param->u8Buffer[1];
switch (lchan->tch_mode) { switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_V1: case GSM48_CMODE_SPEECH_V1:
if (lchan->type == GSM_LCHAN_TCH_F) { if (lchan->type == GSM_LCHAN_TCH_F) {
@@ -397,10 +472,11 @@ void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, uint8_t *rtp_pl,
rtp_pl, rtp_pl_len); rtp_pl, rtp_pl_len);
} }
break; break;
#ifdef GsmL1_TchPlType_Efr #if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
case GSM48_CMODE_SPEECH_EFR: case GSM48_CMODE_SPEECH_EFR:
*payload_type = GsmL1_TchPlType_Efr; *payload_type = GsmL1_TchPlType_Efr;
rc = FIXME; rc = rtppayload_to_l1_efr(l1_payload, rtp_pl,
rtp_pl_len);
break; break;
#endif #endif
case GSM48_CMODE_SPEECH_AMR: case GSM48_CMODE_SPEECH_AMR:
@@ -466,9 +542,45 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
} }
payload_len = data_ind->msgUnitParam.u8Size - 1; payload_len = data_ind->msgUnitParam.u8Size - 1;
if (lchan->loopback) {
GsmL1_Prim_t *rl1p;
GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param;
struct msgb *tmp;
int count = 0;
/* generate a new msgb from the paylaod */
rmsg = l1p_msgb_alloc();
if (!rmsg)
return -ENOMEM;
rl1p = msgb_l1prim(rmsg);
data_req = &rl1p->u.phDataReq;
msu_param = &data_req->msgUnitParam;
memcpy(msu_param->u8Buffer,
data_ind->msgUnitParam.u8Buffer,
data_ind->msgUnitParam.u8Size);
msu_param->u8Size = data_ind->msgUnitParam.u8Size;
/* make sure the queue doesn't get too long */
llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
count++;
while (count >= 1) {
tmp = msgb_dequeue(&lchan->dl_tch_queue);
msgb_free(tmp);
count--;
}
msgb_enqueue(&lchan->dl_tch_queue, rmsg);
return 0;
}
switch (payload_type) { switch (payload_type) {
case GsmL1_TchPlType_Fr: case GsmL1_TchPlType_Fr:
#ifdef GsmL1_TchPlType_Efr #if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
case GsmL1_TchPlType_Efr: case GsmL1_TchPlType_Efr:
#endif #endif
if (lchan->type != GSM_LCHAN_TCH_F) if (lchan->type != GSM_LCHAN_TCH_F)
@@ -500,8 +612,8 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
case GsmL1_TchPlType_Hr: case GsmL1_TchPlType_Hr:
rmsg = l1_to_rtppayload_hr(payload, payload_len); rmsg = l1_to_rtppayload_hr(payload, payload_len);
break; break;
#ifdef GsmL1_TchPlType_Efr #if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
case GsmL1_TchPlType_Efr case GsmL1_TchPlType_Efr:
rmsg = l1_to_rtppayload_efr(payload, payload_len); rmsg = l1_to_rtppayload_efr(payload, payload_len);
break; break;
#endif #endif
@@ -532,12 +644,22 @@ err_payload_match:
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan) struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan)
{ {
struct msgb *msg = l1p_msgb_alloc(); struct msgb *msg;
GsmL1_Prim_t *l1p = msgb_l1prim(msg); GsmL1_Prim_t *l1p;
GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq; GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param = &data_req->msgUnitParam; GsmL1_MsgUnitParam_t *msu_param;
uint8_t *payload_type = &msu_param->u8Buffer[0]; uint8_t *payload_type;
uint8_t *l1_payload = &msu_param->u8Buffer[1]; uint8_t *l1_payload;
msg = l1p_msgb_alloc();
if (!msg)
return NULL;
l1p = msgb_l1prim(msg);
data_req = &l1p->u.phDataReq;
msu_param = &data_req->msgUnitParam;
payload_type = &msu_param->u8Buffer[0];
l1_payload = &msu_param->u8Buffer[1];
switch (lchan->tch_mode) { switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_AMR: case GSM48_CMODE_SPEECH_AMR:

41
tests/Makefile.am Normal file
View File

@@ -0,0 +1,41 @@
SUBDIRS = paging
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
:;{ \
echo '# Signature of the current package.' && \
echo 'm4_define([AT_PACKAGE_NAME],' && \
echo ' [$(PACKAGE_NAME)])' && \
echo 'm4_define([AT_PACKAGE_TARNAME],' && \
echo ' [$(PACKAGE_TARNAME)])' && \
echo 'm4_define([AT_PACKAGE_VERSION],' && \
echo ' [$(PACKAGE_VERSION)])' && \
echo 'm4_define([AT_PACKAGE_STRING],' && \
echo ' [$(PACKAGE_STRING)])' && \
echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \
echo ' [$(PACKAGE_BUGREPORT)])'; \
echo 'm4_define([AT_PACKAGE_URL],' && \
echo ' [$(PACKAGE_URL)])'; \
} >'$(srcdir)/package.m4'
EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE)
TESTSUITE = $(srcdir)/testsuite
check-local: atconfig $(TESTSUITE)
$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
installcheck-local: atconfig $(TESTSUITE)
$(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \
$(TESTSUITEFLAGS)
clean-local:
test ! -f '$(TESTSUITE)' || \
$(SHELL) '$(TESTSUITE)' --clean
$(RM) -f atconfig
AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te
AUTOTEST = $(AUTOM4TE) --language=autotest
$(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4
$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
mv $@.tmp $@

8
tests/paging/Makefile.am Normal file
View File

@@ -0,0 +1,8 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) -lortp
noinst_PROGRAMS = paging_test
EXTRA_DIST = paging_test.ok
paging_test_SOURCES = paging_test.c
paging_test_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)

164
tests/paging/paging_test.c Normal file
View File

@@ -0,0 +1,164 @@
/* testing the paging code */
/* (C) 2011 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 <osmocom/core/talloc.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/paging.h>
#include <osmo-bts/gsm_data.h>
#include <unistd.h>
static struct gsm_bts *bts;
static struct gsm_bts_role_bts *btsb;
int pcu_direct = 0;
static const uint8_t static_ilv[] = {
0x08, 0x59, 0x51, 0x30, 0x99, 0x00, 0x00, 0x00, 0x19
};
#define ASSERT_TRUE(rc) \
if (!(rc)) { \
printf("Assert failed in %s:%d.\n", \
__FILE__, __LINE__); \
abort(); \
}
static void test_paging_smoke(void)
{
int rc;
uint8_t out_buf[GSM_MACBLOCK_LEN];
struct gsm_time g_time;
printf("Testing that paging messages expire.\n");
/* add paging entry */
rc = paging_add_identity(btsb->paging_state, 0, static_ilv, 0);
ASSERT_TRUE(rc == 0);
ASSERT_TRUE(paging_queue_length(btsb->paging_state) == 1);
/* generate messages */
g_time.fn = 0;
g_time.t1 = 0;
g_time.t2 = 0;
g_time.t3 = 6;
rc = paging_gen_msg(btsb->paging_state, out_buf, &g_time);
ASSERT_TRUE(rc == 13);
ASSERT_TRUE(paging_group_queue_empty(btsb->paging_state, 0));
ASSERT_TRUE(paging_queue_length(btsb->paging_state) == 0);
/*
* TODO: test all the cases of different amount tmsi/imsi and check
* if we fill the slots in a optimal way.
*/
}
static void test_paging_sleep(void)
{
int rc;
uint8_t out_buf[GSM_MACBLOCK_LEN];
struct gsm_time g_time;
printf("Testing that paging messages expire with sleep.\n");
/* add paging entry */
rc = paging_add_identity(btsb->paging_state, 0, static_ilv, 0);
ASSERT_TRUE(rc == 0);
ASSERT_TRUE(paging_queue_length(btsb->paging_state) == 1);
/* sleep */
sleep(1);
/* generate messages */
g_time.fn = 0;
g_time.t1 = 0;
g_time.t2 = 0;
g_time.t3 = 6;
rc = paging_gen_msg(btsb->paging_state, out_buf, &g_time);
ASSERT_TRUE(rc == 13);
ASSERT_TRUE(paging_group_queue_empty(btsb->paging_state, 0));
ASSERT_TRUE(paging_queue_length(btsb->paging_state) == 0);
}
int main(int argc, char **argv)
{
void *tall_msgb_ctx;
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
tall_msgb_ctx = talloc_named_const(tall_bts_ctx, 1, "msgb");
msgb_set_talloc_ctx(tall_msgb_ctx);
bts_log_init(NULL);
bts = gsm_bts_alloc(tall_bts_ctx);
if (bts_init(bts) < 0) {
fprintf(stderr, "unable to to open bts\n");
exit(1);
}
btsb = bts_role_bts(bts);
test_paging_smoke();
test_paging_sleep();
printf("Success\n");
return 0;
}
/* stub to link */
const uint8_t abis_mac[6] = { 0,1,2,3,4,5 };
const char *software_version = "0815";
int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
void *obj, uint8_t adm_state)
{ return 0; }
int bts_model_init(struct gsm_bts *bts)
{ return 0; }
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, void *obj)
{ return 0; }
int bts_model_rsl_chan_rel(struct gsm_lchan *lchan)
{ return 0;}
int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan)
{ return 0; }
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx)
{ return 0; }
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
struct tlv_parsed *old_attr, struct tlv_parsed *new_attr,
void *obj)
{ return 0; }
int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
void *obj)
{ return 0; }
int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp)
{ return 0; }
int bts_model_rsl_mode_modify(struct gsm_lchan *lchan)
{ return 0; }
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
unsigned int rtp_pl_len) {}
int 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)
{ return 0; }
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx)
{ return 0; }

View File

@@ -0,0 +1,2 @@
Testing that paging messages expire.
Success

8
tests/testsuite.at Normal file
View File

@@ -0,0 +1,8 @@
AT_INIT
AT_BANNER([Regression tests.])
AT_SETUP([paging])
AT_KEYWORDS([paging])
cat $abs_srcdir/paging/paging_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/paging/paging_test], [], [expout], [ignore])
AT_CLEANUP