Compare commits

..

253 Commits
0.0.1 ... 0.2.0

Author SHA1 Message Date
Andreas Eversberg
6a32f0499f Get RSSI from received uplink data and send to PCU
This bumps the PCU API version and thus requires a new version of the
code on the sysmoBTS side!
2013-03-17 17:11:58 +01:00
Harald Welte
a5608d092d measurement: use new 'struct gsm_meas_rep_unidir' of libosmocore 2013-03-17 17:10:35 +01:00
Holger Hans Peter Freyther
4ad8d4d3c0 sysmobts: Name the screen and use '-X quit' to shut it down
Use "kill -2 0" for the PCU as SIGTERM is not handled yet. With
the current set of code the stop function will stop both the PCU
and the BTS.
2013-03-16 23:29:59 +01:00
Holger Hans Peter Freyther
e45fc86359 respawn: Adjust the oom score for the supervisor and bts/pcu, increase sleep
Make the script mostly unkillable due to OOM and make sure that the
process has a score of zero. Wait 10 seconds before re-launching.

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

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

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

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

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

Use link timeout value from BSC via OML attribute.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

19
.gitignore vendored
View File

@@ -15,6 +15,25 @@ missing
core
core.*
contrib/sysmobts-calib/sysmobts-calib
src/osmo-bts-sysmo/l1fwd-proxy
src/osmo-bts-sysmo/sysmobts
src/osmo-bts-sysmo/sysmobts-remote
src/osmo-bts-sysmo/sysmobts-mgr
tests/atconfig
tests/package.m4
tests/paging/paging_test
tests/cipher/cipher_test
tests/testsuite
tests/testsuite.log
# Possible generated file
doc/vty_reference.xml
# Backups, vi, merges
*.sw?
*.orig
*.sav

View File

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

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
for turning modified OsmocomBB-supported phones into a simplistic BTS.
This code implementes the Layer 2 and higher of a more or less
conventional GSM BTS (Base Transceiver Station) - however, using an
Abis/IP interface, rather than the old-fashioned E1/T1.
However, the BTS-side A-bis is also going to be needed for other projects, thus
the split.
Specificallt, this includes
* BTS-Side implementation of TS 08.58 (RSL) and TS 12.21 (OML)
* BTS-Side implementation of LAPDm (using libosmocore/libosmogsm)
* A somewhat separated interface between those higher layer parts
and the Layer1 interface.
It doesn't really build yet, as a lot of dependencies have not yet been
resolved.
Right now, only one hardware and Layer1 are supported: The sysmocom
sysmoBTS.
There is some experimental and way incomplete code to use a couple of
OsmocomBB phones and run them in the BTS. However, the required code
for the Calypso DSP code have not been written yet. This would still
require a lot of work.
Some additional work is being done in using some parts of the OpenBTS
L1FEC and glue it against omso-bts. However, this is also still in an
early, experimental stage.
== Known Limitations ==
As of June 3, 2012, the following known limitations exist in this
implementation:
=== Common Core ===
* No Extended BCCH support
* System Information limited to 1,2,2bis,2ter,2quater,3,4,5,6,9,13
* No RATSCCH in AMR
* No OML (TS 12.21) alarms yet (temperature, ...)
* Only single-TRX BTS at this point
* Will reject TS 12.21 STARTING TIME in SET BTS ATTR / SET CHAN ATTR
* No support for frequency hopping
* No reporting of interference levels as part of TS 08.58 RF RES IND
* No error reporting in case PAGING COMMAND fails due to queue overflow
* No hand-over support (planned)
* No use of TS 08.58 BS Power and MS Power parameters
* No support of TS 08.58 MultiRate Control
* No support of TS 08.58 Supported Codec Types
* No support of Bter frame / ENHANCED MEASUREMENT REPORT
=== osmo-bts-sysmo ===
* No CSD / ECSD support (not planned)
* No GPRS/EDGE support (planned)
* GSM-R frequency band supported, but no NCH/ASCI/SoLSA
* All timeslots on one TRX have to use same training sequence (TSC)
* No multi-TRX support yet, though hardware+L1 support stacking
* Makes no use of 12.21 Intave Parameters and Interference
Level Boundaries
* Makes no use of TS 12.21 T3105
* Doesn't yet include MAC address in Abis/IP Identity message

View File

@@ -4,6 +4,7 @@ AC_INIT([osmo-bts],
[openbsc-devel@lists.openbsc.org])
AM_INIT_AUTOMAKE([dist-bzip2])
AC_CONFIG_TESTDIR(tests)
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -33,11 +34,30 @@ AC_ARG_ENABLE(sysmocom-bts,
AC_MSG_RESULT([$enable_sysmocom_bts])
AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes")
# We share gsm_data.h with OpenBSC and need to be pointed to the source
# directory of OpenBSC for now.
AC_ARG_WITH([openbsc],
[AS_HELP_STRING([--with-openbsc=INCLUDE_DIR],
[OpenBSC include directory for openbsc/gsm_data_shared.h])],
[openbsc_incdir="$withval"],
[openbsc_incdir="`cd $srcdir; pwd`/../openbsc/openbsc/include"])
AC_SUBST([OPENBSC_INCDIR], $openbsc_incdir)
oldCPPFLAGS=$CPPFLAGS
CPPFLAGS="$CPPFLAGS -I$OPENBSC_INCDIR $LIBOSMOCORE_CFLAGS"
AC_CHECK_HEADER([openbsc/gsm_data_shared.h],[],
[AC_MSG_ERROR([openbsc/gsm_data_shared.h can not be found in $openbsc_incdir])],
[])
CPPFLAGS=$oldCPPFLAGS
AC_OUTPUT(
src/Makefile
src/common/Makefile
src/osmo-bts-sysmo/Makefile
src/osmo-bts-bb/Makefile
dnl src/osmo-bts-bb/Makefile
include/Makefile
include/osmo-bts/Makefile
tests/Makefile
tests/paging/Makefile
tests/cipher/Makefile
Makefile)

40
contrib/dump_docs.py Executable file
View File

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

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

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

View File

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

View File

@@ -1,3 +1,4 @@
chdir /tmp
screen -t BTS 0 /etc/osmocom/respawn.sh /usr/bin/sysmobts -c /etc/osmocom/osmo-bts.cfg
screen -t BTS 0 /etc/osmocom/respawn.sh /usr/bin/sysmobts -c /etc/osmocom/osmo-bts.cfg -M
screen -t PCU 1 /etc/osmocom/respawn-only.sh /usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
detach

View File

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

View File

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

View File

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

View File

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

View File

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

15
contrib/sysmobts.service Normal file
View File

@@ -0,0 +1,15 @@
[Unit]
Description=sysmocom sysmoBTS
[Service]
Type=simple
ExecStartPre=/bin/sh -c 'echo 0 > /sys/class/leds/activity_led/brightness'
ExecStart=/usr/bin/sysmobts -s -c /etc/osmocom/osmo-bts.cfg
ExecStartPost=/bin/sh -c 'echo 0 > /sys/class/leds/activity_led/brightness'
ExecStartPost=/bin/sh -c 'cat /lib/firmware/sysmobts-v?.out > /dev/dspdl_dm644x_0'
Restart=always
RestartSec=2
# The msg queues must be read fast enough
CPUSchedulingPolicy=rr
CPUSchedulingPriority=1

View File

@@ -3,7 +3,7 @@
!!
!
log stderr
logging color 1
logging color 0
logging timestamp 0
logging level all everything
logging level rsl info
@@ -13,15 +13,9 @@ log stderr
logging level meas notice
logging level pag info
logging level l1c info
logging level l1p debug
logging level l1p info
logging level dsp debug
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
no login
@@ -30,4 +24,3 @@ bts 0
band 1800
ipa unit-id 1234 0
oml remote-ip 192.168.100.11
rtp bind-ip 192.168.100.239

View File

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

View File

@@ -1,2 +1,2 @@
noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \
oml.h paging.h rsl.h signal.h vty.h
oml.h paging.h rsl.h signal.h vty.h amr.h pcu_if.h pcuif_proto.h

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,14 @@ int trx_link_estab(struct gsm_bts_trx *trx);
void bts_new_si(void *arg);
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);
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 *lchan_sacch_get(struct gsm_lchan *lchan, struct gsm_time *g_time);
int lchan_init_lapdm(struct gsm_lchan *lchan);
void load_timer_start(struct gsm_bts *bts);
#endif /* _BTS_H */

View File

@@ -30,10 +30,17 @@ 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_rel(struct gsm_lchan *lchan);
int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan);
int bts_model_rsl_mode_modify(struct gsm_lchan *lchan);
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx);
int bts_model_trx_close(struct gsm_bts_trx *trx);
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, 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);
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

View File

@@ -7,9 +7,13 @@
#include <osmo-bts/paging.h>
struct pcu_sock_state;
struct gsm_network {
struct llist_head bts_list;
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 */
@@ -36,6 +40,10 @@ struct gsm_bts_role_bts {
/* Input parameters from OML */
int16_t busy_thresh; /* in dBm */
uint16_t averaging_slots;
/* Internal data */
unsigned int total; /* total nr */
unsigned int busy; /* above busy_thresh */
unsigned int access; /* access bursts */
} rach;
} load;
uint8_t ny1;
@@ -43,12 +51,27 @@ struct gsm_bts_role_bts {
struct llist_head agch_queue;
struct paging_state *paging_state;
char *bsc_oml_host;
char *rtp_bind_host;
unsigned int rtp_jitter_buf_ms;
struct {
uint8_t ciphers; /* flags A5/1==0x1, A5/2==0x2, A5/3==0x4 */
} support;
struct {
uint8_t tc4_ctr;
} si;
uint8_t radio_link_timeout;
};
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)
#include "../../openbsc/openbsc/include/openbsc/gsm_data_shared.h"
#include "openbsc/gsm_data_shared.h"
struct femtol1_hdl;
@@ -57,4 +80,13 @@ static inline struct femtol1_hdl *trx_femtol1_hdl(struct gsm_bts_trx *trx)
return trx->role_bts.l1h;
}
void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state);
/* cipher code */
#define CIPHER_A5(x) (1 << (x-1))
int bts_supports_cipher(struct gsm_bts_role_bts *bts, int rsl_cipher);
#endif /* _GSM_DATA_H */

View File

@@ -14,6 +14,7 @@ enum {
DL1C,
DL1P,
DDSP,
DPCU,
DABIS,
DRTP,
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_opstart_ack(struct gsm_abis_mo *mo);
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 */
int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state);

View File

@@ -6,13 +6,28 @@
#include <osmocom/gsm/protocol/gsm_04_08.h>
struct paging_state;
struct gsm_bts_role_bts;
/* 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);
/* (re) configure paging code */
void paging_config(struct paging_state *ps,
unsigned int num_paging_max,
unsigned int paging_lifetime);
void paging_reset(struct paging_state *ps);
/* The max number of paging entries */
unsigned int paging_get_queue_max(struct paging_state *ps);
void paging_set_queue_max(struct paging_state *ps, unsigned int queue_max);
/* The lifetime of a paging entry */
unsigned int paging_get_lifetime(struct paging_state *ps);
void paging_set_lifetime(struct paging_state *ps, unsigned int lifetime);
/* update with new SYSTEM INFORMATION parameters */
int paging_si_update(struct paging_state *ps, struct gsm48_control_channel_descr *chan_desc);
@@ -20,7 +35,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,
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 */
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

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

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

View File

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

View File

@@ -8,12 +8,17 @@ 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_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime);
int rsl_tx_chan_act_nack(struct gsm_lchan *lchan, uint8_t cause);
int rsl_tx_conn_fail(struct gsm_lchan *lchan, uint8_t cause);
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan);
/* 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 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);

View File

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

View File

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

View File

@@ -1,7 +1,8 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include
INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOTRAU_LIBS)
noinst_LIBRARIES = libbts.a
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/oml.h>
extern char *software_version;
extern uint8_t abis_mac[6];
/*
@@ -55,7 +54,7 @@ extern uint8_t abis_mac[6];
/* send message to BSC */
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");
msgb_free(msg);
return -EIO;
@@ -174,7 +173,7 @@ static int abis_rx_ipa_id_get(struct ipabis_link *link, uint8_t *data, int len)
break;
case IPAC_IDTAG_EQUIPVERS:
case IPAC_IDTAG_SWVERSION:
strcpy(str, software_version);
strcpy(str, PACKAGE_VERSION);
break;
case IPAC_IDTAG_UNITNAME:
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 (!link->rx_msg) {
link->rx_msg = msgb_alloc(ABIS_ALLOC_SIZE, "Abis/IP");
link->rx_msg = abis_msgb_alloc(128);
if (!link->rx_msg)
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,17 +57,33 @@ int bts_init(struct gsm_bts *bts)
struct gsm_bts_trx *trx;
int rc;
/* add to list of BTSs */
llist_add_tail(&bts->list, &bts_gsmnet.bts_list);
bts->band = GSM_BAND_1800;
bts->role = btsb = talloc_zero(bts, struct gsm_bts_role_bts);
INIT_LLIST_HEAD(&btsb->agch_queue);
/* FIXME: make those parameters configurable */
/* configurable via VTY */
btsb->paging_state = paging_init(btsb, 200, 0);
/* configurable via OML */
btsb->load.ccch.load_ind_period = 112;
load_timer_start(bts);
btsb->rtp_jitter_buf_ms = 100;
btsb->max_ta = 63;
/* default RADIO_LINK_TIMEOUT */
btsb->radio_link_timeout = 32;
/* set BTS to 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 */
llist_for_each_entry(trx, &bts->trx_list, list) {
@@ -86,11 +102,11 @@ int bts_init(struct gsm_bts *bts)
osmo_rtp_init(tall_bts_ctx);
rc = bts_model_init(bts);
if (rc < 0)
if (rc < 0) {
llist_del(&bts->list);
return rc;
}
/* add to list of BTSs */
llist_add_tail(&bts->list, &bts_gsmnet.bts_list);
bts_gsmnet.num_bts++;
return rc;
@@ -113,235 +129,16 @@ void bts_shutdown(struct gsm_bts *bts, const char *reason)
LOGP(DOML, LOGL_NOTICE, "Shutting down BTS %u, Reason %s\n",
bts->nr, reason);
llist_for_each_entry(trx, &bts->trx_list, list)
llist_for_each_entry(trx, &bts->trx_list, list) {
bts_model_trx_deact_rf(trx);
bts_model_trx_close(trx);
}
/* shedule a timer to make sure select loop logic can run again
* to dispatch any pending primitives */
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 */
int bts_link_estab(struct gsm_bts *bts)
{
@@ -353,6 +150,12 @@ int bts_link_estab(struct gsm_bts *bts)
oml_tx_state_changed(&bts->site_mgr.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 */
for (i = 0; i < bts->num_trx; i++) {
struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, i);
@@ -387,36 +190,6 @@ int trx_link_estab(struct gsm_bts_trx *trx)
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)
{
struct lapdm_channel *lc = &lchan->lapdm_ch;
@@ -431,24 +204,32 @@ int lchan_init_lapdm(struct gsm_lchan *lchan)
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 */
llist_add_tail(&msg->list, &btsb->agch_queue);
msgb_enqueue(&btsb->agch_queue, msg);
return 0;
}
struct msgb *bts_agch_dequeue(struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);;
struct msgb *msg;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
if (llist_empty(&btsb->agch_queue))
return NULL;
msg = llist_entry(btsb->agch_queue.next, struct msgb, list);
llist_del(&msg->list);
return msg;
return msgb_dequeue(&btsb->agch_queue);
}
int bts_supports_cipher(struct gsm_bts_role_bts *bts, int rsl_cipher)
{
int sup;
if (rsl_cipher < 1 || rsl_cipher > 8)
return -ENOTSUP;
/* No encryption is always supported */
if (rsl_cipher == 1)
return 1;
sup = (1 << (rsl_cipher - 2)) & bts->support.ciphers;
return sup > 0;
}

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/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 */
btsb->load.ccch.pch_used = btsb->load.ccch.pch_total = 0;
}
@@ -32,38 +39,62 @@ static void reset_load_counters(void)
static void load_timer_cb(void *data)
{
struct gsm_bts *bts = data;
struct gsm_bts_role_bts *btsb = FIXME;
unsigned int pch_percent;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
unsigned int pch_percent, rach_percent;
/* 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) {
/* send RSL load indication message to BSC */
uint16_t paging_buffer_space = FIXME;
rsl_tx_ccch_load_ind_pch(bts, paging_buffer_space);
uint16_t buffer_space = paging_buffer_space(btsb->paging_state);
rsl_tx_ccch_load_ind_pch(bts, buffer_space);
} else {
/* This is an extenstion of TS 08.58. We don't only
* send load indications if the load is above threshold,
* but we also explicitly indicate that we are below
* threshold by using the magic value 0xffff */
rsl_tx_ccch_load_ind_pch(bts, 0xffff);
}
reset_load_counters();
if (btsb->load.rach.total == 0)
rach_percent = 0;
else
rach_percent = (btsb->load.rach.busy * 100) /
btsb->load.rach.total;
if (rach_percent >= btsb->load.ccch.load_ind_thresh) {
/* send RSL load indication message to BSC */
rsl_tx_ccch_load_ind_rach(bts, btsb->load.rach.total,
btsb->load.rach.busy,
btsb->load.rach.access);
}
reset_load_counters(bts);
/* re-schedule the timer */
osmo_timer_schedule(&btsb->load.ccch.timer,
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;
reset_load_counters();
btsb->load.ccch.timer.cb = load_timer_cb;
reset_load_counters(bts);
osmo_timer_schedule(&btsb->load.ccch.timer,
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);
}

View File

@@ -95,6 +95,12 @@ static struct log_info_cat bts_log_info_cat[] = {
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DPCU] = {
.name = "DPCU",
.description = "PCU interface",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
#if 0
[DNS] = {
.name = "DNS",

View File

@@ -178,10 +178,10 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
irssi_sub_sum);
/* store results */
lchan->meas.res.rxlev_full = dbm2rxlev((int)irssi_full_sum * -1);
lchan->meas.res.rxlev_sub = dbm2rxlev((int)irssi_sub_sum * -1);
lchan->meas.res.rxqual_full = ber10k_to_rxqual(ber_full_sum);
lchan->meas.res.rxqual_sub = ber10k_to_rxqual(ber_sub_sum);
lchan->meas.ul_res.full.rx_lev = dbm2rxlev((int)irssi_full_sum * -1);
lchan->meas.ul_res.sub.rx_lev = dbm2rxlev((int)irssi_sub_sum * -1);
lchan->meas.ul_res.full.rx_qual = ber10k_to_rxqual(ber_full_sum);
lchan->meas.ul_res.sub.rx_qual = ber10k_to_rxqual(ber_sub_sum);
lchan->meas.flags |= LC_UL_M_F_RES_VALID;
lchan->meas.num_ul_meas = 0;
@@ -194,10 +194,10 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
/* build the 3 byte RSL uplinke measurement IE content */
int lchan_build_rsl_ul_meas(struct gsm_lchan *lchan, uint8_t *buf)
{
buf[0] = (lchan->meas.res.rxlev_full & 0x3f); /* FIXME: DTXu support */
buf[1] = (lchan->meas.res.rxlev_sub & 0x3f);
buf[2] = ((lchan->meas.res.rxqual_full & 7) << 3) |
(lchan->meas.res.rxqual_sub & 7);
buf[0] = (lchan->meas.ul_res.full.rx_lev & 0x3f); /* FIXME: DTXu support */
buf[1] = (lchan->meas.ul_res.sub.rx_lev & 0x3f);
buf[2] = ((lchan->meas.ul_res.full.rx_qual & 7) << 3) |
(lchan->meas.ul_res.sub.rx_qual & 7);
return 3;
}
@@ -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_TCH_F:
case GSM_LCHAN_TCH_H:
case GSM_LCHAN_PDTCH:
lchan_meas_check_compute(lchan, fn);
break;
default:

View File

@@ -37,6 +37,8 @@
#include <osmo-bts/abis.h>
#include <osmo-bts/oml.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/signal.h>
/* FIXME: move this to libosmocore */
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 */
static const char ipaccess_magic[] = "com.ipaccess";
static int oml_ipa_set_attr(struct gsm_bts *bts, struct msgb *msg);
/*
* 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(op_state));
mo->nm_state.operational = op_state;
osmo_signal_dispatch(SS_GLOBAL, S_NEW_OP_STATE, NULL);
}
/* 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);
}
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)
{
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_fom_hdr *old_foh = msgb_l3(old_msg);
struct msgb *msg = oml_msgb_alloc();
struct msgb *msg;
struct abis_om_fom_hdr *foh;
int is_manuf = 0;
msg = oml_msgb_alloc();
if (!msg)
return -ENOMEM;
/* make sure to respond with MANUF if request was MANUF */
if (old_oh->mdisc == ABIS_OM_MDISC_MANUF)
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 */
if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) {
const uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN);
uint16_t arfcn = ntohs(*value);
const uint16_t *value = (const uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN);
uint16_t arfcn = ntohs(tlvp_val16_unal(&tp, NM_ATT_BCCH_ARFCN));
LOGP(DOML, LOGL_NOTICE, "MSG: %s\n", osmo_hexdump(msgb_l3(msg), msgb_l3len(msg)));
LOGP(DOML, LOGL_NOTICE, "L3=%p, VAL=%p, DIF=%tu\n", msgb_l3(msg), value,
(void *)value - (void *) msgb_l3(msg));
if (arfcn > 1024) {
LOGP(DOML, LOGL_NOTICE, "Given ARFCN %d is not supported.\n", arfcn);
return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL);
@@ -414,13 +440,27 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
for (i = 0; i < 6; i++) {
int16_t boundary = *payload;
btsb->interference.boundary[i] = -1 * boundary;
}
}
/* 9.4.24 Intave Parameter */
if (TLVP_PRESENT(&tp, NM_ATT_INTAVE_PARAM))
btsb->interference.intave = *TLVP_VAL(&tp, NM_ATT_INTAVE_PARAM);
/* 9.4.14 Connection Failure Criterion */
/* ... can be 'operator dependent' and needs to be parsed by bts driver */
if (TLVP_PRESENT(&tp, NM_ATT_CONN_FAIL_CRIT)) {
const uint8_t *val = TLVP_VAL(&tp, NM_ATT_CONN_FAIL_CRIT);
if (TLVP_LEN(&tp, NM_ATT_CONN_FAIL_CRIT) < 2
|| val[0] != 0x01 || val[1] < 4 || val[1] > 64) {
LOGP(DOML, LOGL_NOTICE, "Given Conn. Failure Criterion "
"not supported. Please use critetion 0x01 with "
"RADIO_LINK_TIMEOUT value of 4..64\n");
return oml_fom_ack_nack(msg, NM_NACK_PARAM_RANGE);
}
btsb->radio_link_timeout = val[1];
}
/* if val[0] != 0x01: can be 'operator dependent' and needs to
* be parsed by bts driver */
/* 9.4.53 T200 */
if (TLVP_PRESENT(&tp, NM_ATT_T200)) {
@@ -430,10 +470,8 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
}
/* 9.4.31 Maximum Timing Advance */
if (TLVP_PRESENT(&tp, NM_ATT_MAX_TA)) {
uint16_t *fn = (uint16_t *) TLVP_VAL(&tp, NM_ATT_MAX_TA);
btsb->max_ta = ntohs(*fn);
}
if (TLVP_PRESENT(&tp, NM_ATT_MAX_TA))
btsb->max_ta = *TLVP_VAL(&tp, NM_ATT_MAX_TA);
/* 9.4.39 Overload Period */
if (TLVP_PRESENT(&tp, NM_ATT_OVERL_PERIOD))
@@ -454,9 +492,9 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
}
/* 9.4.45 RACH Load Averaging Slots */
if (TLVP_PRESENT(&tp, NM_ATT_LDAVG_SLOTS))
payload = TLVP_VAL(&tp, NM_ATT_LDAVG_SLOTS);
btsb->load.rach.averaging_slots = ntohs(*(uint16_t *)payload);
if (TLVP_PRESENT(&tp, NM_ATT_LDAVG_SLOTS)) {
btsb->load.rach.averaging_slots =
ntohs(tlvp_val16_unal(&tp, NM_ATT_LDAVG_SLOTS));
}
/* 9.4.10 BTS Air Timer */
@@ -468,10 +506,9 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
btsb->ny1 = *TLVP_VAL(&tp, NM_ATT_NY1);
/* 9.4.8 BCCH ARFCN */
if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) {
const uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN);
bts->c0->arfcn = ntohs(*value);
}
if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN))
bts->c0->arfcn = ntohs(tlvp_val16_unal(&tp, NM_ATT_BCCH_ARFCN));
/* 9.4.9 BSIC */
if (TLVP_PRESENT(&tp, NM_ATT_BSIC))
bts->bsic = *TLVP_VAL(&tp, NM_ATT_BSIC);
@@ -514,18 +551,22 @@ static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg)
/* 9.4.47 RF Max Power Reduction */
if (TLVP_PRESENT(&tp, NM_ATT_RF_MAXPOWR_R)) {
trx->max_power_red = *TLVP_VAL(&tp, NM_ATT_RF_MAXPOWR_R);
LOGP(DOML, LOGL_INFO, "Set RF Max Power Reduction = %d\n", trx->max_power_red);
trx->max_power_red = *TLVP_VAL(&tp, NM_ATT_RF_MAXPOWR_R) * 2;
LOGP(DOML, LOGL_INFO, "Set RF Max Power Reduction = %d dBm\n",
trx->max_power_red);
}
/* 9.4.5 ARFCN List */
#if 0
if (TLVP_PRESENT(&tp, NM_ATT_ARFCN_LIST)) {
uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_ARFCN_LIST);
uint8_t *value = TLVP_VAL(&tp, NM_ATT_ARFCN_LIST);
uint16_t _value;
uint16_t length = TLVP_LEN(&tp, NM_ATT_ARFCN_LIST);
uint16_t arfcn;
int i;
for (i = 0; i < length; i++) {
arfcn = ntohs(*value++);
memcpy(&_value, value, 2);
arfcn = ntohs(_value);
value += 2;
if (arfcn > 1024)
return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL);
trx->arfcn_list[i] = arfcn;
@@ -571,6 +612,10 @@ static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts)
lchan->type = GSM_LCHAN_SDCCH;
}
break;
case GSM_PCHAN_PDCH:
lchan = &ts->lchan[0];
lchan->type = GSM_LCHAN_PDTCH;
break;
default:
/* FIXME */
break;
@@ -607,17 +652,21 @@ static int oml_rx_set_chan_attr(struct gsm_bts_trx_ts *ts, struct msgb *msg)
}
/* 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);
/* Call into BTS driver to check attribute values */
rc = bts_model_check_oml(bts, foh->msg_type, ts->mo.nm_attr, tp_merged, ts);
if (rc < 0) {
talloc_free(&tp_merged);
talloc_free(tp_merged);
/* FIXME: Send NACK */
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 */
if (TLVP_PRESENT(&tp, NM_ATT_CHAN_COMB)) {
uint8_t comb = *TLVP_VAL(&tp, NM_ATT_CHAN_COMB);
@@ -702,7 +751,7 @@ static int oml_rx_chg_adm_state(struct gsm_bts *bts, struct msgb *msg)
if (mo->nm_state.administrative == adm_state) {
DEBUGP(DOML, "... automatic ACK, ADM state already was %s\n",
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 */
@@ -749,6 +798,9 @@ static int down_fom(struct gsm_bts *bts, struct msgb *msg)
case NM_MT_CHG_ADM_STATE:
ret = oml_rx_chg_adm_state(bts, msg);
break;
case NM_MT_IPACC_SET_ATTR:
ret = oml_ipa_set_attr(bts, msg);
break;
default:
LOGP(DOML, LOGL_INFO, "unknown Formatted O&M msg_type 0x%02x\n",
foh->msg_type);
@@ -762,6 +814,176 @@ static int down_fom(struct gsm_bts *bts, struct msgb *msg)
* 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(tlvp_val16_unal(tp, NM_ATT_IPACC_NSEI));
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NS_CFG, 7)) {
memcpy(&bts->gprs.nse.timer,
TLVP_VAL(tp, NM_ATT_IPACC_NS_CFG), 7);
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_BSSGP_CFG, 11)) {
memcpy(&bts->gprs.cell.timer,
TLVP_VAL(tp, NM_ATT_IPACC_BSSGP_CFG), 11);
}
osmo_signal_dispatch(SS_GLOBAL, S_NEW_NSE_ATTR, bts);
return 0;
}
static int oml_ipa_mo_set_attr_cell(void *obj, struct tlv_parsed *tp)
{
struct gsm_bts *bts = container_of(obj, struct gsm_bts, gprs.cell);
struct gprs_rlc_cfg *rlcc = &bts->gprs.cell.rlc_cfg;
const uint8_t *cur;
uint16_t _cur_s;
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RAC, 1))
bts->gprs.rac = *TLVP_VAL(tp, NM_ATT_IPACC_RAC);
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_GPRS_PAGING_CFG, 2)) {
cur = TLVP_VAL(tp, NM_ATT_IPACC_GPRS_PAGING_CFG);
rlcc->paging.repeat_time = cur[0] * 50;
rlcc->paging.repeat_count = cur[1];
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_BVCI, 2))
bts->gprs.cell.bvci =
htons(tlvp_val16_unal(tp, NM_ATT_IPACC_BVCI));
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG, 9)) {
cur = TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG);
rlcc->parameter[RLC_T3142] = cur[0];
rlcc->parameter[RLC_T3169] = cur[1];
rlcc->parameter[RLC_T3191] = cur[2];
rlcc->parameter[RLC_T3193] = cur[3];
rlcc->parameter[RLC_T3195] = cur[4];
rlcc->parameter[RLC_N3101] = cur[5];
rlcc->parameter[RLC_N3103] = cur[6];
rlcc->parameter[RLC_N3105] = cur[7];
rlcc->parameter[CV_COUNTDOWN] = cur[8];
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_CODING_SCHEMES, 2)) {
int i;
rlcc->cs_mask = 0;
cur = TLVP_VAL(tp, NM_ATT_IPACC_CODING_SCHEMES);
for (i = 0; i < 4; i++) {
if (cur[0] & (1 << i))
rlcc->cs_mask |= (1 << (GPRS_CS1+i));
}
if (cur[0] & 0x80)
rlcc->cs_mask |= (1 << GPRS_MCS9);
for (i = 0; i < 8; i++) {
if (cur[1] & (1 << i))
rlcc->cs_mask |= (1 << (GPRS_MCS1+i));
}
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG_2, 5)) {
cur = TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG_2);
memcpy(&_cur_s, cur, 2);
rlcc->parameter[T_DL_TBF_EXT] = ntohs(_cur_s) * 10;
cur += 2;
memcpy(&_cur_s, cur, 2);
rlcc->parameter[T_UL_TBF_EXT] = ntohs(_cur_s) * 10;
cur += 2;
rlcc->initial_cs = *cur;
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG_3, 1)) {
rlcc->initial_mcs = *TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG_3);
}
osmo_signal_dispatch(SS_GLOBAL, S_NEW_CELL_ATTR, bts);
return 0;
}
static int oml_ipa_mo_set_attr_nsvc(struct gsm_bts_gprs_nsvc *nsvc,
struct tlv_parsed *tp)
{
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NSVCI, 2))
nsvc->nsvci = ntohs(tlvp_val16_unal(tp, NM_ATT_IPACC_NSVCI));
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NS_LINK_CFG, 8)) {
const uint8_t *cur = TLVP_VAL(tp, NM_ATT_IPACC_NS_LINK_CFG);
uint16_t _cur_s;
uint32_t _cur_l;
memcpy(&_cur_s, cur, 2);
nsvc->remote_port = ntohs(_cur_s);
cur += 2;
memcpy(&_cur_l, cur, 4);
nsvc->remote_ip = ntohl(_cur_l);
cur += 4;
memcpy(&_cur_s, cur, 2);
nsvc->local_port = ntohs(_cur_s);
}
osmo_signal_dispatch(SS_GLOBAL, S_NEW_NSVC_ATTR, nsvc);
return 0;
}
static int oml_ipa_mo_set_attr(struct gsm_bts *bts, struct gsm_abis_mo *mo,
void *obj, struct tlv_parsed *tp)
{
int rc;
switch (mo->obj_class) {
case NM_OC_GPRS_NSE:
rc = oml_ipa_mo_set_attr_nse(obj, tp);
break;
case NM_OC_GPRS_CELL:
rc = oml_ipa_mo_set_attr_cell(obj, tp);
break;
case NM_OC_GPRS_NSVC:
rc = oml_ipa_mo_set_attr_nsvc(obj, tp);
break;
default:
rc = NM_NACK_OBJINST_UNKN;
}
return rc;
}
static int oml_ipa_set_attr(struct gsm_bts *bts, struct msgb *msg)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
struct gsm_abis_mo *mo;
struct tlv_parsed tp;
void *obj;
int rc;
abis_nm_debugp_foh(DOML, foh);
DEBUGPC(DOML, "Rx IPA SET ATTR\n");
rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh));
if (rc < 0)
return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT);
/* Resolve MO by obj_class/obj_inst */
mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst);
if (!mo || !obj)
return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN);
rc = oml_ipa_mo_set_attr(bts, mo, obj, &tp);
return oml_fom_ack_nack(msg, rc);
}
static int rx_oml_ipa_rsl_connect(struct gsm_bts_trx *trx, struct msgb *msg,
struct tlv_parsed *tp)
@@ -775,12 +997,10 @@ static int rx_oml_ipa_rsl_connect(struct gsm_bts_trx *trx, struct msgb *msg,
uint8_t stream_id = 0;
if (TLVP_PRESENT(tp, NM_ATT_IPACC_DST_IP)) {
const uint8_t *ptr = TLVP_VAL(tp, NM_ATT_IPACC_DST_IP);
ip = ntohl(*(uint32_t *)ptr);
ip = ntohl(tlvp_val32_unal(tp, NM_ATT_IPACC_DST_IP));
}
if (TLVP_PRESENT(tp, NM_ATT_IPACC_DST_IP_PORT)) {
const uint8_t *ptr = TLVP_VAL(tp, NM_ATT_IPACC_DST_IP_PORT);
port = ntohs(*(uint16_t *)ptr);
port = ntohs(tlvp_val16_unal(tp, NM_ATT_IPACC_DST_IP_PORT));
}
if (TLVP_PRESENT(tp, NM_ATT_IPACC_STREAM_ID)) {
stream_id = *TLVP_VAL(tp, NM_ATT_IPACC_STREAM_ID);
@@ -847,6 +1067,9 @@ static int down_mom(struct gsm_bts *bts, struct msgb *msg)
trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
ret = rx_oml_ipa_rsl_connect(trx, msg, &tp);
break;
case NM_MT_IPACC_SET_ATTR:
ret = oml_ipa_set_attr(bts, msg);
break;
default:
LOGP(DOML, LOGL_INFO, "Manufacturer Formatted O&M msg_type 0x%02x\n",
foh->msg_type);

View File

@@ -1,6 +1,6 @@
/* 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
*
@@ -41,18 +41,34 @@
#include <osmo-bts/logging.h>
#include <osmo-bts/paging.h>
#include <osmo-bts/signal.h>
#include <osmo-bts/pcu_if.h>
#define MAX_PAGING_BLOCKS_CCCH 9
#define MAX_BS_PA_MFRMS 9
enum paging_record_type {
PAGING_RECORD_PAGING,
PAGING_RECORD_IMM_ASS
};
struct paging_record {
struct llist_head list;
time_t expiration_time;
uint8_t chan_needed;
uint8_t identity_lv[9];
enum paging_record_type type;
union {
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 gsm_bts_role_bts *btsb;
/* parameters taken / interpreted from BCCH/CCCH configuration */
struct gsm48_control_channel_descr chan_desc;
@@ -65,6 +81,26 @@ struct paging_state {
struct llist_head paging_queue[MAX_PAGING_BLOCKS_CCCH*MAX_BS_PA_MFRMS];
};
unsigned int paging_get_lifetime(struct paging_state *ps)
{
return ps->paging_lifetime;
}
unsigned int paging_get_queue_max(struct paging_state *ps)
{
return ps->num_paging_max;
}
void paging_set_lifetime(struct paging_state *ps, unsigned int lifetime)
{
ps->paging_lifetime = lifetime;
}
void paging_set_queue_max(struct paging_state *ps, unsigned int queue_max)
{
ps->num_paging_max = queue_max;
}
static int tmsi_mi_to_uint(uint32_t *out, const uint8_t *tmsi_lv)
{
if (tmsi_lv[0] < 5)
@@ -128,6 +164,13 @@ static int get_pag_subch_nr(struct paging_state *ps, struct gsm_time *gt)
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 */
int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
@@ -144,10 +187,14 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
/* Check if we already have this identity */
llist_for_each_entry(pr, group_q, list) {
if (identity_lv[0] == pr->identity_lv[0] &&
!memcmp(identity_lv+1, pr->identity_lv+1, identity_lv[0])) {
if (pr->type != PAGING_RECORD_PAGING)
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");
pr->expiration_time = time(NULL) + ps->paging_lifetime;
pr->u.paging.expiration_time =
time(NULL) + ps->paging_lifetime;
return -EEXIST;
}
}
@@ -155,8 +202,9 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
pr = talloc_zero(ps, struct paging_record);
if (!pr)
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);
return -E2BIG;
}
@@ -164,9 +212,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",
paging_group, ps->num_paging+1);
pr->expiration_time = time(NULL) + ps->paging_lifetime;
pr->chan_needed = chan_needed;
memcpy(&pr->identity_lv, identity_lv, identity_lv[0]+1);
pr->u.paging.expiration_time = time(NULL) + ps->paging_lifetime;
pr->u.paging.chan_needed = chan_needed;
memcpy(&pr->u.paging.identity_lv, identity_lv, identity_lv[0]+1);
/* enqueue the new identity to the HEAD of the queue,
* to ensure it will be paged quickly at least once. */
@@ -176,6 +224,42 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
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)
static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv,
@@ -266,7 +350,7 @@ static struct paging_record *dequeue_pr(struct llist_head *group_q)
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;
else
return 0;
@@ -295,10 +379,22 @@ static void sort_pr_tmsi_imsi(struct paging_record *pr[], unsigned int n)
/* generate paging message for given gsm time */
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 = &ps->paging_queue[group];
struct llist_head *group_q;
int group;
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 */
if (llist_empty(group_q)) {
//DEBUGP(DPAG, "Tx PAGING TYPE 1 (empty)\n");
@@ -306,15 +402,24 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
NULL, 0);
} else {
struct paging_record *pr[4];
unsigned int num_pr = 0;
unsigned int num_pr = 0, imm_ass = 0;
time_t now = time(NULL);
unsigned int i, num_imsi = 0;
ps->btsb->load.ccch.pch_used += 1;
/* get (if we have) up to four paging records */
for (i = 0; i < ARRAY_SIZE(pr); i++) {
if (llist_empty(group_q))
break;
pr[i] = dequeue_pr(group_q);
/* check for IMM.ASS */
if (pr[i]->type == PAGING_RECORD_IMM_ASS) {
imm_ass = 1;
break;
}
num_pr++;
/* count how many IMSIs are among them */
@@ -322,27 +427,43 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
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 */
sort_pr_tmsi_imsi(pr, num_pr);
if (num_pr == 4 && num_imsi == 0) {
/* No IMSI: easy case, can use TYPE 3 */
DEBUGP(DPAG, "Tx PAGING TYPE 3 (4 TMSI)\n");
len = fill_paging_type_3(out_buf, pr[0]->identity_lv,
pr[0]->chan_needed,
pr[1]->identity_lv,
pr[1]->chan_needed,
pr[2]->identity_lv,
pr[3]->identity_lv);
len = fill_paging_type_3(out_buf,
pr[0]->u.paging.identity_lv,
pr[0]->u.paging.chan_needed,
pr[1]->u.paging.identity_lv,
pr[1]->u.paging.chan_needed,
pr[2]->u.paging.identity_lv,
pr[3]->u.paging.identity_lv);
} else if (num_pr >= 3 && num_imsi <= 1) {
/* 3 or 4, of which only up to 1 is IMSI */
DEBUGP(DPAG, "Tx PAGING TYPE 2 (2 TMSI,1 xMSI)\n");
len = fill_paging_type_2(out_buf,
pr[0]->identity_lv,
pr[0]->chan_needed,
pr[1]->identity_lv,
pr[1]->chan_needed,
pr[2]->identity_lv);
pr[0]->u.paging.identity_lv,
pr[0]->u.paging.chan_needed,
pr[1]->u.paging.identity_lv,
pr[1]->u.paging.chan_needed,
pr[2]->u.paging.identity_lv);
if (num_pr == 4) {
/* re-add #4 for next time */
llist_add(&pr[3]->list, group_q);
@@ -350,16 +471,19 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
}
} else if (num_pr == 1) {
DEBUGP(DPAG, "Tx PAGING TYPE 1 (1 xMSI,1 empty)\n");
len = fill_paging_type_1(out_buf, pr[0]->identity_lv,
pr[0]->chan_needed, NULL, 0);
len = fill_paging_type_1(out_buf,
pr[0]->u.paging.identity_lv,
pr[0]->u.paging.chan_needed,
NULL, 0);
} else {
/* 2 (any type) or
* 3 or 4, of which only 2 will be sent */
DEBUGP(DPAG, "Tx PAGING TYPE 1 (2 xMSI)\n");
len = fill_paging_type_1(out_buf, pr[0]->identity_lv,
pr[0]->chan_needed,
pr[1]->identity_lv,
pr[1]->chan_needed);
len = fill_paging_type_1(out_buf,
pr[0]->u.paging.identity_lv,
pr[0]->u.paging.chan_needed,
pr[1]->u.paging.identity_lv,
pr[1]->u.paging.chan_needed);
if (num_pr >= 3) {
/* re-add #4 for next time */
llist_add(&pr[2]->list, group_q);
@@ -378,7 +502,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
continue;
/* check if we can expire the paging record,
* 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]);
ps->num_paging--;
LOGP(DPAG, LOGL_INFO, "Removed paging record, queue_len=%u\n",
@@ -395,7 +519,7 @@ int paging_si_update(struct paging_state *ps, struct gsm48_control_channel_descr
{
LOGP(DPAG, LOGL_INFO, "Paging SI update\n");
memcpy(&ps->chan_desc, chan_desc, sizeof(chan_desc));
ps->chan_desc = *chan_desc;
/* FIXME: do we need to re-sort the old paging_records? */
@@ -418,16 +542,18 @@ static int paging_signal_cbfn(unsigned int subsys, unsigned int signal, void *hd
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)
{
struct paging_state *ps;
unsigned int i;
ps = talloc_zero(ctx, struct paging_state);
ps = talloc_zero(btsb, struct paging_state);
if (!ps)
return NULL;
ps->btsb = btsb;
ps->paging_lifetime = paging_lifetime;
ps->num_paging_max = num_paging_max;
@@ -441,6 +567,14 @@ struct paging_state *paging_init(void *ctx, unsigned int num_paging_max,
return ps;
}
void paging_config(struct paging_state *ps,
unsigned int num_paging_max,
unsigned int paging_lifetime)
{
ps->num_paging_max = num_paging_max;
ps->paging_lifetime = paging_lifetime;
}
void paging_reset(struct paging_state *ps)
{
int i;
@@ -460,3 +594,18 @@ void paging_reset(struct paging_state *ps)
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;
}

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

@@ -0,0 +1,922 @@
/* pcu_sock.c: Connect from PCU via unix domain socket */
/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2009-2012 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2012 by Holger Hans Peter Freyther
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/pcuif_proto.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/signal.h>
#include <osmo-bts/bts_model.h>
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx);
extern struct gsm_network bts_gsmnet;
extern int pcu_direct;
static int avail_lai = 0, avail_nse = 0, avail_cell = 0, avail_nsvc[2] = {0, 0};
static const char *sapi_string[] = {
[PCU_IF_SAPI_RACH] = "RACH",
[PCU_IF_SAPI_AGCH] = "AGCH",
[PCU_IF_SAPI_PCH] = "PCH",
[PCU_IF_SAPI_BCCH] = "BCCH",
[PCU_IF_SAPI_PDTCH] = "PDTCH",
[PCU_IF_SAPI_PRACH] = "PRACH",
[PCU_IF_SAPI_PTCCH] = "PTCCH",
};
/* FIXME: common l1if include ? */
int l1if_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
static int pcu_sock_send(struct gsm_network *net, struct msgb *msg);
/* FIXME: move this to libosmocore */
int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path);
static struct gsm_bts_trx *trx_by_nr(struct gsm_bts *bts, uint8_t trx_nr)
{
struct gsm_bts_trx *trx;
llist_for_each_entry(trx, &bts->trx_list, list) {
if (trx->nr == trx_nr)
return trx;
}
return NULL;
}
/*
* PCU messages
*/
struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx");
if (!msg)
return NULL;
msgb_put(msg, sizeof(struct gsm_pcu_if));
pcu_prim = (struct gsm_pcu_if *) msg->data;
pcu_prim->msg_type = msg_type;
pcu_prim->bts_nr = bts_nr;
return msg;
}
int pcu_tx_info_ind(void)
{
struct gsm_network *net = &bts_gsmnet;
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_info_ind *info_ind;
struct gsm_bts *bts;
struct gprs_rlc_cfg *rlcc;
struct gsm_bts_gprs_nsvc *nsvc;
struct gsm_bts_trx *trx;
struct gsm_bts_trx_ts *ts;
int i, j;
LOGP(DPCU, LOGL_INFO, "Sending info\n");
/* FIXME: allow multiple BTS */
bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
rlcc = &bts->gprs.cell.rlc_cfg;
msg = pcu_msgb_alloc(PCU_IF_MSG_INFO_IND, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
info_ind = &pcu_prim->u.info_ind;
info_ind->version = PCU_IF_VERSION;
if (avail_lai && avail_nse && avail_cell && avail_nsvc[0]) {
info_ind->flags |= PCU_IF_FLAG_ACTIVE;
LOGP(DPCU, LOGL_INFO, "BTS is up\n");
} else
LOGP(DPCU, LOGL_INFO, "BTS is down\n");
if (pcu_direct)
info_ind->flags |= PCU_IF_FLAG_SYSMO;
/* RAI */
info_ind->mcc = net->mcc;
info_ind->mnc = net->mnc;
info_ind->lac = bts->location_area_code;
info_ind->rac = bts->gprs.rac;
/* NSE */
info_ind->nsei = bts->gprs.nse.nsei;
memcpy(info_ind->nse_timer, bts->gprs.nse.timer, 7);
memcpy(info_ind->cell_timer, bts->gprs.cell.timer, 11);
/* cell attributes */
info_ind->cell_id = bts->cell_identity;
info_ind->repeat_time = rlcc->paging.repeat_time;
info_ind->repeat_count = rlcc->paging.repeat_count;
info_ind->bvci = bts->gprs.cell.bvci;
info_ind->t3142 = rlcc->parameter[RLC_T3142];
info_ind->t3169 = rlcc->parameter[RLC_T3169];
info_ind->t3191 = rlcc->parameter[RLC_T3191];
info_ind->t3193_10ms = rlcc->parameter[RLC_T3193];
info_ind->t3195 = rlcc->parameter[RLC_T3195];
info_ind->n3101 = rlcc->parameter[RLC_N3101];
info_ind->n3103 = rlcc->parameter[RLC_N3103];
info_ind->n3105 = rlcc->parameter[RLC_N3105];
info_ind->cv_countdown = rlcc->parameter[CV_COUNTDOWN];
if (rlcc->cs_mask & (1 << GPRS_CS1))
info_ind->flags |= PCU_IF_FLAG_CS1;
if (rlcc->cs_mask & (1 << GPRS_CS2))
info_ind->flags |= PCU_IF_FLAG_CS2;
if (rlcc->cs_mask & (1 << GPRS_CS3))
info_ind->flags |= PCU_IF_FLAG_CS3;
if (rlcc->cs_mask & (1 << GPRS_CS4))
info_ind->flags |= PCU_IF_FLAG_CS4;
if (rlcc->cs_mask & (1 << GPRS_MCS1))
info_ind->flags |= PCU_IF_FLAG_MCS1;
if (rlcc->cs_mask & (1 << GPRS_MCS2))
info_ind->flags |= PCU_IF_FLAG_MCS2;
if (rlcc->cs_mask & (1 << GPRS_MCS3))
info_ind->flags |= PCU_IF_FLAG_MCS3;
if (rlcc->cs_mask & (1 << GPRS_MCS4))
info_ind->flags |= PCU_IF_FLAG_MCS4;
if (rlcc->cs_mask & (1 << GPRS_MCS5))
info_ind->flags |= PCU_IF_FLAG_MCS5;
if (rlcc->cs_mask & (1 << GPRS_MCS6))
info_ind->flags |= PCU_IF_FLAG_MCS6;
if (rlcc->cs_mask & (1 << GPRS_MCS7))
info_ind->flags |= PCU_IF_FLAG_MCS7;
if (rlcc->cs_mask & (1 << GPRS_MCS8))
info_ind->flags |= PCU_IF_FLAG_MCS8;
if (rlcc->cs_mask & (1 << GPRS_MCS9))
info_ind->flags |= PCU_IF_FLAG_MCS9;
#warning "isn't dl_tbf_ext wrong?: * 10 and no ntohs"
info_ind->dl_tbf_ext = rlcc->parameter[T_DL_TBF_EXT];
#warning "isn't ul_tbf_ext wrong?: * 10 and no ntohs"
info_ind->ul_tbf_ext = rlcc->parameter[T_UL_TBF_EXT];
info_ind->initial_cs = rlcc->initial_cs;
info_ind->initial_mcs = rlcc->initial_mcs;
/* NSVC */
for (i = 0; i < 2; i++) {
nsvc = &bts->gprs.nsvc[i];
info_ind->nsvci[i] = nsvc->nsvci;
info_ind->local_port[i] = nsvc->local_port;
info_ind->remote_port[i] = nsvc->remote_port;
info_ind->remote_ip[i] = nsvc->remote_ip;
}
for (i = 0; i < 8; i++) {
trx = trx_by_nr(bts, i);
if (!trx)
break;
info_ind->trx[i].pdch_mask = 0;
info_ind->trx[i].arfcn = trx->arfcn;
info_ind->trx[i].hlayer1 = trx_get_hlayer1(trx);
for (j = 0; j < 8; j++) {
ts = &trx->ts[j];
if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
&& ts->pchan == GSM_PCHAN_PDCH) {
info_ind->trx[i].pdch_mask |= (1 << j);
info_ind->trx[i].tsc[j] =
(ts->tsc >= 0) ? ts->tsc : bts->tsc;
LOGP(DPCU, LOGL_INFO, "trx=%d ts=%d: "
"available (tsc=%d arfcn=%d)\n",
trx->nr, ts->nr,
info_ind->trx[i].tsc[j],
info_ind->trx[i].arfcn);
}
}
}
return pcu_sock_send(net, msg);
}
static int pcu_if_signal_cb(unsigned int subsys, unsigned int signal,
void *hdlr_data, void *signal_data)
{
struct gsm_network *net = &bts_gsmnet;
struct gsm_bts_gprs_nsvc *nsvc;
struct gsm_bts *bts;
struct gsm48_system_information_type_3 *si3;
int id;
if (subsys != SS_GLOBAL)
return -EINVAL;
switch(signal) {
case S_NEW_SYSINFO:
bts = signal_data;
if (!(bts->si_valid & (1 << SYSINFO_TYPE_3)))
break;
si3 = (struct gsm48_system_information_type_3 *)
bts->si_buf[SYSINFO_TYPE_3];
net->mcc = ((si3->lai.digits[0] & 0x0f) << 8)
| (si3->lai.digits[0] & 0xf0)
| (si3->lai.digits[1] & 0x0f);
net->mnc = ((si3->lai.digits[2] & 0x0f) << 8)
| (si3->lai.digits[2] & 0xf0)
| ((si3->lai.digits[1] & 0xf0) >> 4);
if ((net->mnc & 0x00f) == 0x00f)
net->mnc >>= 4;
bts->location_area_code = ntohs(si3->lai.lac);
bts->cell_identity = si3->cell_identity;
avail_lai = 1;
break;
case S_NEW_NSE_ATTR:
bts = signal_data;
avail_nse = 1;
break;
case S_NEW_CELL_ATTR:
bts = signal_data;
avail_cell = 1;
break;
case S_NEW_NSVC_ATTR:
nsvc = signal_data;
id = nsvc->id;
if (id < 0 || id > 1)
return -EINVAL;
avail_nsvc[id] = 1;
break;
case S_NEW_OP_STATE:
break;
default:
return -EINVAL;
}
/* If all infos have been received, of if one info is updated after
* all infos have been received, transmit info update. */
if (avail_lai && avail_nse && avail_cell && avail_nsvc[0])
pcu_tx_info_ind();
return 0;
}
int pcu_tx_rts_req(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_rts_req *rts_req;
struct gsm_bts *bts = ts->trx->bts;
LOGP(DPCU, LOGL_DEBUG, "Sending rts request: is_ptcch=%d arfcn=%d "
"block=%d\n", is_ptcch, arfcn, block_nr);
msg = pcu_msgb_alloc(PCU_IF_MSG_RTS_REQ, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
rts_req = &pcu_prim->u.rts_req;
rts_req->sapi = (is_ptcch) ? PCU_IF_SAPI_PTCCH : PCU_IF_SAPI_PDTCH;
rts_req->fn = fn;
rts_req->arfcn = arfcn;
rts_req->trx_nr = ts->trx->nr;
rts_req->ts_nr = ts->nr;
rts_req->block_nr = block_nr;
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len,
int8_t rssi)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_data *data_ind;
struct gsm_bts *bts = ts->trx->bts;
LOGP(DPCU, LOGL_DEBUG, "Sending data indication: is_ptcch=%d arfcn=%d "
"block=%d data=%s\n", is_ptcch, arfcn, block_nr,
osmo_hexdump(data, len));
msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_IND, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
data_ind = &pcu_prim->u.data_ind;
data_ind->sapi = (is_ptcch) ? PCU_IF_SAPI_PTCCH : PCU_IF_SAPI_PDTCH;
data_ind->fn = fn;
data_ind->arfcn = arfcn;
data_ind->trx_nr = ts->trx->nr;
data_ind->ts_nr = ts->nr;
data_ind->block_nr = block_nr;
data_ind->rssi = rssi;
memcpy(data_ind->data, data, len);
data_ind->len = len;
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint8_t ra, uint32_t fn)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_rach_ind *rach_ind;
LOGP(DPCU, LOGL_INFO, "Sending RACH indication: qta=%d, ra=%d, "
"fn=%d\n", qta, ra, fn);
msg = pcu_msgb_alloc(PCU_IF_MSG_RACH_IND, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
rach_ind = &pcu_prim->u.rach_ind;
rach_ind->sapi = PCU_IF_SAPI_RACH;
rach_ind->ra = ra;
rach_ind->qta = qta;
rach_ind->fn = fn;
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_time_ind(uint32_t fn)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_time_ind *time_ind;
uint8_t fn13 = fn % 13;
/* omit frame numbers not starting at a MAC block */
if (fn13 != 0 && fn13 != 4 && fn13 != 8)
return 0;
msg = pcu_msgb_alloc(PCU_IF_MSG_TIME_IND, 0);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
time_ind = &pcu_prim->u.time_ind;
time_ind->fn = fn;
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_pag_req(const uint8_t *identity_lv, uint8_t chan_needed)
{
struct pcu_sock_state *state = bts_gsmnet.pcu_state;
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_pag_req *pag_req;
/* check if identity does not fit: length > sizeof(lv) - 1 */
if (identity_lv[0] >= sizeof(pag_req->identity_lv)) {
LOGP(DPCU, LOGL_ERROR, "Paging identity too large (%d)\n",
identity_lv[0]);
return -EINVAL;
}
/* socket not created */
if (!state) {
LOGP(DPCU, LOGL_DEBUG, "PCU socket not created, ignoring "
"paging message\n");
return 0;
}
msg = pcu_msgb_alloc(PCU_IF_MSG_PAG_REQ, 0);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
pag_req = &pcu_prim->u.pag_req;
pag_req->chan_needed = chan_needed;
memcpy(pag_req->identity_lv, identity_lv, identity_lv[0] + 1);
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_pch_data_cnf(uint32_t fn, uint8_t *data, uint8_t len)
{
struct gsm_network *net = &bts_gsmnet;
struct gsm_bts *bts;
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_data *data_cnf;
/* FIXME: allow multiple BTS */
bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
LOGP(DPCU, LOGL_INFO, "Sending PCH confirm\n");
msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_CNF, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
data_cnf = &pcu_prim->u.data_cnf;
data_cnf->sapi = PCU_IF_SAPI_PCH;
data_cnf->fn = fn;
memcpy(data_cnf->data, data, len);
data_cnf->len = len;
return pcu_sock_send(&bts_gsmnet, msg);
}
static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
struct gsm_pcu_if_data *data_req)
{
uint8_t is_ptcch;
struct gsm_bts_trx *trx;
struct gsm_bts_trx_ts *ts;
struct msgb *msg;
int rc = 0;
LOGP(DPCU, LOGL_DEBUG, "Data request received: sapi=%s arfcn=%d "
"block=%d data=%s\n", sapi_string[data_req->sapi],
data_req->arfcn, data_req->block_nr,
osmo_hexdump(data_req->data, data_req->len));
switch (data_req->sapi) {
case PCU_IF_SAPI_BCCH:
if (data_req->len == 23) {
bts->si_valid |= (1 << SYSINFO_TYPE_13);
memcpy(bts->si_buf[SYSINFO_TYPE_13], data_req->data,
data_req->len);
} else {
bts->si_valid &= ~(1 << SYSINFO_TYPE_13);
}
osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts);
break;
case PCU_IF_SAPI_PCH:
if (msg_type == PCU_IF_MSG_PAG_REQ) {
/* FIXME: Add function to schedule paging request.
* This might not be required, if PCU_IF_MSG_DATA_REQ
* is used instead. */
} else {
struct gsm_bts_role_bts *btsb = bts->role;
paging_add_imm_ass(btsb->paging_state, data_req->data,
data_req->len);
}
break;
case PCU_IF_SAPI_AGCH:
msg = msgb_alloc(data_req->len, "pcu_agch");
if (!msg) {
rc = -ENOMEM;
break;
}
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 connected to external PCU\n");
/* send current info */
pcu_tx_info_ind();
return 0;
}
int pcu_sock_init(void)
{
struct pcu_sock_state *state;
struct osmo_fd *bfd;
int rc;
state = talloc_zero(NULL, struct pcu_sock_state);
if (!state)
return -ENOMEM;
INIT_LLIST_HEAD(&state->upqueue);
state->net = &bts_gsmnet;
state->conn_bfd.fd = -1;
bfd = &state->listen_bfd;
rc = osmo_unixsock_listen(bfd, SOCK_SEQPACKET, "/tmp/pcu_bts");
if (rc < 0) {
LOGP(DPCU, LOGL_ERROR, "Could not create unix socket: %s\n",
strerror(errno));
talloc_free(state);
return rc;
}
bfd->when = BSC_FD_READ;
bfd->cb = pcu_sock_accept;
bfd->data = state;
rc = osmo_fd_register(bfd);
if (rc < 0) {
LOGP(DPCU, LOGL_ERROR, "Could not register listen fd: %d\n",
rc);
close(bfd->fd);
talloc_free(state);
return rc;
}
osmo_signal_register_handler(SS_GLOBAL, pcu_if_signal_cb, NULL);
bts_gsmnet.pcu_state = state;
return 0;
}
void pcu_sock_exit(void)
{
struct pcu_sock_state *state = bts_gsmnet.pcu_state;
struct osmo_fd *bfd, *conn_bfd;
if (!state)
return;
osmo_signal_unregister_handler(SS_GLOBAL, pcu_if_signal_cb, NULL);
conn_bfd = &state->conn_bfd;
if (conn_bfd->fd > 0)
pcu_sock_close(state);
bfd = &state->listen_bfd;
close(bfd->fd);
osmo_fd_unregister(bfd);
talloc_free(state);
bts_gsmnet.pcu_state = NULL;
}
/* FIXME: move this to libosmocore */
int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path)
{
struct sockaddr_un local;
unsigned int namelen;
int rc;
bfd->fd = socket(AF_UNIX, type, 0);
if (bfd->fd < 0) {
fprintf(stderr, "Failed to create Unix Domain Socket.\n");
return -1;
}
local.sun_family = AF_UNIX;
strncpy(local.sun_path, path, sizeof(local.sun_path));
local.sun_path[sizeof(local.sun_path) - 1] = '\0';
unlink(local.sun_path);
/* we use the same magic that X11 uses in Xtranssock.c for
* calculating the proper length of the sockaddr */
#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
local.sun_len = strlen(local.sun_path);
#endif
#if defined(BSD44SOCKETS) || defined(SUN_LEN)
namelen = SUN_LEN(&local);
#else
namelen = strlen(local.sun_path) +
offsetof(struct sockaddr_un, sun_path);
#endif
rc = bind(bfd->fd, (struct sockaddr *) &local, namelen);
if (rc != 0) {
fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n",
local.sun_path);
close(bfd->fd);
bfd->fd = -1;
return -1;
}
if (listen(bfd->fd, 0) != 0) {
fprintf(stderr, "Failed to listen.\n");
close(bfd->fd);
bfd->fd = -1;
return -1;
}
return 0;
}

View File

@@ -1,7 +1,7 @@
/* GSM TS 08.58 RSL, BTS Side */
/* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
* (C) 2011-2013 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <arpa/inet.h>
@@ -38,9 +39,11 @@
#include <osmo-bts/bts.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/oml.h>
#include <osmo-bts/amr.h>
#include <osmo-bts/signal.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/measurement.h>
#include <osmo-bts/pcu_if.h>
//#define FAKE_CIPH_MODE_COMPL
@@ -121,89 +124,18 @@ static void lchan_tchmode_from_cmode(struct gsm_lchan *lchan,
* 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;
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");
return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT);
}
/* 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
/* 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)
@@ -285,6 +217,7 @@ static void rsl_cch_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr
cch = (struct abis_rsl_cchan_hdr *) msgb_push(msg, sizeof(*cch));
cch->c.msg_discr = ABIS_RSL_MDISC_COM_CHAN;
cch->c.msg_type = msg_type;
cch->ie_chan = RSL_IE_CHAN_NR;
cch->chan_nr = chan_nr;
}
@@ -409,16 +342,38 @@ int rsl_tx_ccch_load_ind_pch(struct gsm_bts *bts, uint16_t paging_avail)
{
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)
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);
msg->trx = bts->c0;
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 */
static int rsl_rx_paging_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
{
@@ -446,30 +401,11 @@ static int rsl_rx_paging_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
/* FIXME: notfiy the BSC somehow ?*/
}
pcu_tx_pag_req(identity_lv, chan_needed);
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 */
static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg)
{
@@ -499,8 +435,8 @@ static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg)
if (len > sizeof(sysinfo_buf_t)-2)
len = sizeof(sysinfo_buf_t)-2;
bts->si_valid |= (1 << osmo_si);
bts->si_buf[osmo_si][0] = 0x00;
bts->si_buf[osmo_si][1] = 0x03;
bts->si_buf[osmo_si][0] = 0x03; /* C/R + EA */
bts->si_buf[osmo_si][1] = 0x03; /* UI frame */
memcpy(bts->si_buf[osmo_si]+2,
TLVP_VAL(&tp, RSL_IE_L3_INFO), len);
LOGP(DRSL, LOGL_INFO, " Rx RSL SACCH FILLING (SI%s)\n",
@@ -544,14 +480,18 @@ static int rsl_rx_imm_ass(struct gsm_bts_trx *trx, struct msgb *msg)
* 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)
{
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(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);
msg->trx = lchan->ts->trx;
@@ -561,12 +501,16 @@ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
/* 8.4.2 sending CHANnel ACTIVation ACKnowledge */
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime)
{
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 ie[2];
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);
msgb_tv_fixed_put(msg, RSL_IE_FRAME_NUMBER, 2, ie);
rsl_dch_push_hdr(msg, RSL_MT_CHAN_ACTIV_ACK, chan_nr);
@@ -579,20 +523,41 @@ int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime)
}
/* 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);
uint8_t chan_nr = dch->chan_nr;
struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
LOGP(DRSL, LOGL_NOTICE, "Sending Channel Activated NACK: cause = 0x%02x\n", cause);
msg->len = 0;
msg->data = msg->tail = msg->l3h;
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
/* 9.3.26 Cause */
msgb_tlv_put(msg, RSL_IE_CAUSE, 1, &cause);
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);
}
/* 8.4.4 sending CONNection FAILure */
int rsl_tx_conn_fail(struct gsm_lchan *lchan, uint8_t cause)
{
struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
LOGP(DRSL, LOGL_NOTICE, "Sending Connection Failure: cause = 0x%02x\n", cause);
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
/* 9.3.26 Cause */
msgb_tlv_put(msg, RSL_IE_CAUSE, 1, &cause);
rsl_dch_push_hdr(msg, RSL_MT_CONN_FAIL, chan_nr);
msg->trx = lchan->ts->trx;
return abis_rsl_sendmsg(msg);
}
@@ -650,6 +615,14 @@ static void copy_sacch_si_to_lchan(struct gsm_lchan *lchan)
static int encr_info2lchan(struct gsm_lchan *lchan,
const uint8_t *val, uint8_t len)
{
int rc;
struct gsm_bts_role_bts *btsb = bts_role_bts(lchan->ts->trx->bts);
/* check if the encryption algorithm sent by BSC is supported! */
rc = bts_supports_cipher(btsb, *val);
if (rc != 1)
return rc;
/* length can be '1' in case of no ciphering */
if (len < 1)
return -EINVAL;
@@ -672,21 +645,26 @@ static int rsl_rx_chan_activ(struct msgb *msg)
struct tlv_parsed tp;
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));
/* 9.3.3 Activation Type */
if (!TLVP_PRESENT(&tp, RSL_IE_ACT_TYPE)) {
LOGP(DRSL, LOGL_NOTICE, "missing Activation Type\n");
msgb_free(msg);
return rsl_tx_chan_nack(msg->trx, msg, RSL_ERR_MAND_IE_ERROR);
return rsl_tx_chan_act_nack(lchan, RSL_ERR_MAND_IE_ERROR);
}
type = *TLVP_VAL(&tp, RSL_IE_ACT_TYPE);
/* 9.3.6 Channel Mode */
if (!TLVP_PRESENT(&tp, RSL_IE_CHAN_MODE)) {
LOGP(DRSL, LOGL_NOTICE, "missing Channel Mode\n");
msgb_free(msg);
return rsl_tx_chan_nack(msg->trx, msg, RSL_ERR_MAND_IE_ERROR);
return rsl_tx_chan_act_nack(lchan, RSL_ERR_MAND_IE_ERROR);
}
cm = (struct rsl_ie_chan_mode *) TLVP_VAL(&tp, RSL_IE_CHAN_MODE);
lchan_tchmode_from_cmode(lchan, cm);
@@ -698,7 +676,8 @@ static int rsl_rx_chan_activ(struct msgb *msg)
if (encr_info2lchan(lchan, val, len) < 0)
return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT);
}
} else
memset(&lchan->encr, 0, sizeof(lchan->encr));
/* 9.3.9 Handover Reference */
@@ -707,7 +686,7 @@ static int rsl_rx_chan_activ(struct msgb *msg)
lchan->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER);
/* 9.3.13 MS Power */
if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER))
lchan->bs_power = *TLVP_VAL(&tp, RSL_IE_MS_POWER);
lchan->ms_power = *TLVP_VAL(&tp, RSL_IE_MS_POWER);
/* 9.3.24 Timing Advance */
if (TLVP_PRESENT(&tp, RSL_IE_TIMING_ADVANCE))
lchan->rqd_ta = *TLVP_VAL(&tp, RSL_IE_TIMING_ADVANCE);
@@ -765,10 +744,10 @@ static int rsl_rx_chan_activ(struct msgb *msg)
}
memcpy(&lchan->mr_conf, TLVP_VAL(&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),
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
&lchan->tch.amr_mr);
amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
&lchan->tch.amr_mr);
}
/* 9.3.53 MultiRate Control */
/* 9.3.54 Supported Codec Types */
@@ -806,11 +785,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,
const char *imeisv)
{
struct msgb *fake_msg = rsl_msgb_alloc(128);
struct msgb *fake_msg;
struct gsm48_hdr *g48h;
uint8_t mid_buf[11];
int rc;
fake_msg = rsl_msgb_alloc(128);
if (!fake_msg)
return -ENOMEM;
/* generate 04.08 RR message */
g48h = (struct gsm48_hdr *) msgb_put(fake_msg, sizeof(*g48h));
g48h->proto_discr = GSM48_PDISC_RR;
@@ -887,8 +870,6 @@ static int rsl_rx_encr_cmd(struct msgb *msg)
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 */
link_id = *TLVP_VAL(&tp, RSL_IE_LINK_IDENT);
@@ -935,12 +916,16 @@ static int rsl_rx_encr_cmd(struct msgb *msg)
/* 8.4.11 MODE MODIFY NEGATIVE ACKNOWLEDGE */
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);
LOGP(DRSL, LOGL_NOTICE, "%s Tx MODE MODIFY NACK (cause = 0x%02x)\n",
gsm_lchan_name(lchan), cause);
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
msg->len = 0;
msg->data = msg->tail = msg->l3h;
@@ -955,11 +940,15 @@ static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause)
/* 8.4.10 MODE MODIFY ACK */
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);
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);
msg->trx = lchan->ts->trx;
@@ -1004,10 +993,10 @@ static int rsl_rx_mode_modif(struct msgb *msg)
}
memcpy(&lchan->mr_conf, TLVP_VAL(&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),
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
&lchan->tch.amr_mr);
amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
&lchan->tch.amr_mr);
}
/* 9.3.53 MultiRate Control */
/* 9.3.54 Supported Codec Types */
@@ -1082,12 +1071,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",
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)
return -ENOMEM;
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;
@@ -1098,7 +1087,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,
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);
uint32_t *att_ip;
const char *name;
@@ -1117,6 +1106,11 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
LOGPC(DRSL, LOGL_INFO, "remote %s:%u)\n",
inet_ntoa(ia), lchan->abis_ip.connect_port);
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
/* Connection ID */
msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, htons(lchan->abis_ip.conn_id));
@@ -1144,12 +1138,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)
{
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
struct msgb *msg;
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
LOGP(DRSL, LOGL_INFO, "%s RSL Tx IPAC_DLCX_ACK\n",
gsm_lchan_name(lchan));
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
if (inc_conn_id)
msgb_tv_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
@@ -1162,12 +1160,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,
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);
LOGP(DRSL, LOGL_INFO, "%s RSL Tx IPAC_DLCX_NACK\n",
gsm_lchan_name(lchan));
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
if (inc_conn_id)
msgb_tv_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
@@ -1185,13 +1187,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,
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);
/* FIXME: allocate new msgb and copy old over */
LOGP(DRSL, LOGL_NOTICE, "%s RSL Tx IPAC_BIND_NACK\n",
gsm_lchan_name(lchan));
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
if (!msg)
return -ENOMEM;
if (inc_ipport) {
uint32_t *att_ip;
/* remote IP */
@@ -1214,6 +1220,27 @@ static int tx_ipac_XXcx_nack(struct gsm_lchan *lchan, uint8_t cause,
return abis_rsl_sendmsg(msg);
}
static char *get_rsl_local_ip(struct gsm_bts_trx *trx)
{
struct sockaddr_storage ss;
socklen_t sa_len = sizeof(ss);
static char hostbuf[256];
int rc;
rc = getsockname(trx->rsl_link->bfd.fd, (struct sockaddr *) &ss,
&sa_len);
if (rc < 0)
return NULL;
rc = getnameinfo((struct sockaddr *)&ss, sa_len,
hostbuf, sizeof(hostbuf), NULL, 0,
NI_NUMERICHOST);
if (rc < 0)
return NULL;
return hostbuf;
}
static int rsl_rx_ipac_XXcx(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
@@ -1223,7 +1250,7 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
const uint8_t *payload_type, *speech_mode, *payload_type2;
const uint32_t *connect_ip;
const uint16_t *connect_port;
int rc, inc_ip_port = 0;
int rc, inc_ip_port = 0, port;
char *name;
if (dch->c.msg_type == RSL_MT_IPAC_CRCX)
@@ -1255,6 +1282,7 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
}
if (dch->c.msg_type == RSL_MT_IPAC_CRCX) {
char *ipstr = NULL;
if (lchan->abis_ip.rtp_socket) {
LOGP(DRSL, LOGL_ERROR, "%s Rx RSL IPAC CRCX, "
"but we already have socket!\n",
@@ -1273,11 +1301,28 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
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->rx_cb = &bts_model_rtp_rx_cb;
if (connect_ip && connect_port) {
/* if CRCX specifies a remote IP, we can bind()
* here to 0.0.0.0 and wait for the connect()
* below, after which the kernel will have
* selected the local IP address. */
ipstr = "0.0.0.0";
} else {
/* if CRCX does not specify a remote IP, we will
* not do any connect() below, and thus the
* local socket will remain bound to 0.0.0.0 -
* which however we cannot legitimately report
* back to the BSC in the CRCX_ACK */
ipstr = get_rsl_local_ip(lchan->ts->trx);
}
rc = osmo_rtp_socket_bind(lchan->abis_ip.rtp_socket,
btsb->rtp_bind_host, -1);
ipstr, -1);
if (rc < 0) {
LOGP(DRSL, LOGL_ERROR,
"%s IPAC Failed to bind RTP/RTCP sockets\n",
@@ -1288,13 +1333,6 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
inc_ip_port, dch->c.msg_type);
}
rc = osmo_rtp_get_bound_ip_port(lchan->abis_ip.rtp_socket,
&lchan->abis_ip.bound_ip,
&lchan->abis_ip.bound_port);
if (rc < 0)
LOGP(DRSL, LOGL_ERROR, "%s IPAC cannot obtain "
"locally bound IP/port: %d\n",
gsm_lchan_name(lchan), rc);
/* FIXME: multiplex connection, BSC proxy */
} else {
/* MDCX */
@@ -1309,6 +1347,7 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
if (connect_ip && connect_port) {
struct in_addr ia;
/* Special rule: If connect_ip == 0.0.0.0, use RSL IP
* address */
if (*connect_ip == 0) {
@@ -1332,9 +1371,20 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
/* save IP address and port number */
lchan->abis_ip.connect_ip = ntohl(ia.s_addr);
lchan->abis_ip.connect_port = ntohs(*connect_port);
} else {
/* FIXME: discard all codec frames */
}
rc = osmo_rtp_get_bound_ip_port(lchan->abis_ip.rtp_socket,
&lchan->abis_ip.bound_ip,
&port);
if (rc < 0)
LOGP(DRSL, LOGL_ERROR, "%s IPAC cannot obtain "
"locally bound IP/port: %d\n",
gsm_lchan_name(lchan), rc);
lchan->abis_ip.bound_port = port;
/* Everything has succeeded, we can store new values in lchan */
if (payload_type) {
lchan->abis_ip.rtp_payload = *payload_type;
@@ -1359,7 +1409,6 @@ static int rsl_rx_ipac_XXcx(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 gsm_lchan *lchan = msg->lchan;
int rc, inc_conn_id = 0;
@@ -1398,7 +1447,8 @@ static int rsl_rx_rll(struct gsm_bts_trx *trx, struct msgb *msg)
if (!lchan) {
LOGP(DRLL, LOGL_NOTICE, "Rx RLL %s for unknown lchan\n",
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),
@@ -1454,11 +1504,15 @@ static int rslms_is_meas_rep(struct msgb *msg)
/* 8.4.8 MEASUREMENT RESult */
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);
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++);
if (lchan->meas.flags & LC_UL_M_F_RES_VALID) {
uint8_t meas_res[16];
@@ -1525,7 +1579,8 @@ static int rsl_rx_cchan(struct gsm_bts_trx *trx, struct msgb *msg)
if (!msg->lchan) {
LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n",
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),
@@ -1576,7 +1631,8 @@ static int rsl_rx_dchan(struct gsm_bts_trx *trx, struct msgb *msg)
if (!msg->lchan) {
LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n",
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),
@@ -1666,7 +1722,8 @@ static int rsl_rx_ipaccess(struct gsm_bts_trx *trx, struct msgb *msg)
if (!msg->lchan) {
LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n",
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),

View File

@@ -24,23 +24,103 @@
#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)
{
/* 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) {
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);
case 1:
/* A SI 2 message will be sent at least every time TC = 1. */
return GSM_BTS_SI(bts, SYSINFO_TYPE_2);
case 2:
return GSM_BTS_SI(bts, SYSINFO_TYPE_3);
case 3:
return GSM_BTS_SI(bts, SYSINFO_TYPE_4);
case 4:
/* 2ter, 2quater, 9, 13 */
break;
/* iterate over 2ter, 2quater, 9, 13 */
/* 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:
/* 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;
case 6:
return GSM_BTS_SI(bts, SYSINFO_TYPE_3);

View File

@@ -29,6 +29,8 @@
#include <osmocom/vty/command.h>
#include <osmocom/vty/logging.h>
#include <osmocom/trau/osmo_ortp.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
@@ -45,6 +47,13 @@
enum node_type bts_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
case TRX_NODE:
vty->node = BTS_NODE;
{
struct gsm_bts_trx *trx = vty->index;
vty->index = trx->bts;
}
break;
default:
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)
{
switch (node) {
case TRX_NODE:
case BTS_NODE:
return 1;
default:
@@ -65,6 +75,13 @@ gDEFUN(ournode_exit, ournode_exit_cmd, "exit",
"Exit current node, go down to provious node")
{
switch (vty->node) {
case TRX_NODE:
vty->node = BTS_NODE;
{
struct gsm_bts_trx *trx = vty->index;
vty->index = trx->bts;
}
break;
default:
break;
}
@@ -85,19 +102,20 @@ gDEFUN(ournode_end, ournode_end_cmd, "end",
return CMD_SUCCESS;
}
struct vty_app_info bts_vty_info = {
.name = "OsmoBTS",
.version = PACKAGE_VERSION,
.go_parent_cb = bts_vty_go_parent,
.is_config_node = bts_vty_is_config_node,
};
const char *osmobts_copyright =
static const char osmobts_copyright[] =
"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"
"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";
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;
struct gsm_network *gsmnet_from_vty(struct vty *v)
@@ -126,9 +144,37 @@ static struct cmd_node bts_node = {
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)
{
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);
if (bts->description)
@@ -137,10 +183,22 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
vty_out(vty, " ipa unit-id %u %u%s",
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, " rtp bind-ip %s%s", btsb->rtp_bind_host, VTY_NEWLINE);
vty_out(vty, " rtp jitter-buffer %u%s", btsb->rtp_jitter_buf_ms,
VTY_NEWLINE);
vty_out(vty, " paging queue-size %u%s", paging_get_queue_max(btsb->paging_state),
VTY_NEWLINE);
vty_out(vty, " paging lifetime %u%s", paging_get_lifetime(btsb->paging_state),
VTY_NEWLINE);
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_bts *bts;
@@ -151,6 +209,11 @@ int config_write_bts(struct vty *vty)
return CMD_SUCCESS;
}
static int config_write_dummy(struct vty *vty)
{
return CMD_SUCCESS;
}
/* per-BTS configuration */
DEFUN(cfg_bts,
cfg_bts_cmd,
@@ -180,7 +243,9 @@ DEFUN(cfg_bts,
DEFUN(cfg_bts_unit_id,
cfg_bts_unit_id_cmd,
"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;
int site_id = atoi(argv[0]);
@@ -195,7 +260,15 @@ DEFUN(cfg_bts_unit_id,
DEFUN(cfg_bts_band,
cfg_bts_band_cmd,
"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;
int band = gsm_band_parse(argv[0]);
@@ -227,21 +300,63 @@ DEFUN(cfg_bts_oml_ip,
return CMD_SUCCESS;
}
DEFUN(cfg_bts_rtp_bind_ip,
#define RTP_STR "RTP parameters\n"
DEFUN_HIDDEN(cfg_bts_rtp_bind_ip,
cfg_bts_rtp_bind_ip_cmd,
"rtp bind-ip A.B.C.D",
"RTP Parameters\n" "RTP local bind IP Address\n" "RTP local bind IP Address\n")
RTP_STR "RTP local bind IP Address\n" "RTP local bind IP Address\n")
{
vty_out(vty, "%% rtp bind-ip is now deprecated%s", VTY_NEWLINE);
return CMD_WARNING;
}
DEFUN(cfg_bts_rtp_jitbuf,
cfg_bts_rtp_jitbuf_cmd,
"rtp jitter-buffer <0-10000>",
RTP_STR "RTP jitter buffer\n" "jitter buffer in ms\n")
{
struct gsm_bts *bts = vty->index;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
if (btsb->rtp_bind_host)
talloc_free(btsb->rtp_bind_host);
btsb->rtp_bind_host = talloc_strdup(btsb, argv[0]);
btsb->rtp_jitter_buf_ms = atoi(argv[0]);
return CMD_SUCCESS;
}
#define PAG_STR "Paging related parameters\n"
DEFUN(cfg_bts_paging_queue_size,
cfg_bts_paging_queue_size_cmd,
"paging queue-size <1-1024>",
PAG_STR "Maximum length of BTS-internal paging queue\n"
"Maximum length of BTS-internal paging queue\n")
{
struct gsm_bts *bts = vty->index;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
paging_set_queue_max(btsb->paging_state, atoi(argv[0]));
return CMD_SUCCESS;
}
DEFUN(cfg_bts_paging_lifetime,
cfg_bts_paging_lifetime_cmd,
"paging lifetime <0-60>",
PAG_STR "Maximum lifetime of a paging record\n"
"Maximum lifetime of a paging record (secods)\n")
{
struct gsm_bts *bts = vty->index;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
paging_set_lifetime(btsb->paging_state, atoi(argv[0]));
return CMD_SUCCESS;
}
/* ======================================================================
* SHOW
* ======================================================================*/
@@ -255,6 +370,8 @@ static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms)
static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts->role;
vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, "
"BSIC %u, TSC %u and %u TRX%s",
bts->nr, "FIXME", gsm_band_name(bts->band),
@@ -270,6 +387,9 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
net_dump_nmstate(vty, &bts->mo.nm_state);
vty_out(vty, " Site Mgr NM State: ");
net_dump_nmstate(vty, &bts->site_mgr.mo.nm_state);
vty_out(vty, " Paging: Queue size %u, occupied %u, lifetime %us%s",
paging_get_queue_max(btsb->paging_state), paging_queue_length(btsb->paging_state),
paging_get_lifetime(btsb->paging_state), VTY_NEWLINE);
#if 0
vty_out(vty, " Paging: %u pending requests, %u free slots%s",
paging_pending_requests_nr(bts),
@@ -316,6 +436,70 @@ DEFUN(show_bts, show_bts_cmd, "show bts <0-255>",
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)
{
@@ -329,7 +513,19 @@ int bts_vty_init(const struct log_info *cat)
install_element(BTS_NODE, &cfg_bts_unit_id_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_jitbuf_cmd);
install_element(BTS_NODE, &cfg_bts_band_cmd);
install_element(BTS_NODE, &cfg_description_cmd);
install_element(BTS_NODE, &cfg_no_description_cmd);
install_element(BTS_NODE, &cfg_bts_paging_queue_size_cmd);
install_element(BTS_NODE, &cfg_bts_paging_lifetime_cmd);
/* 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

@@ -1,10 +1,14 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include
INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
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 femtobts.h hw_misc.h l1_fwd.h l1_if.h \
l1_transp.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_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
@@ -14,3 +18,5 @@ sysmobts_remote_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
l1fwd_proxy_SOURCES = l1_fwd_main.c l1_transp_hw.c
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,270 @@
/* 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/gsm_data.h>
#include <osmo-bts/logging.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1const.h>
#include "l1_if.h"
#include "femtobts.h"
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;
}
static int calib_file_read(const char *path, const struct calib_file_desc *desc,
SuperFemto_Prim_t *prim)
{
FILE *in;
char fname[PATH_MAX];
int i;
fname[0] = '\0';
snprintf(fname, sizeof(fname)-1, "%s/%s", path, desc->fname);
fname[sizeof(fname)-1] = '\0';
in = fopen(fname, "r");
if (!in) {
LOGP(DL1C, LOGL_ERROR,
"Failed to open '%s' for calibration data.\n", fname);
return -1;
}
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,4,0)
if (desc->rx) {
SuperFemto_SetRxCalibTblReq_t *rx = &prim->u.setRxCalibTblReq;
memset(rx, 0, sizeof(*rx));
prim->id = SuperFemto_PrimId_SetRxCalibTblReq;
rx->freqBand = desc->band;
rx->bUplink = desc->uplink;
rx->fExtRxGain = read_float(in);
rx->fRxMixGainCorr = read_float(in);
for (i = 0; i < ARRAY_SIZE(rx->fRxLnaGainCorr); i++)
rx->fRxLnaGainCorr[i] = read_float(in);
for (i = 0; i < arrsize_by_band[desc->band]; i++)
rx->fRxRollOffCorr[i] = read_float(in);
if (desc->uplink) {
rx->u8IqImbalMode = read_int(in);
printf("%s: u8IqImbalMode=%d\n", desc->fname, rx->u8IqImbalMode);
for (i = 0; i < ARRAY_SIZE(rx->u16IqImbalCorr); i++)
rx->u16IqImbalCorr[i] = read_int(in);
}
} else {
SuperFemto_SetTxCalibTblReq_t *tx = &prim->u.setTxCalibTblReq;
memset(tx, 0, sizeof(*tx));
prim->id = SuperFemto_PrimId_SetTxCalibTblReq;
tx->freqBand = desc->band;
for (i = 0; i < ARRAY_SIZE(tx->fTxGainGmsk); i++)
tx->fTxGainGmsk[i] = read_float(in);
tx->fTx8PskCorr = read_float(in);
for (i = 0; i < ARRAY_SIZE(tx->fTxExtAttCorr); i++)
tx->fTxExtAttCorr[i] = read_float(in);
for (i = 0; i < arrsize_by_band[desc->band]; i++)
tx->fTxRollOffCorr[i] = read_float(in);
}
#else
#warning Format of calibration tables before API version 2.4.0 not supported
#endif
fclose(in);
return 0;
}
/* iteratively download the calibration data into the L1 */
static int calib_send_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg);
/* send the calibration table for a single specified file */
static int calib_file_send(struct femtol1_hdl *fl1h,
const struct calib_file_desc *desc)
{
struct 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, calib_send_compl_cb);
}
/* completion callback after every SetCalibTbl is confirmed */
static int calib_send_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
struct calib_send_state *st = &fl1h->st;
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(fl1h,
&calib_files[st->last_file_idx]);
LOGP(DL1C, LOGL_INFO, "L1 calibration table loading complete!\n");
return 0;
}
int calib_load(struct femtol1_hdl *fl1h)
{
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
LOGP(DL1C, LOGL_ERROR, "L1 calibration is not supported on pre 2.4.0 firmware.\n");
return -1;
#else
return calib_file_send(fl1h, &calib_files[0]);
#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/gsml1dbg.h>
@@ -91,48 +91,64 @@ const GsmL1_PrimId_t femtobts_l1prim_req2conf[GsmL1_PrimId_NUM] = {
[GsmL1_PrimId_MphMeasureReq] = GsmL1_PrimId_MphMeasureCnf,
};
const enum l1prim_type femtobts_sysprim_type[FemtoBts_PrimId_NUM] = {
[FemtoBts_PrimId_SystemInfoReq] = L1P_T_REQ,
[FemtoBts_PrimId_SystemInfoCnf] = L1P_T_CONF,
[FemtoBts_PrimId_SystemFailureInd] = L1P_T_IND,
[FemtoBts_PrimId_ActivateRfReq] = L1P_T_REQ,
[FemtoBts_PrimId_ActivateRfCnf] = L1P_T_CONF,
[FemtoBts_PrimId_DeactivateRfReq] = L1P_T_REQ,
[FemtoBts_PrimId_DeactivateRfCnf] = L1P_T_CONF,
[FemtoBts_PrimId_SetTraceFlagsReq] = L1P_T_REQ,
[FemtoBts_PrimId_RfClockInfoReq] = L1P_T_REQ,
[FemtoBts_PrimId_RfClockInfoCnf] = L1P_T_CONF,
[FemtoBts_PrimId_RfClockSetupReq] = L1P_T_REQ,
[FemtoBts_PrimId_RfClockSetupCnf] = L1P_T_CONF,
[FemtoBts_PrimId_Layer1ResetReq] = L1P_T_REQ,
[FemtoBts_PrimId_Layer1ResetCnf] = L1P_T_CONF,
const enum l1prim_type femtobts_sysprim_type[SuperFemto_PrimId_NUM] = {
[SuperFemto_PrimId_SystemInfoReq] = L1P_T_REQ,
[SuperFemto_PrimId_SystemInfoCnf] = L1P_T_CONF,
[SuperFemto_PrimId_SystemFailureInd] = L1P_T_IND,
[SuperFemto_PrimId_ActivateRfReq] = L1P_T_REQ,
[SuperFemto_PrimId_ActivateRfCnf] = L1P_T_CONF,
[SuperFemto_PrimId_DeactivateRfReq] = L1P_T_REQ,
[SuperFemto_PrimId_DeactivateRfCnf] = L1P_T_CONF,
[SuperFemto_PrimId_SetTraceFlagsReq] = L1P_T_REQ,
[SuperFemto_PrimId_RfClockInfoReq] = L1P_T_REQ,
[SuperFemto_PrimId_RfClockInfoCnf] = L1P_T_CONF,
[SuperFemto_PrimId_RfClockSetupReq] = L1P_T_REQ,
[SuperFemto_PrimId_RfClockSetupCnf] = L1P_T_CONF,
[SuperFemto_PrimId_Layer1ResetReq] = L1P_T_REQ,
[SuperFemto_PrimId_Layer1ResetCnf] = L1P_T_CONF,
};
const struct value_string femtobts_sysprim_names[FemtoBts_PrimId_NUM+1] = {
{ FemtoBts_PrimId_SystemInfoReq, "SYSTEM-INFO.req" },
{ FemtoBts_PrimId_SystemInfoCnf, "SYSTEM-INFO.conf" },
{ FemtoBts_PrimId_SystemFailureInd, "SYSTEM-FAILURE.ind" },
{ FemtoBts_PrimId_ActivateRfReq, "ACTIVATE-RF.req" },
{ FemtoBts_PrimId_ActivateRfCnf, "ACTIVATE-RF.conf" },
{ FemtoBts_PrimId_DeactivateRfReq, "DEACTIVATE-RF.req" },
{ FemtoBts_PrimId_DeactivateRfCnf, "DEACTIVATE-RF.conf" },
{ FemtoBts_PrimId_SetTraceFlagsReq, "SET-TRACE-FLAGS.req" },
{ FemtoBts_PrimId_RfClockInfoReq, "RF-CLOCK-INFO.req" },
{ FemtoBts_PrimId_RfClockInfoCnf, "RF-CLOCK-INFO.conf" },
{ FemtoBts_PrimId_RfClockSetupReq, "RF-CLOCK-SETUP.req" },
{ FemtoBts_PrimId_RfClockSetupCnf, "RF-CLOCK-SETUP.conf" },
{ FemtoBts_PrimId_Layer1ResetReq, "LAYER1-RESET.req" },
{ FemtoBts_PrimId_Layer1ResetCnf, "LAYER1-RESET.conf" },
const struct value_string femtobts_sysprim_names[SuperFemto_PrimId_NUM+1] = {
{ SuperFemto_PrimId_SystemInfoReq, "SYSTEM-INFO.req" },
{ SuperFemto_PrimId_SystemInfoCnf, "SYSTEM-INFO.conf" },
{ SuperFemto_PrimId_SystemFailureInd, "SYSTEM-FAILURE.ind" },
{ SuperFemto_PrimId_ActivateRfReq, "ACTIVATE-RF.req" },
{ SuperFemto_PrimId_ActivateRfCnf, "ACTIVATE-RF.conf" },
{ SuperFemto_PrimId_DeactivateRfReq, "DEACTIVATE-RF.req" },
{ SuperFemto_PrimId_DeactivateRfCnf, "DEACTIVATE-RF.conf" },
{ SuperFemto_PrimId_SetTraceFlagsReq, "SET-TRACE-FLAGS.req" },
{ SuperFemto_PrimId_RfClockInfoReq, "RF-CLOCK-INFO.req" },
{ SuperFemto_PrimId_RfClockInfoCnf, "RF-CLOCK-INFO.conf" },
{ SuperFemto_PrimId_RfClockSetupReq, "RF-CLOCK-SETUP.req" },
{ SuperFemto_PrimId_RfClockSetupCnf, "RF-CLOCK-SETUP.conf" },
{ SuperFemto_PrimId_Layer1ResetReq, "LAYER1-RESET.req" },
{ 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 }
};
const FemtoBts_PrimId_t femtobts_sysprim_req2conf[FemtoBts_PrimId_NUM] = {
[FemtoBts_PrimId_SystemInfoReq] = FemtoBts_PrimId_SystemInfoCnf,
[FemtoBts_PrimId_ActivateRfReq] = FemtoBts_PrimId_ActivateRfCnf,
[FemtoBts_PrimId_DeactivateRfReq] = FemtoBts_PrimId_DeactivateRfCnf,
[FemtoBts_PrimId_RfClockInfoReq] = FemtoBts_PrimId_RfClockInfoCnf,
[FemtoBts_PrimId_RfClockSetupReq] = FemtoBts_PrimId_RfClockSetupCnf,
[FemtoBts_PrimId_Layer1ResetReq] = FemtoBts_PrimId_Layer1ResetCnf,
const SuperFemto_PrimId_t femtobts_sysprim_req2conf[SuperFemto_PrimId_NUM] = {
[SuperFemto_PrimId_SystemInfoReq] = SuperFemto_PrimId_SystemInfoCnf,
[SuperFemto_PrimId_ActivateRfReq] = SuperFemto_PrimId_ActivateRfCnf,
[SuperFemto_PrimId_DeactivateRfReq] = SuperFemto_PrimId_DeactivateRfCnf,
[SuperFemto_PrimId_RfClockInfoReq] = SuperFemto_PrimId_RfClockInfoCnf,
[SuperFemto_PrimId_RfClockSetupReq] = SuperFemto_PrimId_RfClockSetupCnf,
[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] = {
@@ -220,10 +236,42 @@ const struct value_string femtobts_tracef_names[29] = {
{ 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[] = {
{ GsmL1_TchPlType_NA, "N/A" },
{ GsmL1_TchPlType_Fr, "FR" },
{ GsmL1_TchPlType_Hr, "HR" },
{ GsmL1_TchPlType_Efr, "EFR" },
{ GsmL1_TchPlType_Amr, "AMR(IF2)" },
{ GsmL1_TchPlType_Amr_SidBad, "AMR(SID BAD)" },
{ 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)" },
{ 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,101 @@
#include <stdlib.h>
#include <osmocom/core/utils.h>
#include <sysmocom/femtobts/femtobts.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1const.h>
#ifdef FEMTOBTS_API_VERSION
#define SuperFemto_PrimId_t FemtoBts_PrimId_t
#define SuperFemto_Prim_t FemtoBts_Prim_t
#define SuperFemto_PrimId_SystemInfoReq FemtoBts_PrimId_SystemInfoReq
#define SuperFemto_PrimId_SystemInfoCnf FemtoBts_PrimId_SystemInfoCnf
#define SuperFemto_SystemInfoCnf_t FemtoBts_SystemInfoCnf_t
#define SuperFemto_PrimId_SystemFailureInd FemtoBts_PrimId_SystemFailureInd
#define SuperFemto_PrimId_ActivateRfReq FemtoBts_PrimId_ActivateRfReq
#define SuperFemto_PrimId_ActivateRfCnf FemtoBts_PrimId_ActivateRfCnf
#define SuperFemto_PrimId_DeactivateRfReq FemtoBts_PrimId_DeactivateRfReq
#define SuperFemto_PrimId_DeactivateRfCnf FemtoBts_PrimId_DeactivateRfCnf
#define SuperFemto_PrimId_SetTraceFlagsReq FemtoBts_PrimId_SetTraceFlagsReq
#define SuperFemto_PrimId_RfClockInfoReq FemtoBts_PrimId_RfClockInfoReq
#define SuperFemto_PrimId_RfClockInfoCnf FemtoBts_PrimId_RfClockInfoCnf
#define SuperFemto_PrimId_RfClockSetupReq FemtoBts_PrimId_RfClockSetupReq
#define SuperFemto_PrimId_RfClockSetupCnf FemtoBts_PrimId_RfClockSetupCnf
#define SuperFemto_PrimId_Layer1ResetReq FemtoBts_PrimId_Layer1ResetReq
#define SuperFemto_PrimId_Layer1ResetCnf FemtoBts_PrimId_Layer1ResetCnf
#define SuperFemto_PrimId_NUM FemtoBts_PrimId_NUM
#define HW_SYSMOBTS_V1 1
#define SUPERFEMTO_API(x,y,z) FEMTOBTS_API(x,y,z)
#endif
#ifdef L1_HAS_RTP_MODE
/* 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 {
L1P_T_REQ,
L1P_T_CONF,
L1P_T_IND,
};
#if !defined(SUPERFEMTO_API_VERSION) || SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,1,0)
enum uperfemto_clk_src {
SF_CLKSRC_NONE = 0,
SF_CLKSRC_OCXO = 1,
SF_CLKSRC_TCXO = 2,
SF_CLKSRC_EXT = 3,
SF_CLKSRC_GPS = 4,
SF_CLKSRC_TRX = 5,
SF_CLKSRC_RX = 6,
SF_CLKSRC_NL = 7,
};
#endif
const enum l1prim_type femtobts_l1prim_type[GsmL1_PrimId_NUM];
const struct value_string femtobts_l1prim_names[GsmL1_PrimId_NUM+1];
const GsmL1_PrimId_t femtobts_l1prim_req2conf[GsmL1_PrimId_NUM];
const enum l1prim_type femtobts_sysprim_type[FemtoBts_PrimId_NUM];
const struct value_string femtobts_sysprim_names[FemtoBts_PrimId_NUM+1];
const FemtoBts_PrimId_t femtobts_sysprim_req2conf[FemtoBts_PrimId_NUM];
const enum l1prim_type femtobts_sysprim_type[SuperFemto_PrimId_NUM];
const struct value_string femtobts_sysprim_names[SuperFemto_PrimId_NUM+1];
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_l1status_names[GSML1_STATUS_NUM+1];
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 */

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_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/gsm_data.h>
#include <sysmocom/femtobts/femtobts.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h>
#include <sysmocom/femtobts/gsml1const.h>
#include <sysmocom/femtobts/gsml1types.h>
@@ -55,11 +55,15 @@
static const uint16_t fwd_udp_ports[_NUM_MQ_WRITE] = {
[MQ_SYS_READ] = L1FWD_SYS_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 sockaddr_storage remote_sa;
socklen_t remote_sa_len;
struct sockaddr_storage remote_sa[_NUM_MQ_WRITE];
socklen_t remote_sa_len[_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 */
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;
/* 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 */
@@ -89,7 +93,7 @@ int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg)
/* data has arrived on the udp socket */
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 femtol1_hdl *fl1h = l1fh->fl1h;
int rc;
@@ -99,9 +103,9 @@ static int udp_read_cb(struct osmo_fd *ofd)
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,
(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) {
perror("read from udp");
msgb_free(msg);
@@ -113,14 +117,11 @@ static int udp_read_cb(struct osmo_fd *ofd)
}
msgb_put(msg, rc);
DEBUGP(DL1C, "UDP: Received %u bytes for %s queue\n", rc,
ofd->priv_nr == MQ_SYS_WRITE ? "SYS" : "L1");
DEBUGP(DL1C, "UDP: Received %u bytes for queue %d\n", rc,
ofd->priv_nr);
/* put the message into the right queue */
if (ofd->priv_nr == MQ_SYS_WRITE)
rc = osmo_wqueue_enqueue(&fl1h->write_q[MQ_SYS_WRITE], msg);
else
rc = osmo_wqueue_enqueue(&fl1h->write_q[MQ_L1_WRITE], msg);
rc = osmo_wqueue_enqueue(&fl1h->write_q[ofd->priv_nr], msg);
return rc;
}
@@ -131,11 +132,11 @@ static int udp_write_cb(struct osmo_fd *ofd, struct msgb *msg)
int rc;
struct l1fwd_hdl *l1fh = ofd->data;
DEBUGP(DL1C, "UDP: Writing %u bytes for %s queue\n", msgb_l1len(msg),
ofd->priv_nr == MQ_SYS_WRITE ? "SYS" : "L1");
DEBUGP(DL1C, "UDP: Writing %u bytes for queue %d\n", msgb_l1len(msg),
ofd->priv_nr);
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) {
LOGP(DL1C, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
strerror(errno));
@@ -155,8 +156,8 @@ int main(int argc, char **argv)
struct femtol1_hdl *fl1h;
int rc, i;
printf("sizeof(GsmL1_Prim_t) = %lu\n", sizeof(GsmL1_Prim_t));
printf("sizeof(FemtoBts_Prim_t) = %lu\n", sizeof(FemtoBts_Prim_t));
printf("sizeof(GsmL1_Prim_t) = %zu\n", sizeof(GsmL1_Prim_t));
printf("sizeof(SuperFemto_Prim_t) = %zu\n", sizeof(SuperFemto_Prim_t));
bts_log_init(NULL);
@@ -165,9 +166,11 @@ int main(int argc, char **argv)
INIT_LLIST_HEAD(&fl1h->wlc_list);
/* open the actual hardware transport */
rc = l1if_transport_open(fl1h);
if (rc < 0)
exit(1);
for (i = 0; i < ARRAY_SIZE(fl1h->write_q); i++) {
rc = l1if_transport_open(i, fl1h);
if (rc < 0)
exit(1);
}
/* create our fwd handle */
l1fh = talloc_zero(NULL, struct l1fwd_hdl);
@@ -176,7 +179,7 @@ int main(int argc, char **argv)
fl1h->priv = l1fh;
/* 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];
osmo_wqueue_init(wq, 10);

View File

@@ -32,6 +32,8 @@
#include <osmocom/core/select.h>
#include <osmocom/core/timer.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/lapdm.h>
@@ -40,11 +42,13 @@
#include <osmo-bts/logging.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/oml.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/paging.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/gsml1const.h>
#include <sysmocom/femtobts/gsml1types.h>
@@ -52,11 +56,106 @@
#include "femtobts.h"
#include "l1_if.h"
#include "l1_transp.h"
#include "hw_misc.h"
extern int pcu_direct;
/* FIXME: make threshold configurable */
#define MIN_QUAL_RACH 5.0f /* at least 5 dB C/I */
#define MIN_QUAL_NORM -0.5f /* at least -1 dB C/I */
/* mapping from femtobts 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,
data_ind->measParam.fRssi,
data_ind->measParam.fLinkQuality,
data_ind->msgUnitParam.u8Buffer + skip,
data_ind->msgUnitParam.u8Size - skip);
}
}
struct wait_l1_conf {
struct llist_head list; /* internal linked list */
struct osmo_timer_list timer; /* timer for L1 timeout */
@@ -85,9 +184,8 @@ static void l1if_req_timeout(void *data)
exit(23);
}
/* send a request primitive to the L1 and schedule completion call-back */
int l1if_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
int is_system_prim, l1if_compl_cb *cb, void *data)
static int _l1if_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
int is_system_prim, l1if_compl_cb *cb)
{
struct wait_l1_conf *wlc;
struct osmo_wqueue *wqueue;
@@ -96,7 +194,7 @@ int l1if_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
/* allocate new wsc and store reference to mutex and conf_id */
wlc = talloc_zero(fl1h, struct wait_l1_conf);
wlc->cb = cb;
wlc->cb_data = data;
wlc->cb_data = NULL;
/* Make sure we actually have received a REQUEST type primitive */
if (is_system_prim == 0) {
@@ -116,7 +214,7 @@ int l1if_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
wqueue = &fl1h->write_q[MQ_L1_WRITE];
timeout_secs = 30;
} else {
FemtoBts_Prim_t *sysp = msgb_sysprim(msg);
SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
LOGP(DL1C, LOGL_INFO, "Tx SYS prim %s\n",
get_value_string(femtobts_sysprim_names, sysp->id));
@@ -137,7 +235,7 @@ int l1if_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
osmo_wqueue_enqueue(wqueue, msg);
llist_add(&wlc->list, &fl1h->wlc_list);
/* schedule a timer for 10 seconds. If DSP fails to respond, we terminate */
/* schedule a timer for timeout_secs seconds. If DSP fails to respond, we terminate */
wlc->timer.data = wlc;
wlc->timer.cb = l1if_req_timeout;
osmo_timer_schedule(&wlc->timer, timeout_secs, 0);
@@ -145,6 +243,19 @@ int l1if_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
return 0;
}
/* send a request primitive to the L1 and schedule completion call-back */
int l1if_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
l1if_compl_cb *cb)
{
return _l1if_req_compl(fl1h, msg, 1, cb);
}
int l1if_gsm_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
l1if_compl_cb *cb)
{
return _l1if_req_compl(fl1h, msg, 0, cb);
}
/* allocate a msgb containing a GsmL1_Prim_t */
struct msgb *l1p_msgb_alloc(void)
{
@@ -156,13 +267,13 @@ struct msgb *l1p_msgb_alloc(void)
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 *msg = msgb_alloc(sizeof(FemtoBts_Prim_t), "sys_prim");
struct msgb *msg = msgb_alloc(sizeof(SuperFemto_Prim_t), "sys_prim");
if (msg)
msg->l1h = msgb_put(msg, sizeof(FemtoBts_Prim_t));
msg->l1h = msgb_put(msg, sizeof(SuperFemto_Prim_t));
return msg;
}
@@ -210,15 +321,48 @@ get_lapdm_chan_by_hl2(struct gsm_bts_trx *trx, uint32_t hLayer2)
{
struct gsm_lchan *lchan;
lchan = l1if_hLayer2_to_lchan(trx, hLayer2);
lchan = l1if_hLayer_to_lchan(trx, hLayer2);
if (!lchan)
return NULL;
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] = {
0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
0x03, 0x03, 0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
0x2B, 0x2B, 0x2B
};
@@ -232,7 +376,6 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
struct msgb *resp_msg;
GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param;
struct lapdm_channel *lc;
struct lapdm_entity *le;
struct gsm_lchan *lchan;
struct gsm_time g_time;
@@ -255,11 +398,11 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
case GsmL1_Sapi_TchF:
case GsmL1_Sapi_TchH:
/* resolve the L2 entity using rts_ind->hLayer2 */
lchan = l1if_hLayer2_to_lchan(trx, rts_ind->hLayer2);
lchan = l1if_hLayer_to_lchan(trx, rts_ind->hLayer2);
if (!lchan)
break;
if (lchan->abis_ip.rtp_socket) {
if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
/* FIXME: we _assume_ that we never miss TDMA
* frames and that we always get to this point
@@ -274,7 +417,7 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
/* if there is none, try to generate empty TCH frame
* like AMR SID_BAD */
if (!resp_msg) {
LOGP(DL1C, LOGL_NOTICE, "%s DL TCH Tx queue underrun\n",
LOGP(DL1C, LOGL_DEBUG, "%s DL TCH Tx queue underrun\n",
gsm_lchan_name(lchan));
resp_msg = gen_empty_tch_msg(lchan);
/* if there really is none, break here and send empty */
@@ -287,6 +430,13 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
/* actually transmit it */
goto tx;
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:
break;
}
@@ -321,8 +471,12 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
break;
case GsmL1_Sapi_Sacch:
/* resolve the L2 entity using rts_ind->hLayer2 */
lchan = l1if_hLayer2_to_lchan(trx, rts_ind->hLayer2);
lchan = l1if_hLayer_to_lchan(trx, rts_ind->hLayer2);
le = &lchan->lapdm_ch.lapdm_acch;
/* if the DSP is taking care of power control
* (ul_power_target==0), then this value will be
* overridden. */
msu_param->u8Buffer[0] = lchan->ms_power;
rc = lapdm_phsap_dequeue_prim(le, &pp);
if (rc < 0) {
/* No SACCH data from LAPDM pending, send SACCH filling */
@@ -331,7 +485,7 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
/* The +2 is empty space where the DSP inserts the L1 hdr */
memcpy(msu_param->u8Buffer+2, si, GSM_MACBLOCK_LEN-2);
} else
memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
memcpy(msu_param->u8Buffer+2, fill_frame, GSM_MACBLOCK_LEN-2);
} else {
/* The +2 is empty space where the DSP inserts the L1 hdr */
memcpy(msu_param->u8Buffer+2, pp.oph.msg->data, GSM_MACBLOCK_LEN-2);
@@ -340,13 +494,15 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
break;
case GsmL1_Sapi_Sdcch:
/* resolve the L2 entity using rts_ind->hLayer2 */
lc = get_lapdm_chan_by_hl2(trx, rts_ind->hLayer2);
le = &lc->lapdm_dcch;
lchan = l1if_hLayer_to_lchan(trx, rts_ind->hLayer2);
le = &lchan->lapdm_ch.lapdm_dcch;
rc = lapdm_phsap_dequeue_prim(le, &pp);
if (rc < 0)
memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
else {
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);
}
break;
@@ -374,22 +530,29 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
case GsmL1_Sapi_FacchF:
case GsmL1_Sapi_FacchH:
/* resolve the L2 entity using rts_ind->hLayer2 */
lc = get_lapdm_chan_by_hl2(trx, rts_ind->hLayer2);
le = &lc->lapdm_dcch;
lchan = l1if_hLayer_to_lchan(trx, rts_ind->hLayer2);
le = &lchan->lapdm_ch.lapdm_dcch;
rc = lapdm_phsap_dequeue_prim(le, &pp);
if (rc < 0)
goto empty_frame;
else {
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);
}
break;
/* we should never receive a request here */
case GsmL1_Sapi_Prach:
goto empty_frame;
break;
default:
memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
break;
}
tx:
tx_to_gsmtap(fl1, resp_msg);
/* transmit */
osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], resp_msg);
@@ -405,6 +568,15 @@ empty_frame:
static int handle_mph_time_ind(struct femtol1_hdl *fl1,
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 */
gsm_fn2gsmtime(&fl1->gsm_time, time_ind->u32Fn);
@@ -415,6 +587,19 @@ static int handle_mph_time_ind(struct femtol1_hdl *fl1,
/* increment the primitive count for the alive timer */
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;
}
@@ -439,9 +624,9 @@ static uint8_t gen_link_id(GsmL1_Sapi_t l1_sapi, uint8_t lapdm_sapi)
return c_bits | (lapdm_sapi & 7);
}
static void dump_meas_res(GsmL1_MeasParam_t *m)
static void dump_meas_res(int ll, GsmL1_MeasParam_t *m)
{
DEBUGPC(DL1C, ", Meas: RSSI %-3.2f dBm, Qual %-3.2f dB, "
LOGPC(DL1C, ll, ", Meas: RSSI %-3.2f dBm, Qual %-3.2f dB, "
"BER %-3.2f, Timing %d\n", m->fRssi, m->fLinkQuality,
m->fBer, m->i16BurstTiming);
}
@@ -450,6 +635,11 @@ static int process_meas_res(struct gsm_lchan *lchan, GsmL1_MeasParam_t *m)
{
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.ber10k = (unsigned int) (m->fBer * 100);
ulm.inv_rssi = (uint8_t) (m->fRssi * -1);
@@ -457,16 +647,48 @@ static int process_meas_res(struct gsm_lchan *lchan, GsmL1_MeasParam_t *m)
return lchan_new_ul_meas(lchan, &ulm);
}
/* process radio link timeout counter S */
static void radio_link_timeout(struct gsm_lchan *lchan, int bad_frame)
{
struct gsm_bts_role_bts *btsb = lchan->ts->trx->bts->role;
/* if link loss criterion already reached */
if (lchan->s == 0)
return;
if (bad_frame) {
/* count down radio link counter S */
lchan->s--;
DEBUGP(DMEAS, "counting down radio link counter S=%d\n",
lchan->s);
if (lchan->s == 0)
rsl_tx_conn_fail(lchan, RSL_ERR_RADIO_LINK_FAIL);
return;
}
if (lchan->s < btsb->radio_link_timeout) {
/* count up radio link counter S */
lchan->s += 2;
if (lchan->s > btsb->radio_link_timeout)
lchan->s = btsb->radio_link_timeout;
DEBUGP(DMEAS, "counting up radio link counter S=%d\n",
lchan->s);
}
}
static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_ind,
struct msgb *l1p_msg)
{
struct gsm_bts_trx *trx = fl1->priv;
struct osmo_phsap_prim pp;
struct gsm_lchan *lchan;
struct lapdm_entity *le;
struct msgb *msg;
int rc;
int rc = 0;
lchan = l1if_hLayer2_to_lchan(fl1->priv, data_ind->hLayer2);
ul_to_gsmtap(fl1, l1p_msg);
lchan = l1if_hLayer_to_lchan(fl1->priv, data_ind->hLayer2);
if (!lchan) {
LOGP(DL1C, LOGL_ERROR, "unable to resolve lchan by hLayer2\n");
return -ENODEV;
@@ -474,7 +696,8 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
process_meas_res(lchan, &data_ind->measParam);
if (data_ind->measParam.fLinkQuality < MIN_QUAL_NORM)
if (data_ind->measParam.fLinkQuality < MIN_QUAL_NORM
&& data_ind->msgUnitParam.u8Size != 0)
return 0;
DEBUGP(DL1C, "Rx PH-DATA.ind %s (hL2 %08x): %s",
@@ -482,20 +705,52 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
data_ind->hLayer2,
osmo_hexdump(data_ind->msgUnitParam.u8Buffer,
data_ind->msgUnitParam.u8Size));
dump_meas_res(&data_ind->measParam);
dump_meas_res(LOGL_DEBUG, &data_ind->measParam);
switch (data_ind->sapi) {
case GsmL1_Sapi_Sacch:
/* save the SACCH L1 header in the lchan struct for RSL MEAS RES */
if (data_ind->msgUnitParam.u8Size < 2)
radio_link_timeout(lchan, (data_ind->msgUnitParam.u8Size == 0));
if (data_ind->msgUnitParam.u8Size == 0)
break;
lchan->meas.l1_info[0] = data_ind->msgUnitParam.u8Buffer[0];
/* save the SACCH L1 header in the lchan struct for RSL MEAS RES */
if (data_ind->msgUnitParam.u8Size < 2) {
LOGP(DL1C, LOGL_NOTICE, "SACCH with size %u<2 !?!\n",
data_ind->msgUnitParam.u8Size);
break;
}
/* Some brilliant engineer decided that the ordering of
* fields on the Um interface is different from the
* order of fields in RLS. See TS 04.04 (Chapter 7.2)
* vs. TS 08.58 (Chapter 9.3.10). */
lchan->meas.l1_info[0] = data_ind->msgUnitParam.u8Buffer[0] << 3;
lchan->meas.l1_info[0] |= ((data_ind->msgUnitParam.u8Buffer[0] >> 5) & 1) << 2;
lchan->meas.l1_info[1] = data_ind->msgUnitParam.u8Buffer[1];
lchan->meas.flags |= LC_UL_M_F_L1_VALID;
/* fall-through */
case GsmL1_Sapi_Sdcch:
case GsmL1_Sapi_FacchF:
case GsmL1_Sapi_FacchH:
/* Check and Re-check for the SACCH */
if (data_ind->msgUnitParam.u8Size == 0) {
LOGP(DL1C, LOGL_NOTICE, "%s %s data is null.\n",
gsm_lchan_name(lchan),
get_value_string(femtobts_l1sapi_names, data_ind->sapi));
break;
}
/* 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 */
le = le_by_l1_sapi(&lchan->lapdm_ch, data_ind->sapi);
/* allocate and fill LAPDm primitive */
@@ -520,6 +775,30 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
/* TCH speech frame handling */
rc = l1if_tch_rx(lchan, l1p_msg);
break;
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
/* drop incomplete UL block */
if (!data_ind->msgUnitParam.u8Size
|| 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,
(int8_t) (data_ind->measParam.fRssi));
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,
(int8_t) (data_ind->measParam.fRssi));
break;
default:
LOGP(DL1C, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n",
get_value_string(femtobts_l1sapi_names, data_ind->sapi));
@@ -532,14 +811,27 @@ 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)
{
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 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)
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");
dump_meas_res(&ra_ind->measParam);
dump_meas_res(LOGL_DEBUG, &ra_ind->measParam);
lc = get_lapdm_chan_by_hl2(fl1->priv, ra_ind->hLayer2);
if (!lc) {
@@ -547,17 +839,31 @@ static int handle_ph_ra_ind(struct femtol1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind)
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,
PRIM_OP_INDICATION, NULL);
pp.u.rach_ind.ra = ra_ind->msgUnitParam.u8Buffer[0];
pp.u.rach_ind.fn = ra_ind->u32Fn;
/* FIXME: check for under/overflow / sign */
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;
pp.u.rach_ind.acc_delay = acc_delay;
return lapdm_phsap_up(&pp.oph, &lc->lapdm_dcch);
}
@@ -596,7 +902,7 @@ static int l1if_handle_ind(struct femtol1_hdl *fl1, struct msgb *msg)
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);
struct wait_l1_conf *wlc;
@@ -607,8 +913,8 @@ int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg)
/* silent, don't clog the log file */
break;
default:
LOGP(DL1P, LOGL_DEBUG, "Rx L1 prim %s\n",
get_value_string(femtobts_l1prim_names, l1p->id));
LOGP(DL1P, LOGL_DEBUG, "Rx L1 prim %s on queue %d\n",
get_value_string(femtobts_l1prim_names, l1p->id), wq);
}
/* check if this is a resposne to a sync-waiting request */
@@ -617,7 +923,12 @@ int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg)
* sending the same primitive */
if (wlc->is_sys_prim == 0 && l1p->id == wlc->conf_prim_id) {
llist_del(&wlc->list);
rc = wlc->cb(msg, wlc->cb_data);
if (wlc->cb)
rc = wlc->cb(fl1h->priv, msg);
else {
rc = 0;
msgb_free(msg);
}
release_wlc(wlc);
return rc;
}
@@ -629,7 +940,7 @@ int l1if_handle_l1prim(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;
int rc;
@@ -642,7 +953,12 @@ int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg)
* sending the same primitive */
if (wlc->is_sys_prim && sysp->id == wlc->conf_prim_id) {
llist_del(&wlc->list);
rc = wlc->cb(msg, wlc->cb_data);
if (wlc->cb)
rc = wlc->cb(fl1h->priv, msg);
else {
rc = 0;
msgb_free(msg);
}
release_wlc(wlc);
return rc;
}
@@ -667,16 +983,14 @@ int sysinfo_has_changed(struct gsm_bts *bts, int si)
}
#endif
static int activate_rf_compl_cb(struct msgb *resp, void *data)
static int activate_rf_compl_cb(struct gsm_bts_trx *trx, struct msgb *resp)
{
FemtoBts_Prim_t *sysp = msgb_sysprim(resp);
struct femtol1_hdl *fl1h = data;
struct gsm_bts_trx *trx = fl1h->priv;
SuperFemto_Prim_t *sysp = msgb_sysprim(resp);
GsmL1_Status_t status;
int on = 0;
unsigned int i;
if (sysp->id == FemtoBts_PrimId_ActivateRfCnf)
if (sysp->id == SuperFemto_PrimId_ActivateRfCnf)
on = 1;
if (on)
@@ -693,7 +1007,9 @@ static int activate_rf_compl_cb(struct msgb *resp, void *data)
LOGP(DL1C, LOGL_FATAL, "RF-ACT.conf with status %s\n",
get_value_string(femtobts_l1status_names, status));
bts_shutdown(trx->bts, "RF-ACT failure");
}
} else
sysmobts_led_set(LED_RF_ACTIVE, 1);
/* signal availability */
oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OK);
oml_mo_tx_sw_act_rep(&trx->mo);
@@ -703,6 +1019,7 @@ static int activate_rf_compl_cb(struct msgb *resp, void *data)
for (i = 0; i < ARRAY_SIZE(trx->ts); i++)
oml_mo_state_chg(&trx->ts[i].mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY);
} 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->bb_transc.mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
}
@@ -716,23 +1033,97 @@ static int activate_rf_compl_cb(struct msgb *resp, void *data)
int l1if_activate_rf(struct femtol1_hdl *hdl, int on)
{
struct msgb *msg = sysp_msgb_alloc();
FemtoBts_Prim_t *sysp = msgb_sysprim(msg);
SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
if (on) {
sysp->id = FemtoBts_PrimId_ActivateRfReq;
sysp->u.activateRfReq.u12ClkVc = 0xFFFF;
sysp->id = SuperFemto_PrimId_ActivateRfReq;
#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 {
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, activate_rf_compl_cb);
}
static int reset_compl_cb(struct msgb *resp, void *data)
/* call-back on arrival of DSP+FPGA version + band capability */
static int info_compl_cb(struct gsm_bts_trx *trx, struct msgb *resp)
{
struct femtol1_hdl *fl1h = data;
struct gsm_bts_trx *trx = fl1h->priv;
FemtoBts_Prim_t *sysp = msgb_sysprim(resp);
SuperFemto_Prim_t *sysp = msgb_sysprim(resp);
SuperFemto_SystemInfoCnf_t *sic = &sysp->u.systemInfoCnf;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
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, info_compl_cb);
}
static int reset_compl_cb(struct gsm_bts_trx *trx, struct msgb *resp)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
SuperFemto_Prim_t *sysp = msgb_sysprim(resp);
GsmL1_Status_t status = sysp->u.layer1ResetCnf.status;
LOGP(DL1C, LOGL_NOTICE, "Rx L1-RESET.conf (status=%s)\n",
@@ -751,6 +1142,17 @@ static int reset_compl_cb(struct msgb *resp, void *data)
* set them to zero (or whatever dsp_trace_f has been initialized to */
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,4,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 */
l1if_activate_rf(fl1h, 1);
@@ -760,22 +1162,22 @@ static int reset_compl_cb(struct msgb *resp, void *data)
int l1if_reset(struct femtol1_hdl *hdl)
{
struct msgb *msg = sysp_msgb_alloc();
FemtoBts_Prim_t *sysp = msgb_sysprim(msg);
sysp->id = FemtoBts_PrimId_Layer1ResetReq;
SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
sysp->id = SuperFemto_PrimId_Layer1ResetReq;
return l1if_req_compl(hdl, msg, 1, reset_compl_cb, hdl);
return l1if_req_compl(hdl, msg, reset_compl_cb);
}
/* set the trace flags within the DSP */
int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags)
{
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",
flags);
sysp->id = FemtoBts_PrimId_SetTraceFlagsReq;
sysp->id = SuperFemto_PrimId_SetTraceFlagsReq;
sysp->u.setTraceFlagsReq.u32Tf = flags;
hdl->dsp_trace_f = flags;
@@ -784,28 +1186,101 @@ int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags)
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 *fl1h;
int rc;
#ifndef HW_SYSMOBTS_V1
LOGP(DL1C, LOGL_INFO, "sysmoBTSv2 L1IF compiled against API headers "
"v%u.%u.%u\n", SUPERFEMTO_API_VERSION >> 16,
(SUPERFEMTO_API_VERSION >> 8) & 0xff,
SUPERFEMTO_API_VERSION & 0xff);
#else
LOGP(DL1C, LOGL_INFO, "sysmoBTSv1 L1IF compiled against API headers "
"v%u.%u.%u\n", FEMTOBTS_API_VERSION >> 16,
(FEMTOBTS_API_VERSION >> 8) & 0xff,
FEMTOBTS_API_VERSION & 0xff);
#endif
fl1h = talloc_zero(priv, struct femtol1_hdl);
if (!fl1h)
return NULL;
INIT_LLIST_HEAD(&fl1h->wlc_list);
fl1h->priv = priv;
fl1h->clk_cal = 0;
fl1h->ul_power_target = -75; /* dBm default */
/* 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) {
talloc_free(fl1h);
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;
}
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,26 +3,48 @@
#include <osmocom/core/select.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsm_utils.h>
enum {
MQ_SYS_READ,
MQ_L1_READ,
#ifndef HW_SYSMOBTS_V1
MQ_TCH_READ,
MQ_PDTCH_READ,
#endif
_NUM_MQ_READ
};
enum {
MQ_SYS_WRITE,
MQ_L1_WRITE,
#ifndef HW_SYSMOBTS_V1
MQ_TCH_WRITE,
MQ_PDTCH_WRITE,
#endif
_NUM_MQ_WRITE
};
struct calib_send_state {
const char *path;
int last_file_idx;
};
struct femtol1_hdl {
struct gsm_time gsm_time;
uint32_t hLayer1; /* handle to the L1 instance in the DSP */
uint32_t dsp_trace_f;
int clk_cal;
int ul_power_target;
uint8_t clk_src;
char *calib_path;
struct llist_head wlc_list;
struct gsmtap_inst *gsmtap;
uint32_t gsmtap_sapi_mask;
void *priv; /* user reference */
struct osmo_timer_list alive_timer;
@@ -30,31 +52,51 @@ struct femtol1_hdl {
struct osmo_fd read_ofd[_NUM_MQ_READ]; /* osmo file descriptors */
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;
struct calib_send_state st;
};
#define msgb_l1prim(msg) ((GsmL1_Prim_t *)(msg)->l1h)
#define msgb_sysprim(msg) ((FemtoBts_Prim_t *)(msg)->l1h)
#define msgb_sysprim(msg) ((SuperFemto_Prim_t *)(msg)->l1h)
typedef int l1if_compl_cb(struct msgb *l1_msg, void *data);
typedef int l1if_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg);
/* send a request primitive to the L1 and schedule completion call-back */
int l1if_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
int is_system_prim, l1if_compl_cb *cb, void *data);
l1if_compl_cb *cb);
int l1if_gsm_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
l1if_compl_cb *cb);
struct femtol1_hdl *l1if_open(void *priv);
int l1if_close(struct femtol1_hdl *hdl);
int l1if_reset(struct femtol1_hdl *hdl);
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_txpower(struct femtol1_hdl *fl1h, float tx_power);
struct msgb *l1p_msgb_alloc(void);
struct msgb *sysp_msgb_alloc(void);
uint32_t l1if_lchan_to_hLayer2(struct gsm_lchan *lchan);
struct gsm_lchan *l1if_hLayer2_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer2);
uint32_t l1if_lchan_to_hLayer(struct gsm_lchan *lchan);
struct gsm_lchan *l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer);
/* tch.c */
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
/* ciphering */
int l1if_set_ciphering(struct femtol1_hdl *fl1h,
struct gsm_lchan *lchan,
int dir_downlink);
/* calibration loading */
int calib_load(struct femtol1_hdl *fl1h);
#endif /* _FEMTO_L1_H */

View File

@@ -4,11 +4,11 @@
#include <osmocom/core/msgb.h>
/* 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);
/* functions exported by a transport */
int l1if_transport_open(struct femtol1_hdl *fl1h);
int l1if_transport_close(struct femtol1_hdl *fl1h);
int l1if_transport_open(int q, struct femtol1_hdl *fl1h);
int l1if_transport_close(int q, struct femtol1_hdl *fl1h);
#endif /* _FEMTOL1_TRANSP_H */

View File

@@ -43,7 +43,7 @@
#include <osmo-bts/logging.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/gsml1const.h>
#include <sysmocom/femtobts/gsml1types.h>
@@ -56,11 +56,15 @@
static const uint16_t fwd_udp_ports[] = {
[MQ_SYS_WRITE] = L1FWD_SYS_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)
{
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;
int rc;
@@ -83,7 +87,7 @@ static int fwd_read_cb(struct osmo_fd *ofd)
if (ofd->priv_nr == MQ_SYS_WRITE)
rc = l1if_handle_sysprim(fl1h, msg);
else
rc = l1if_handle_l1prim(fl1h, msg);
rc = l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
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);
}
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");
printf("sizeof(GsmL1_Prim_t) = %lu\n", sizeof(GsmL1_Prim_t));
printf("sizeof(FemtoBts_Prim_t) = %lu\n", sizeof(FemtoBts_Prim_t));
switch (q) {
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) {
fprintf(stderr, "You have to set the L1FWD_BTS_HOST environment variable\n");
exit(2);
}
for (i = 0; i < ARRAY_SIZE(fl1h->write_q); i++) {
struct osmo_wqueue *wq = &fl1h->write_q[i];
struct osmo_fd *ofd = &wq->bfd;
struct osmo_wqueue *wq = &fl1h->write_q[q];
struct osmo_fd *ofd = &wq->bfd;
osmo_wqueue_init(wq, 10);
wq->write_cb = prim_write_cb;
wq->read_cb = fwd_read_cb;
osmo_wqueue_init(wq, 10);
wq->write_cb = prim_write_cb;
wq->read_cb = fwd_read_cb;
ofd->data = fl1h;
ofd->priv_nr = i;
ofd->when |= BSC_FD_READ;
ofd->data = fl1h;
ofd->priv_nr = q;
ofd->when |= BSC_FD_READ;
rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
bts_host, fwd_udp_ports[i],
OSMO_SOCK_F_CONNECT);
if (rc < 0) {
talloc_free(fl1h);
return rc;
}
}
rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
bts_host, fwd_udp_ports[q],
OSMO_SOCK_F_CONNECT);
if (rc < 0)
return rc;
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++) {
struct osmo_wqueue *wq = &fl1h->write_q[i];
struct osmo_fd *ofd = &wq->bfd;
osmo_wqueue_clear(wq);
osmo_fd_unregister(ofd);
close(ofd->fd);
}
osmo_wqueue_clear(wq);
osmo_fd_unregister(ofd);
close(ofd->fd);
return 0;
}

View File

@@ -19,6 +19,7 @@
*
*/
#include <assert.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
@@ -36,7 +37,7 @@
#include <osmo-bts/logging.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/gsml1const.h>
#include <sysmocom/femtobts/gsml1types.h>
@@ -46,31 +47,57 @@
#include "l1_transp.h"
#ifdef HW_SYSMOBTS_V1
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/femtobts_dsp2arm"
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/femtobts_arm2dsp"
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_dsp2arm"
#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[] = {
[MQ_SYS_READ] = DEV_SYS_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[] = {
[MQ_SYS_WRITE] = DEV_SYS_ARM2DSP_NAME,
[MQ_L1_WRITE] = DEV_L1_ARM2DSP_NAME,
#ifndef HW_SYSMOBTS_V1
[MQ_TCH_WRITE] = DEV_TCH_ARM2DSP_NAME,
[MQ_PDTCH_WRITE]= DEV_PDTCH_ARM2DSP_NAME,
#endif
};
/*
* Make sure that all structs we read fit into the SYSMOBTS_PRIM_SIZE
*/
osmo_static_assert(sizeof(GsmL1_Prim_t) + 128 <= SYSMOBTS_PRIM_SIZE, l1_prim)
osmo_static_assert(sizeof(SuperFemto_Prim_t) + 128 <= SYSMOBTS_PRIM_SIZE, super_prim)
/* callback when there's something to read from the l1 msg_queue */
static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
{
//struct msgb *msg = l1p_msgb_alloc();
struct msgb *msg = msgb_alloc_headroom(2048, 128, "1l_fd");
struct msgb *msg = msgb_alloc_headroom(SYSMOBTS_PRIM_SIZE, 128, "1l_fd");
struct femtol1_hdl *fl1h = ofd->data;
int rc;
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 != -1)
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);
if (ofd->priv_nr == MQ_L1_WRITE)
return l1if_handle_l1prim(fl1h, msg);
else
switch (ofd->priv_nr) {
case MQ_SYS_WRITE:
if (rc != sizeof(SuperFemto_Prim_t))
LOGP(DL1C, LOGL_NOTICE, "%u != "
"sizeof(SuperFemto_Prim_t)\n", rc);
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 */
@@ -105,88 +150,73 @@ static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
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 */
for (i = 0; i < ARRAY_SIZE(hdl->read_ofd); i++) {
struct osmo_fd *ofd = &hdl->read_ofd[i];
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
struct osmo_wqueue *wq = &hdl->write_q[q];
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
rc = open(rd_devnames[i], O_RDONLY);
if (rc < 0) {
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue: %s\n",
strerror(errno));
goto out_free;
}
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;
}
rc = open(rd_devnames[q], O_RDONLY);
if (rc < 0) {
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue: %s\n",
strerror(errno));
return rc;
}
for (i = 0; i < ARRAY_SIZE(hdl->write_q); i++) {
struct osmo_wqueue *wq = &hdl->write_q[i];
struct osmo_fd *ofd = &hdl->write_q[i].bfd;
rc = open(wr_devnames[i], 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;
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;
}
read_ofd->fd = rc;
read_ofd->priv_nr = q;
read_ofd->data = hdl;
read_ofd->cb = l1if_fd_cb;
read_ofd->when = BSC_FD_READ;
rc = osmo_fd_register(read_ofd);
if (rc < 0) {
close(read_ofd->fd);
read_ofd->fd = -1;
return rc;
}
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;
out_read:
for (i = 0; i < ARRAY_SIZE(hdl->read_ofd); i++) {
close(hdl->read_ofd[i].fd);
osmo_fd_unregister(&hdl->read_ofd[i]);
}
out_free:
talloc_free(hdl);
close(hdl->read_ofd[q].fd);
osmo_fd_unregister(&hdl->read_ofd[q]);
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++) {
struct osmo_fd *ofd = &hdl->read_ofd[i];
osmo_fd_unregister(read_ofd);
close(read_ofd->fd);
read_ofd->fd = -1;
osmo_fd_unregister(ofd);
close(ofd->fd);
ofd->fd = -1;
}
osmo_fd_unregister(write_ofd);
close(write_ofd->fd);
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;
}

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 <errno.h>
#include <getopt.h>
#include <limits.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
@@ -39,18 +42,20 @@
#include <osmo-bts/abis.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/vty.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/pcu_if.h>
#define SYSMOBTS_RF_LOCK_PATH "/var/lock/bts_rf_lock"
#include "l1_if.h"
/* FIXME: read from real hardware */
const uint8_t abis_mac[6] = { 0,1,2,3,4,5 };
/* FIXME: generate from git */
const char *software_version = "0815";
int pcu_direct = 0;
static const char *config_file = "osmo-bts.cfg";
extern const char *osmobts_copyright;
static int daemonize = 0;
static unsigned int dsp_trace = 0;
static unsigned int dsp_trace = 0x71c00020;
int bts_model_init(struct gsm_bts *bts)
{
@@ -68,7 +73,7 @@ int bts_model_init(struct gsm_bts *bts)
l1if_reset(fl1h);
femtol1_vty_init(bts);
bts_model_vty_init(bts);
return 0;
}
@@ -102,10 +107,22 @@ static void print_help()
" -T --timestamp Prefix every log line with a timestamp\n"
" -V --version Print version information and exit\n"
" -e --log-level Set a global log-level\n"
" -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 */
static void handle_options(int argc, char **argv)
{
@@ -122,10 +139,12 @@ static void handle_options(int argc, char **argv)
{ "version", 0, 0, 'V' },
{ "log-level", 1, 0, 'e' },
{ "dsp-trace", 1, 0, 'p' },
{ "hw-version", 0, 0, 'w' },
{ "pcu-direct", 0, 0, 'M' },
{ 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);
if (c == -1)
break;
@@ -150,6 +169,9 @@ static void handle_options(int argc, char **argv)
case 'T':
log_set_print_timestamp(osmo_stderr_target, 1);
break;
case 'M':
pcu_direct = 1;
break;
case 'V':
print_version(1);
exit(0);
@@ -160,6 +182,10 @@ static void handle_options(int argc, char **argv)
case 'p':
dsp_trace = strtoul(optarg, NULL, 16);
break;
case 'w':
print_hwversion();
exit(0);
break;
default:
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)
{
struct stat st;
struct gsm_bts_role_bts *btsb;
struct ipabis_link *link;
void *tall_msgb_ctx;
@@ -203,14 +249,15 @@ int main(int argc, char **argv)
vty_init(&bts_vty_info);
bts_vty_init(&bts_log_info);
handle_options(argc, argv);
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);
handle_options(argc, argv);
btsb->support.ciphers = CIPHER_A5(1) | CIPHER_A5(2) | CIPHER_A5(3);
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
@@ -219,12 +266,23 @@ int main(int argc, char **argv)
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);
if (rc < 0) {
fprintf(stderr, "Error initializing telnet\n");
exit(1);
}
if (pcu_sock_init()) {
fprintf(stderr, "PCU L1 socket failed\n");
exit(-1);
}
signal(SIGINT, &signal_handler);
//signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);

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 <errno.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
@@ -29,6 +30,10 @@
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.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 "femtobts.h"
@@ -174,9 +179,8 @@ static int compl_cb_send_oml_msg(struct msgb *l1_msg, void *data)
int lchan_activate(struct gsm_lchan *lchan);
static int opstart_compl_cb(struct msgb *l1_msg, void *data)
static int opstart_compl(struct gsm_abis_mo *mo, struct msgb *l1_msg)
{
struct gsm_abis_mo *mo = data;
GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
GsmL1_Status_t status = prim_status(l1p);
@@ -203,10 +207,19 @@ static int opstart_compl_cb(struct msgb *l1_msg, void *data)
return oml_mo_opstart_ack(mo);
}
static int trx_init_compl_cb(struct msgb *l1_msg, void *data)
static int opstart_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg)
{
struct femtol1_hdl *fl1h = data;
struct gsm_bts_trx *trx = fl1h->priv;
struct gsm_abis_mo *mo;
GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
GsmL1_MphConnectCnf_t *cnf = &l1p->u.mphConnectCnf;
mo = &trx->ts[cnf->u8Tn].mo;
return opstart_compl(mo, l1_msg);
}
static int trx_init_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
GsmL1_MphInitCnf_t *ic = &l1p->u.mphInitCnf;
@@ -223,10 +236,10 @@ static int trx_init_compl_cb(struct msgb *l1_msg, void *data)
fl1h->hLayer1 = ic->hLayer1;
return opstart_compl_cb(l1_msg, &trx->mo);
return opstart_compl(&trx->mo, l1_msg);
}
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 i;
@@ -250,7 +263,6 @@ static int trx_init(struct gsm_bts_trx *trx)
struct msgb *msg;
GsmL1_MphInitReq_t *mi_req;
GsmL1_DeviceParam_t *dev_par;
enum gsm_band osmo_band;
int femto_band;
if (!gsm_abis_mo_check_attr(&trx->mo, trx_rqd_attr,
@@ -261,11 +273,10 @@ static int trx_init(struct gsm_bts_trx *trx)
//return oml_mo_opstart_nack(&trx->mo, NM_NACK_CANT_PERFORM);
}
osmo_band = gsm_arfcn2band(trx->arfcn);
femto_band = band_osmo2femto(osmo_band);
femto_band = band_osmo2femto(trx->bts->band);
if (femto_band < 0) {
LOGP(DL1C, LOGL_ERROR, "Unsupported GSM band %s\n",
gsm_band_name(osmo_band));
gsm_band_name(trx->bts->band));
}
msg = l1p_msgb_alloc();
@@ -276,14 +287,39 @@ static int trx_init(struct gsm_bts_trx *trx)
dev_par->u16Arfcn = trx->arfcn;
dev_par->u16BcchArfcn = trx->bts->c0->arfcn;
dev_par->u8NbTsc = trx->bts->bsic & 7;
dev_par->fRxPowerLevel = -75.f;
dev_par->fRxPowerLevel = fl1h->ul_power_target;
dev_par->fTxPowerLevel = trx->nominal_power - trx->max_power_red;
LOGP(DL1C, LOGL_NOTICE, "Init TRX (ARFCN %u, TSC %u, RxPower % 2f dBm, "
"TxPower % 2.2f dBm\n", dev_par->u16Arfcn, dev_par->u8NbTsc,
dev_par->fRxPowerLevel, dev_par->fTxPowerLevel);
/* send MPH-INIT-REQ, wait for MPH-INIT-CNF */
return l1if_req_compl(fl1h, msg, 0, trx_init_compl_cb, fl1h);
return l1if_gsm_req_compl(fl1h, msg, trx_init_compl_cb);
}
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 gsm_bts_trx *trx, struct msgb *l1_msg)
{
msgb_free(l1_msg);
return 0;
}
int bts_model_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_gsm_req_compl(fl1h, msg, trx_close_compl_cb);
}
static int ts_connect(struct gsm_bts_trx_ts *ts)
@@ -296,7 +332,7 @@ static int ts_connect(struct gsm_bts_trx_ts *ts)
cr->u8Tn = ts->nr;
cr->logChComb = pchan_to_logChComb[ts->pchan];
return l1if_req_compl(fl1h, msg, 0, opstart_compl_cb, &ts->mo);
return l1if_gsm_req_compl(fl1h, msg, opstart_compl_cb);
}
GsmL1_Sapi_t lchan_to_GsmL1_Sapi_t(const struct gsm_lchan *lchan)
@@ -353,20 +389,39 @@ static const struct sapi_dir ccch_sapis[] = {
#define DIR_BOTH (GsmL1_Dir_TxDownlink|GsmL1_Dir_RxUplink)
static const struct sapi_dir tchf_sapis[] = {
{ GsmL1_Sapi_TchF, DIR_BOTH },
{ GsmL1_Sapi_FacchF, DIR_BOTH },
{ GsmL1_Sapi_Sacch, DIR_BOTH },
{ GsmL1_Sapi_TchF, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_TchF, GsmL1_Dir_RxUplink },
{ 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[] = {
{ GsmL1_Sapi_TchH, DIR_BOTH },
{ GsmL1_Sapi_FacchH, DIR_BOTH },
{ GsmL1_Sapi_Sacch, DIR_BOTH },
{ GsmL1_Sapi_TchH, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_TchH, GsmL1_Dir_RxUplink },
{ 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[] = {
{ GsmL1_Sapi_Sdcch, DIR_BOTH },
{ GsmL1_Sapi_Sacch, DIR_BOTH },
{ GsmL1_Sapi_Sdcch, GsmL1_Dir_TxDownlink },
{ 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 {
@@ -391,31 +446,56 @@ static const struct lchan_sapis sapis_for_lchan[_GSM_LCHAN_MAX] = {
.sapis = 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 gsm_bts_trx *trx, struct msgb *l1_msg)
{
struct gsm_lchan *lchan = data;
struct gsm_time *time;
struct gsm_lchan *lchan;
GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
GsmL1_MphActivateCnf_t *ic = &l1p->u.mphActivateCnf;
LOGP(DL1C, LOGL_INFO, "%s MPH-ACTIVATE.conf\n", gsm_lchan_name(lchan));
/* get the lchan from the information we supplied */
lchan = l1if_hLayer_to_lchan(trx, ic->hLayer3);
if (!lchan) {
LOGP(DL1C, LOGL_ERROR,
"Failed to find lchan for hLayer3=0x%x\n", ic->hLayer3);
return -1;
}
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) {
DEBUGP(DL1C, "Successful activation of L1 SAPI %s on TS %u\n",
get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn);
lchan->state = LCHAN_S_ACTIVE;
lchan_set_state(lchan, LCHAN_S_ACTIVE);
} else {
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_l1status_names, ic->status));
lchan->state = LCHAN_S_NONE;
lchan_set_state(lchan, LCHAN_S_NONE);
}
switch (ic->sapi) {
case GsmL1_Sapi_Sdcch:
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;
default:
break;
@@ -426,14 +506,14 @@ static int lchan_act_compl_cb(struct msgb *l1_msg, void *data)
return 0;
}
uint32_t l1if_lchan_to_hLayer2(struct gsm_lchan *lchan)
uint32_t l1if_lchan_to_hLayer(struct gsm_lchan *lchan)
{
return (lchan->nr << 8) | (lchan->ts->nr << 16) | (lchan->ts->trx->nr << 24);
}
/* obtain a ptr to the lapdm_channel for a given hLayer2 */
/* obtain a ptr to the lapdm_channel for a given hLayer */
struct gsm_lchan *
l1if_hLayer2_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer2)
l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer2)
{
uint8_t ts_nr = (hLayer2 >> 16) & 0xff;
uint8_t lchan_nr = (hLayer2 >> 8)& 0xff;
@@ -466,28 +546,62 @@ static void alive_timer_cb(void *data)
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)
{
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);
switch (lchan->tch_mode) {
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:
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:
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;
lch_par->tch.tchPlType = GsmL1_TchPlType_Efr;
set_payload_format(lch_par);
clear_amr_params(lch_par);
break;
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? */
if (lchan->mr_conf.icmi)
lch_par->tch.amrInitCodecMode = lchan->mr_conf.smod;
/* else: FIXME (implicit rule by TS 05.09 ?!?) */
lch_par->tch.amrInitCodecMode = amr_get_initial_mode(lchan);
/* initialize to clean state */
for (j = 0; j < ARRAY_SIZE(lch_par->tch.amrActiveCodecSet); j++)
@@ -531,66 +645,104 @@ static void lchan2lch_par(GsmL1_LogChParam_t *lch_par, struct gsm_lchan *lchan)
if (lchan->mr_conf.m12_2)
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_12_2;
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;
}
}
static int mph_send_activate_req(struct gsm_lchan *lchan, int sapi, int dir)
{
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
struct msgb *msg = l1p_msgb_alloc();
GsmL1_MphActivateReq_t *act_req;
GsmL1_LogChParam_t *lch_par;
act_req = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphActivateReq, fl1h);
lch_par = &act_req->logChPrm;
act_req->u8Tn = lchan->ts->nr;
act_req->subCh = lchan_to_GsmL1_SubCh_t(lchan);
act_req->dir = dir;
act_req->sapi = sapi;
act_req->hLayer2 = l1if_lchan_to_hLayer(lchan);
act_req->hLayer3 = act_req->hLayer2;
switch (act_req->sapi) {
case GsmL1_Sapi_Rach:
lch_par->rach.u8Bsic = lchan->ts->trx->bts->bsic;
break;
case GsmL1_Sapi_Agch:
#warning Set BS_AG_BLKS_RES
lch_par->agch.u8NbrOfAgch = 1;
break;
case GsmL1_Sapi_Sacch:
/* Only if we use manual MS power control */
//act_req->logChPrm.sacch.u8MsPowerLevel = FIXME;
/* enable bad frame indication from >= -100dBm on SACCH */
act_req->fBFILevel = -100.0;
break;
case GsmL1_Sapi_TchH:
case GsmL1_Sapi_TchF:
lchan2lch_par(lch_par, lchan);
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:
break;
}
LOGP(DL1C, LOGL_INFO, "%s MPH-ACTIVATE.req (hL2=0x%08x, %s ",
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 */
return l1if_gsm_req_compl(fl1h, msg, lchan_act_compl_cb);
}
int lchan_activate(struct gsm_lchan *lchan)
{
struct gsm_bts_role_bts *btsb = lchan->ts->trx->bts->role;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
const struct lchan_sapis *s4l = &sapis_for_lchan[lchan->type];
unsigned int i;
for (i = 0; i < s4l->num_sapis; i++) {
struct msgb *msg = l1p_msgb_alloc();
GsmL1_MphActivateReq_t *act_req;
GsmL1_LogChParam_t *lch_par;
act_req = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphActivateReq, fl1h);
lch_par = &act_req->logChPrm;
act_req->u8Tn = lchan->ts->nr;
act_req->subCh = lchan_to_GsmL1_SubCh_t(lchan);
act_req->dir = s4l->sapis[i].dir;
act_req->sapi = s4l->sapis[i].sapi;
act_req->hLayer2 = l1if_lchan_to_hLayer2(lchan);
switch (act_req->sapi) {
case GsmL1_Sapi_Sch:
if (s4l->sapis[i].sapi == GsmL1_Sapi_Sch) {
/* once we activate the SCH, we should get MPH-TIME.ind */
fl1h->alive_timer.cb = alive_timer_cb;
fl1h->alive_timer.data = fl1h;
fl1h->alive_prim_cnt = 0;
osmo_timer_schedule(&fl1h->alive_timer, 5, 0);
break;
case GsmL1_Sapi_Rach:
lch_par->rach.u8Bsic = lchan->ts->trx->bts->bsic;
break;
case GsmL1_Sapi_Agch:
#warning Set BS_AG_BLKS_RES
lch_par->agch.u8NbrOfAgch = 1;
break;
case GsmL1_Sapi_Sacch:
/* Only if we use manual MS power control */
//act_req->logChPrm.sacch.u8MsPowerLevel = FIXME;
break;
case GsmL1_Sapi_TchH:
case GsmL1_Sapi_TchF:
lchan2lch_par(lch_par, lchan);
break;
default:
break;
}
LOGP(DL1C, LOGL_INFO, "%s MPH-ACTIVATE.req (hL2=0x%08x)\n",
gsm_lchan_name(lchan), act_req->hLayer2);
/* send the primitive for all GsmL1_Sapi_* that match the LCHAN */
l1if_req_compl(fl1h, msg, 0, lchan_act_compl_cb, lchan);
mph_send_activate_req(lchan, s4l->sapis[i].sapi, s4l->sapis[i].dir);
}
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->s = btsb->radio_link_timeout;
return 0;
}
@@ -629,16 +781,26 @@ static void dump_lch_par(int logl, GsmL1_LogChParam_t *lch_par, GsmL1_Sapi_t sap
}
break;
/* FIXME: PRACH / PTCCH */
default:
break;
}
LOGPC(DL1C, logl, ")\n");
}
static int chmod_modif_compl_cb(struct msgb *l1_msg, void *data)
static int chmod_modif_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg)
{
struct gsm_lchan *lchan = data;
struct gsm_lchan *lchan;
GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
GsmL1_MphConfigCnf_t *cc = &l1p->u.mphConfigCnf;
/* get the lchan from the information we supplied */
lchan = l1if_hLayer_to_lchan(trx, cc->hLayer3);
if (!lchan) {
LOGP(DL1C, LOGL_ERROR,
"Failed to find lchan for hLayer3=0x%x\n", cc->hLayer3);
return -1;
}
LOGP(DL1C, LOGL_INFO, "%s MPH-CONFIG.conf (%s) ",
gsm_lchan_name(lchan),
get_value_string(femtobts_l1cfgt_names, cc->cfgParamId));
@@ -649,6 +811,29 @@ static int chmod_modif_compl_cb(struct msgb *l1_msg, void *data)
&cc->cfgParams.setLogChParams.logChParams,
cc->cfgParams.setLogChParams.sapi);
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);
@@ -673,6 +858,7 @@ static int tx_confreq_logchpar(struct gsm_lchan *lchan, uint8_t direction)
conf_req->cfgParams.setLogChParams.u8Tn = lchan->ts->nr;
conf_req->cfgParams.setLogChParams.subCh = lchan_to_GsmL1_SubCh_t(lchan);
conf_req->cfgParams.setLogChParams.dir = direction;
conf_req->hLayer3 = l1if_lchan_to_hLayer(lchan);
lch_par = &conf_req->cfgParams.setLogChParams.logChParams;
lchan2lch_par(lch_par, lchan);
@@ -683,7 +869,7 @@ static int tx_confreq_logchpar(struct gsm_lchan *lchan, uint8_t direction)
gsm_lchan_name(lchan),
get_value_string(femtobts_l1sapi_names,
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.subCh,
conf_req->cfgParams.setLogChParams.dir);
@@ -691,13 +877,67 @@ static int tx_confreq_logchpar(struct gsm_lchan *lchan, uint8_t direction)
&conf_req->cfgParams.setLogChParams.logChParams,
conf_req->cfgParams.setLogChParams.sapi);
return l1if_req_compl(fl1h, msg, 0, chmod_modif_compl_cb, lchan);
return l1if_gsm_req_compl(fl1h, msg, chmod_modif_compl_cb);
}
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_gsm_req_compl(fl1h, msg, 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);
cfgr->hLayer3 = l1if_lchan_to_hLayer(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_gsm_req_compl(fl1h, msg, chmod_modif_compl_cb);
}
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 */
/* update multi-rate config */
@@ -709,31 +949,42 @@ int bts_model_rsl_mode_modify(struct gsm_lchan *lchan)
return 0;
}
static int lchan_deact_compl_cb(struct msgb *l1_msg, void *data)
static int lchan_deact_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg)
{
struct gsm_lchan *lchan = data;
struct gsm_lchan *lchan;
GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
GsmL1_MphDeactivateCnf_t *ic = &l1p->u.mphDeactivateCnf;
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.conf (%s)\n",
lchan = l1if_hLayer_to_lchan(trx, ic->hLayer3);
if (!lchan) {
LOGP(DL1C, LOGL_ERROR,
"Failed to find lchan for hLayer3=0x%x\n", ic->hLayer3);
return -1;
}
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.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) {
DEBUGP(DL1C, "Successful deactivation of L1 SAPI %s on TS %u\n",
get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn);
lchan->state = LCHAN_S_ACTIVE;
lchan_set_state(lchan, LCHAN_S_NONE);
} else {
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_l1status_names, ic->status));
lchan->state = LCHAN_S_NONE;
lchan_set_state(lchan, LCHAN_S_REL_ERR);
}
switch (ic->sapi) {
case GsmL1_Sapi_Sdcch:
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;
default:
break;
@@ -751,33 +1002,46 @@ int lchan_deactivate(struct gsm_lchan *lchan)
int i;
for (i = s4l->num_sapis-1; i >= 0; i--) {
struct msgb *msg = l1p_msgb_alloc();
struct msgb *msg;
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->u8Tn = lchan->ts->nr;
deact_req->subCh = lchan_to_GsmL1_SubCh_t(lchan);
deact_req->dir = s4l->sapis[i].dir;
deact_req->sapi = s4l->sapis[i].sapi;
deact_req->hLayer3 = l1if_lchan_to_hLayer(lchan);
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.req (%s)\n",
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.req (%s ",
gsm_lchan_name(lchan),
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 */
if (deact_req->sapi == GsmL1_Sapi_Sch)
osmo_timer_del(&fl1h->alive_timer);
/* send the primitive for all GsmL1_Sapi_* that match the LCHAN */
l1if_req_compl(fl1h, msg, 0, lchan_deact_compl_cb, lchan);
l1if_gsm_req_compl(fl1h, msg, lchan_deact_compl_cb);
}
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;
}
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 msgb *msg = l1p_msgb_alloc();
@@ -788,12 +1052,16 @@ int lchan_deactivate_sacch(struct gsm_lchan *lchan)
deact_req->subCh = lchan_to_GsmL1_SubCh_t(lchan);
deact_req->dir = DIR_BOTH;
deact_req->sapi = GsmL1_Sapi_Sacch;
deact_req->hLayer3 = l1if_lchan_to_hLayer(lchan);
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.req (SACCH)\n",
gsm_lchan_name(lchan));
lchan->sacch_deact = 1;
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 */
return l1if_req_compl(fl1h, msg, 0, lchan_deact_compl_cb, lchan);
return l1if_gsm_req_compl(fl1h, msg, lchan_deact_compl_cb);
}
@@ -837,6 +1105,9 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
case NM_OC_BTS:
case NM_OC_SITE_MANAGER:
case NM_OC_BASEB_TRANSC:
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
rc = oml_mo_opstart_ack(mo);
break;
@@ -851,5 +1122,35 @@ int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
{
/* blindly accept all state changes */
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);
return l1if_activate_rf(fl1, 0);
}

View File

@@ -1,6 +1,7 @@
/* VTY interface for sysmoBTS */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
* (C) 2012 by Holger Hans Peter Freyther
*
* All Rights Reserved
*
@@ -23,6 +24,7 @@
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <ctype.h>
#include <arpa/inet.h>
@@ -35,68 +37,168 @@
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/misc.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/vty.h>
#include "femtobts.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;
/* This generates the logging command string for VTY. */
const char *vty_cmd_string_from_valstr(const struct value_string *vals,
const char *prefix)
/* configuration */
DEFUN(cfg_trx_gsmtap_sapi, cfg_trx_gsmtap_sapi_cmd,
"HIDDEN", "HIDDEN")
{
int len = 0, offset = 0, ret, i, rem;
int size = strlen(prefix);
const struct value_string *vs;
char *str;
struct gsm_bts_trx *trx = vty->index;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
int sapi;
for (vs = vals; vs->value || vs->str; vs++)
size += strlen(vs->str) + 1;
sapi = get_string_value(femtobts_l1sapi_names, argv[0]);
rem = size;
str = talloc_zero_size(vty_bts, size);
if (!str)
return NULL;
fl1h->gsmtap_sapi_mask |= (1 << sapi);
ret = snprintf(str + offset, rem, prefix);
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;
return CMD_SUCCESS;
}
DEFUN(cfg_trx_no_gsmtap_sapi, cfg_trx_no_gsmtap_sapi_cmd,
"HIDDEN", "HIDDEN")
{
struct gsm_bts_trx *trx = vty->index;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
int sapi;
sapi = get_string_value(femtobts_l1sapi_names, argv[0]);
fl1h->gsmtap_sapi_mask &= ~(1 << sapi);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_clkcal_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;
}
DEFUN(cfg_trx_ul_power_target, cfg_trx_ul_power_target_cmd,
"uplink-power-target <-110-0>",
"Set the nominal target Rx Level for uplink power control loop\n"
"Target uplink Rx level in dBm\n")
{
struct gsm_bts_trx *trx = vty->index;
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
fl1h->ul_power_target = atoi(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,
"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]);
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
@@ -130,7 +232,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]);
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
@@ -150,7 +252,7 @@ DEFUN(dsp_trace_f, dsp_trace_f_cmd, "HIDDEN", "HIDDEN")
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]);
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
@@ -170,20 +272,225 @@ DEFUN(no_dsp_trace_f, no_dsp_trace_f_cmd, "HIDDEN", "HIDDEN")
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);
vty_out(vty, " uplink-power-target %d%s", fl1h->ul_power_target,
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;
/* runtime-patch the command strings with debug levels */
dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(femtobts_tracef_names,
"trx <0-0> dsp-trace-flag (");
no_dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(femtobts_tracef_names,
"no trx <0-0> dsp-trace-flag (");
dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_tracef_names,
"trx <0-0> dsp-trace-flag (",
"|",")", VTY_DO_LOWER);
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_sys_info_cmd);
install_element_ve(&show_trx_clksrc_cmd);
install_element_ve(&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);
install_element(TRX_NODE, &cfg_trx_ul_power_target_cmd);
return 0;
}

View File

@@ -1,6 +1,6 @@
/* 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
*
@@ -40,7 +40,7 @@
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/measurement.h>
#include <sysmocom/femtobts/femtobts.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h>
#include <sysmocom/femtobts/gsml1const.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)
{
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
struct msgb *msg;
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 */
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);
cur[0] |= 0xD0;
#endif /* USE_L1_RTP_MODE */
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
* \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)
{
#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*/
osmo_nibble_shift_left_unal(l1_payload, rtp_payload, GSM_FR_BITS/4);
/* step1: reverse the bit-order of each payload byte */
osmo_revbytebits_buf(l1_payload, payload_len);
#endif /* USE_L1_RTP_MODE */
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)
{
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
struct msgb *msg;
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 */
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);
cur[0] |= 0xC0;
#endif /* USE_L1_RTP_MODE */
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
#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)
{
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
struct msgb *msg;
uint8_t *cur;
msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
if (!msg)
return NULL;
if (payload_len != GSM_HR_BYTES) {
LOGP(DL1C, LOGL_ERROR, "L1 HR frame length %u != expected %u\n",
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);
memcpy(cur, l1_payload, GSM_HR_BYTES);
#ifndef USE_L1_RTP_MODE
/* reverse the bit-order of each payload byte */
osmo_revbytebits_buf(cur, GSM_HR_BYTES);
#endif /* USE_L1_RTP_MODE */
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
* \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)
{
@@ -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);
#ifndef USE_L1_RTP_MODE
/* reverse the bit-order of each payload byte */
osmo_revbytebits_buf(l1_payload, GSM_HR_BYTES);
#endif /* USE_L1_RTP_MODE */
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,
struct amr_multirate_conf *amr_mrc)
{
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
struct msgb *msg;
uint8_t *cur;
u_int8_t cmr;
uint8_t ft = l1_payload[2] & 0xF;
uint8_t cmr_idx = l1_payload[1];
uint8_t amr_if2_len = payload_len - 2;
msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
if (!msg)
return NULL;
#if 0
uint8_t cmr_idx = l1_payload[1];
/* CMR == Unset means CMR was not transmitted at this TDMA */
if (cmr_idx >= GsmL1_AmrCodecMode_Unset)
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;
#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 */
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 */
osmo_nibble_shift_left_unal(cur, l1_payload+2, amr_if2_len*2 -1);
#endif /* USE_L1_RTP_MODE */
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
* \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,
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;
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 */
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
* core frame contained in the RTP payload */
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
* it will use, so we have to use the frame type */
switch (ft) {
@@ -341,9 +402,6 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, uint8_t *rtp_payload,
}
#endif
/* lower 4 bit of first FR2 byte contains FT */
l1_payload[2] |= ft;
if (ft == AMR_FT_SID_AMR) {
/* store the last SID frame in lchan context */
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
* 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)
{
struct gsm_lchan *lchan = rs->priv;
struct msgb *msg = l1p_msgb_alloc();
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
GsmL1_MsgUnitParam_t *msu_param = &data_req->msgUnitParam;
uint8_t *payload_type = &msu_param->u8Buffer[0];
uint8_t *l1_payload = &msu_param->u8Buffer[1];
struct msgb *msg;
GsmL1_Prim_t *l1p;
GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param;
uint8_t *payload_type;
uint8_t *l1_payload;
int rc;
DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan),
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) {
case GSM48_CMODE_SPEECH_V1:
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);
}
break;
#ifdef GsmL1_TchPlType_Efr
#if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
case GSM48_CMODE_SPEECH_EFR:
*payload_type = GsmL1_TchPlType_Efr;
rc = FIXME;
rc = rtppayload_to_l1_efr(l1_payload, rtp_pl,
rtp_pl_len);
break;
#endif
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;
if (lchan->loopback) {
GsmL1_Prim_t *rl1p;
GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param;
struct msgb *tmp;
int count = 0;
/* generate a new msgb from the paylaod */
rmsg = l1p_msgb_alloc();
if (!rmsg)
return -ENOMEM;
rl1p = msgb_l1prim(rmsg);
data_req = &rl1p->u.phDataReq;
msu_param = &data_req->msgUnitParam;
memcpy(msu_param->u8Buffer,
data_ind->msgUnitParam.u8Buffer,
data_ind->msgUnitParam.u8Size);
msu_param->u8Size = data_ind->msgUnitParam.u8Size;
/* make sure the queue doesn't get too long */
llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
count++;
while (count >= 1) {
tmp = msgb_dequeue(&lchan->dl_tch_queue);
msgb_free(tmp);
count--;
}
msgb_enqueue(&lchan->dl_tch_queue, rmsg);
return 0;
}
switch (payload_type) {
case GsmL1_TchPlType_Fr:
#ifdef GsmL1_TchPlType_Efr
#if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
case GsmL1_TchPlType_Efr:
#endif
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:
rmsg = l1_to_rtppayload_hr(payload, payload_len);
break;
#ifdef GsmL1_TchPlType_Efr
case GsmL1_TchPlType_Efr
#if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
case GsmL1_TchPlType_Efr:
rmsg = l1_to_rtppayload_efr(payload, payload_len);
break;
#endif
@@ -532,12 +644,22 @@ err_payload_match:
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan)
{
struct msgb *msg = l1p_msgb_alloc();
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
GsmL1_MsgUnitParam_t *msu_param = &data_req->msgUnitParam;
uint8_t *payload_type = &msu_param->u8Buffer[0];
uint8_t *l1_payload = &msu_param->u8Buffer[1];
struct msgb *msg;
GsmL1_Prim_t *l1p;
GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param;
uint8_t *payload_type;
uint8_t *l1_payload;
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) {
case GSM48_CMODE_SPEECH_AMR:

41
tests/Makefile.am Normal file
View File

@@ -0,0 +1,41 @@
SUBDIRS = paging cipher
# 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
DISTCLEANFILES = atconfig
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
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/cipher/Makefile.am Normal file
View File

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

View File

@@ -0,0 +1,90 @@
/* (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/bts.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/paging.h>
#include <osmo-bts/gsm_data.h>
#include <osmocom/core/talloc.h>
#include <errno.h>
#include <unistd.h>
static struct gsm_bts *bts;
static struct gsm_bts_role_bts *btsb;
int pcu_direct = 0;
#define ASSERT_TRUE(rc) \
if (!(rc)) { \
printf("Assert failed in %s:%d.\n", \
__FILE__, __LINE__); \
abort(); \
}
static void test_cipher_parsing(void)
{
int i;
btsb->support.ciphers = 0;
/* always support A5/0 */
ASSERT_TRUE(bts_supports_cipher(btsb, 0x0) == -ENOTSUP);
ASSERT_TRUE(bts_supports_cipher(btsb, 0x1) == 1); /* A5/0 */
for (i = 2; i <= 8; ++i) {
ASSERT_TRUE(bts_supports_cipher(btsb, i) == 0);
}
/* checking default A5/1 to A5/3 support */
btsb->support.ciphers = CIPHER_A5(1) | CIPHER_A5(2) | CIPHER_A5(3);
ASSERT_TRUE(bts_supports_cipher(btsb, 0x0) == -ENOTSUP);
ASSERT_TRUE(bts_supports_cipher(btsb, 0x1) == 1); /* A5/0 */
ASSERT_TRUE(bts_supports_cipher(btsb, 0x2) == 1); /* A5/1 */
ASSERT_TRUE(bts_supports_cipher(btsb, 0x3) == 1); /* A5/2 */
ASSERT_TRUE(bts_supports_cipher(btsb, 0x4) == 1); /* A5/3 */
ASSERT_TRUE(bts_supports_cipher(btsb, 0x5) == 0); /* A5/4 */
ASSERT_TRUE(bts_supports_cipher(btsb, 0x6) == 0); /* A5/5 */
ASSERT_TRUE(bts_supports_cipher(btsb, 0x7) == 0); /* A5/6 */
ASSERT_TRUE(bts_supports_cipher(btsb, 0x8) == 0); /* A5/7 */
ASSERT_TRUE(bts_supports_cipher(btsb, 0x9) == -ENOTSUP);
}
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_cipher_parsing();
printf("Success\n");
return 0;
}

View File

@@ -0,0 +1 @@
Success

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

@@ -0,0 +1,8 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
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 $(srcdir)/../stubs.c
paging_test_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)

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

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

View File

@@ -0,0 +1,3 @@
Testing that paging messages expire.
Testing that paging messages expire with sleep.
Success

48
tests/stubs.c Normal file
View File

@@ -0,0 +1,48 @@
#include <osmo-bts/bts.h>
/*
* Stubs to provide an empty bts model implementation for testing.
* If we ever want to re-define such a symbol we can make them weak
* here.
*/
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_trx_close(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; }

14
tests/testsuite.at Normal file
View File

@@ -0,0 +1,14 @@
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
AT_SETUP([cipher])
AT_KEYWORDS([cipher])
cat $abs_srcdir/cipher/cipher_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/cipher/cipher_test], [], [expout], [ignore])
AT_CLEANUP