mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-bts.git
synced 2025-11-09 16:37:11 +00:00
Compare commits
173 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1335d878b | ||
|
|
98a4404279 | ||
|
|
5705cfaebc | ||
|
|
c3646a80a7 | ||
|
|
8debeeeeea | ||
|
|
255343db4b | ||
|
|
4fe622cf9c | ||
|
|
4168d885cf | ||
|
|
c1ad2ac20f | ||
|
|
0efca9a1f9 | ||
|
|
ef2cb5ab7f | ||
|
|
4d197c96d8 | ||
|
|
d127ddbfcc | ||
|
|
f91924bb18 | ||
|
|
8c3d807b3f | ||
|
|
7daa093df7 | ||
|
|
b86bf060d3 | ||
|
|
fde8e6dc0c | ||
|
|
a9dee426d7 | ||
|
|
d777a19bb8 | ||
|
|
e5a04ea35d | ||
|
|
1c74191ff0 | ||
|
|
93c087892c | ||
|
|
7c2427c020 | ||
|
|
678321d013 | ||
|
|
e729a3d595 | ||
|
|
1195148fc6 | ||
|
|
1ddb183736 | ||
|
|
c2d3e45571 | ||
|
|
27baa4c3de | ||
|
|
b3eb6da2db | ||
|
|
d40d4d6071 | ||
|
|
08fce19cfc | ||
|
|
0390d54ade | ||
|
|
0c470759da | ||
|
|
5a53eff4cb | ||
|
|
990d1da8a4 | ||
|
|
65d4d5108a | ||
|
|
0cfefa0e12 | ||
|
|
4253150bab | ||
|
|
38420fb951 | ||
|
|
3696c6946d | ||
|
|
438a28714d | ||
|
|
c1368d4ebe | ||
|
|
744f745d7a | ||
|
|
8169b0bd85 | ||
|
|
07b37853a4 | ||
|
|
bf2a18e623 | ||
|
|
66f1fe15e9 | ||
|
|
07891a0908 | ||
|
|
343cae60b6 | ||
|
|
ea15101896 | ||
|
|
b57e17394b | ||
|
|
b19592f713 | ||
|
|
cb7697074e | ||
|
|
71b216d995 | ||
|
|
d53ae2d0f1 | ||
|
|
5f8a3149fe | ||
|
|
eda6c26360 | ||
|
|
6b561bb7ba | ||
|
|
fa8014f181 | ||
|
|
61fb64d252 | ||
|
|
54b8af0f64 | ||
|
|
9fdefc6ffe | ||
|
|
13e92be8bf | ||
|
|
e01a47aad4 | ||
|
|
babbbbf6ee | ||
|
|
821bf067e4 | ||
|
|
c882b85d8c | ||
|
|
565cf0d8ab | ||
|
|
a540332df3 | ||
|
|
ad3e31dc4b | ||
|
|
1c069cd0a0 | ||
|
|
0455e51cd5 | ||
|
|
ad09615acb | ||
|
|
2100a2e16f | ||
|
|
c58968be02 | ||
|
|
799ea59c2f | ||
|
|
700c645478 | ||
|
|
346e531222 | ||
|
|
b18f00f162 | ||
|
|
268c7f02fd | ||
|
|
388b9d0a35 | ||
|
|
9de1e9f914 | ||
|
|
7fe0838588 | ||
|
|
3af5426d71 | ||
|
|
e6ed814dc3 | ||
|
|
f7fd2e4798 | ||
|
|
36a3b0d85b | ||
|
|
36179bbcdf | ||
|
|
227c57728a | ||
|
|
baa88d542c | ||
|
|
d28b9940b9 | ||
|
|
2b7aace0b5 | ||
|
|
b1644b22d0 | ||
|
|
bcd08888f9 | ||
|
|
9aa6d9496b | ||
|
|
ff9e904926 | ||
|
|
f19ee66096 | ||
|
|
4301b09137 | ||
|
|
f5a0a439e9 | ||
|
|
bf91f06eca | ||
|
|
b0150b7ad4 | ||
|
|
d7718280c9 | ||
|
|
1e2b3259b9 | ||
|
|
29e1fdd994 | ||
|
|
af02387183 | ||
|
|
f78f35880f | ||
|
|
eac221b4ea | ||
|
|
f4f69ee6fc | ||
|
|
f1052b812d | ||
|
|
0be33e3add | ||
|
|
b03f8ae4f0 | ||
|
|
d9ab45d1aa | ||
|
|
51f9693ba6 | ||
|
|
b34faf6f8c | ||
|
|
bc74b7f432 | ||
|
|
f4a5bd2dd2 | ||
|
|
58f419c7ce | ||
|
|
11a787df24 | ||
|
|
caaa7e9d7b | ||
|
|
666fec7ff2 | ||
|
|
76aa95453f | ||
|
|
c623c4e589 | ||
|
|
2ed209c758 | ||
|
|
a0970249bf | ||
|
|
f4d14b3f2e | ||
|
|
d25b6a752b | ||
|
|
3cf942792a | ||
|
|
21724bbaed | ||
|
|
12b95405ff | ||
|
|
452112e823 | ||
|
|
d0e6749327 | ||
|
|
b81c5d4699 | ||
|
|
fe0c13f8bd | ||
|
|
3525f2c038 | ||
|
|
20d73555a2 | ||
|
|
47589f10a4 | ||
|
|
f1cbd81984 | ||
|
|
818cb2d314 | ||
|
|
6dd7c4fb57 | ||
|
|
bb9647f651 | ||
|
|
2e677958d2 | ||
|
|
771e77dff0 | ||
|
|
62579c7a34 | ||
|
|
4cd68dc4d7 | ||
|
|
eab71534ef | ||
|
|
6e121417a5 | ||
|
|
7a44e47ed6 | ||
|
|
6aa2a574fb | ||
|
|
fd58d925a8 | ||
|
|
b0985e3fa5 | ||
|
|
467e149763 | ||
|
|
127ec05b4e | ||
|
|
79da6f3283 | ||
|
|
143bb812dc | ||
|
|
fe4893e625 | ||
|
|
2660812084 | ||
|
|
5cdcf8a837 | ||
|
|
efdb45d5d0 | ||
|
|
477f35e78c | ||
|
|
187871e2ca | ||
|
|
b10d74d821 | ||
|
|
9582883235 | ||
|
|
c373448e03 | ||
|
|
7899dc5fcf | ||
|
|
215d9eecdd | ||
|
|
06636b6155 | ||
|
|
9508fb80a4 | ||
|
|
4ccca1ce36 | ||
|
|
a4a3574b1d | ||
|
|
2c40d02f27 | ||
|
|
16c0ab92c1 |
18
.gitignore
vendored
18
.gitignore
vendored
@@ -15,6 +15,24 @@ missing
|
|||||||
core
|
core
|
||||||
core.*
|
core.*
|
||||||
|
|
||||||
|
contrib/sysmobts-calib/sysmobts-calib
|
||||||
|
|
||||||
src/osmo-bts-sysmo/l1fwd-proxy
|
src/osmo-bts-sysmo/l1fwd-proxy
|
||||||
src/osmo-bts-sysmo/sysmobts
|
src/osmo-bts-sysmo/sysmobts
|
||||||
src/osmo-bts-sysmo/sysmobts-remote
|
src/osmo-bts-sysmo/sysmobts-remote
|
||||||
|
src/osmo-bts-sysmo/sysmobts-mgr
|
||||||
|
|
||||||
|
|
||||||
|
tests/atconfig
|
||||||
|
tests/package.m4
|
||||||
|
tests/paging/paging_test
|
||||||
|
tests/testsuite
|
||||||
|
tests/testsuite.log
|
||||||
|
|
||||||
|
# Possible generated file
|
||||||
|
doc/vty_reference.xml
|
||||||
|
|
||||||
|
# Backups, vi, merges
|
||||||
|
*.sw?
|
||||||
|
*.orig
|
||||||
|
*.sav
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
|
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
|
||||||
|
|
||||||
SUBDIRS = include src
|
SUBDIRS = include src tests
|
||||||
|
|||||||
59
README
59
README
@@ -1,10 +1,55 @@
|
|||||||
Repsoitory for new BTS-side A-bis code
|
Repository for new the Osmocom BTS implementation.
|
||||||
|
|
||||||
This is the code that was originally developed inside osmocom-bb.git
|
This code implementes the Layer 2 and higher of a more or less
|
||||||
for turning modified OsmocomBB-supported phones into a simplistic BTS.
|
conventional GSM BTS (Base Transceiver Station) - however, using an
|
||||||
|
Abis/IP interface, rather than the old-fashioned E1/T1.
|
||||||
|
|
||||||
However, the BTS-side A-bis is also going to be needed for other projects, thus
|
Specificallt, this includes
|
||||||
the split.
|
* BTS-Side implementation of TS 08.58 (RSL) and TS 12.21 (OML)
|
||||||
|
* BTS-Side implementation of LAPDm (using libosmocore/libosmogsm)
|
||||||
|
* A somewhat separated interface between those higher layer parts
|
||||||
|
and the Layer1 interface.
|
||||||
|
|
||||||
It doesn't really build yet, as a lot of dependencies have not yet been
|
Right now, only one hardware and Layer1 are supported: The sysmocom
|
||||||
resolved.
|
sysmoBTS.
|
||||||
|
|
||||||
|
There is some experimental and way incomplete code to use a couple of
|
||||||
|
OsmocomBB phones and run them in the BTS. However, the required code
|
||||||
|
for the Calypso DSP code have not been written yet. This would still
|
||||||
|
require a lot of work.
|
||||||
|
|
||||||
|
Some additional work is being done in using some parts of the OpenBTS
|
||||||
|
L1FEC and glue it against omso-bts. However, this is also still in an
|
||||||
|
early, experimental stage.
|
||||||
|
|
||||||
|
== Known Limitations ==
|
||||||
|
|
||||||
|
As of June 3, 2012, the following known limitations exist in this
|
||||||
|
implementation:
|
||||||
|
|
||||||
|
=== Common Core ===
|
||||||
|
* No Extended BCCH support
|
||||||
|
* System Information limited to 1,2,2bis,2ter,2quater,3,4,5,6,9,13
|
||||||
|
* No RATSCCH in AMR
|
||||||
|
* No OML (TS 12.21) alarms yet (temperature, ...)
|
||||||
|
* Only single-TRX BTS at this point
|
||||||
|
* Will reject TS 12.21 STARTING TIME in SET BTS ATTR / SET CHAN ATTR
|
||||||
|
* No support for frequency hopping
|
||||||
|
* No reporting of interference levels as part of TS 08.58 RF RES IND
|
||||||
|
* No error reporting in case PAGING COMMAND fails due to queue overflow
|
||||||
|
* No hand-over support (planned)
|
||||||
|
* No use of TS 08.58 BS Power and MS Power parameters
|
||||||
|
* No support of TS 08.58 MultiRate Control
|
||||||
|
* No support of TS 08.58 Supported Codec Types
|
||||||
|
* No support of Bter frame / ENHANCED MEASUREMENT REPORT
|
||||||
|
|
||||||
|
=== osmo-bts-sysmo ===
|
||||||
|
* No CSD / ECSD support (not planned)
|
||||||
|
* No GPRS/EDGE support (planned)
|
||||||
|
* GSM-R frequency band supported, but no NCH/ASCI/SoLSA
|
||||||
|
* All timeslots on one TRX have to use same training sequence (TSC)
|
||||||
|
* No multi-TRX support yet, though hardware+L1 support stacking
|
||||||
|
* Makes no use of 12.21 Intave Parameters and Interference
|
||||||
|
Level Boundaries
|
||||||
|
* Makes no use of TS 12.21 T3105
|
||||||
|
* Doesn't yet include MAC address in Abis/IP Identity message
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ AC_INIT([osmo-bts],
|
|||||||
[openbsc-devel@lists.openbsc.org])
|
[openbsc-devel@lists.openbsc.org])
|
||||||
|
|
||||||
AM_INIT_AUTOMAKE([dist-bzip2])
|
AM_INIT_AUTOMAKE([dist-bzip2])
|
||||||
|
AC_CONFIG_TESTDIR(tests)
|
||||||
|
|
||||||
dnl kernel style compile messages
|
dnl kernel style compile messages
|
||||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||||
@@ -40,4 +41,6 @@ AC_OUTPUT(
|
|||||||
src/osmo-bts-bb/Makefile
|
src/osmo-bts-bb/Makefile
|
||||||
include/Makefile
|
include/Makefile
|
||||||
include/osmo-bts/Makefile
|
include/osmo-bts/Makefile
|
||||||
|
tests/Makefile
|
||||||
|
tests/paging/Makefile
|
||||||
Makefile)
|
Makefile)
|
||||||
|
|||||||
40
contrib/dump_docs.py
Executable file
40
contrib/dump_docs.py
Executable 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()
|
||||||
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
while [ -e /etc/passwd ]; do
|
while [ -e /etc/passwd ]; do
|
||||||
$*
|
cat /lib/firmware/sysmobts-v?.out > /dev/dspdl_dm644x_0
|
||||||
|
echo "0" > /sys/class/leds/activity_led/brightness
|
||||||
|
nice -n -20 $*
|
||||||
done
|
done
|
||||||
|
|||||||
10
contrib/sysmobts-calib/Makefile
Normal file
10
contrib/sysmobts-calib/Makefile
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
CFLAGS=`pkg-config --cflags libosmocore` -Wall -Werror
|
||||||
|
LIBS=`pkg-config --libs libosmocore`
|
||||||
|
|
||||||
|
all: sysmobts-calib
|
||||||
|
|
||||||
|
sysmobts-calib: sysmobts-calib.o sysmobts-layer1.o
|
||||||
|
$(CC) $(CPPFLAGS) -o $@ $^ -lrt $(LIBS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm -f sysmobts-calib *.o
|
||||||
447
contrib/sysmobts-calib/sysmobts-calib.c
Normal file
447
contrib/sysmobts-calib/sysmobts-calib.c
Normal file
@@ -0,0 +1,447 @@
|
|||||||
|
/* OCXO/TCXO based calibration utility */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (C) 2012 Holger Hans Peter Freyther
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#include <sysmocom/femtobts/superfemto.h>
|
||||||
|
#include <sysmocom/femtobts/gsml1types.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
|
||||||
|
#include "sysmobts-layer1.h"
|
||||||
|
|
||||||
|
enum actions {
|
||||||
|
ACTION_SCAN,
|
||||||
|
ACTION_CALIB,
|
||||||
|
ACTION_BCCH,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *modes[] = {
|
||||||
|
[ACTION_SCAN] = "scan",
|
||||||
|
[ACTION_CALIB] = "calibrate",
|
||||||
|
[ACTION_BCCH] = "bcch",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *bands[] = {
|
||||||
|
[GsmL1_FreqBand_850] = "850",
|
||||||
|
[GsmL1_FreqBand_900] = "900",
|
||||||
|
[GsmL1_FreqBand_1800] = "1800",
|
||||||
|
[GsmL1_FreqBand_1900] = "1900",
|
||||||
|
};
|
||||||
|
|
||||||
|
struct channel_pair {
|
||||||
|
int min;
|
||||||
|
int max;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct channel_pair arfcns[] = {
|
||||||
|
[GsmL1_FreqBand_850] = { .min = 128, .max = 251 },
|
||||||
|
[GsmL1_FreqBand_900] = { .min = 1, .max = 124 },
|
||||||
|
[GsmL1_FreqBand_1800] = { .min = 512, .max = 885 },
|
||||||
|
[GsmL1_FreqBand_1900] = { .min = 512, .max = 810 },
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *clk_source[] = {
|
||||||
|
[SuperFemto_ClkSrcId_Ocxo] = "ocxo",
|
||||||
|
[SuperFemto_ClkSrcId_Tcxo] = "tcxo",
|
||||||
|
[SuperFemto_ClkSrcId_External] = "external",
|
||||||
|
[SuperFemto_ClkSrcId_GpsPps] = "gps",
|
||||||
|
[SuperFemto_ClkSrcId_Trx] = "trx",
|
||||||
|
[SuperFemto_ClkSrcId_Rx] = "rx",
|
||||||
|
[SuperFemto_ClkSrcId_Edge] = "edge",
|
||||||
|
[SuperFemto_ClkSrcId_NetList] = "netlisten",
|
||||||
|
};
|
||||||
|
|
||||||
|
static int action = ACTION_SCAN;
|
||||||
|
static int band = GsmL1_FreqBand_900;
|
||||||
|
static int calib = SuperFemto_ClkSrcId_Ocxo;
|
||||||
|
static int source = SuperFemto_ClkSrcId_NetList;
|
||||||
|
static int dsp_flags = 0x0;
|
||||||
|
static int cal_arfcn = 0;
|
||||||
|
static int initial_cor = 0;
|
||||||
|
static int steps = -1;
|
||||||
|
|
||||||
|
static void print_usage(void)
|
||||||
|
{
|
||||||
|
printf("Usage: sysmobts-calib ARGS\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_help(void)
|
||||||
|
{
|
||||||
|
printf(" -h --help this text\n");
|
||||||
|
printf(" -c --clock "
|
||||||
|
"ocxo|tcxo|external|gps|trx|rx|edge\n");
|
||||||
|
printf(" -s --calibration-source "
|
||||||
|
"ocxo|tcxo|external|gps|trx|rx|edge|netlisten\n");
|
||||||
|
printf(" -b --band 850|900|1800|1900\n");
|
||||||
|
printf(" -m --mode scan|calibrate|bcch\n");
|
||||||
|
printf(" -a --arfcn NR arfcn for calibration\n");
|
||||||
|
printf(" -d --dsp-flags NR dsp mask for debug log\n");
|
||||||
|
printf(" -t --threshold level\n");
|
||||||
|
printf(" -i --initial-clock-correction COR.\n");
|
||||||
|
printf(" -t --steps STEPS\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_value(const char **array, int size, char *value)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
for (i = 0; i < size; ++i) {
|
||||||
|
if (array[i] == NULL)
|
||||||
|
continue;
|
||||||
|
if (strcmp(value, array[i]) == 0)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Failed to find: '%s'\n", value);
|
||||||
|
exit(-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_options(int argc, char **argv)
|
||||||
|
{
|
||||||
|
while (1) {
|
||||||
|
int option_index = 0, c;
|
||||||
|
static struct option long_options[] = {
|
||||||
|
{"help", 0, 0, 'h'},
|
||||||
|
{"calibration-source", 1, 0, 's'},
|
||||||
|
{"clock", 1, 0, 'c'},
|
||||||
|
{"mode", 1, 0, 'm'},
|
||||||
|
{"band", 1, 0, 'b'},
|
||||||
|
{"dsp-flags", 1, 0, 'd'},
|
||||||
|
{"arfcn", 1, 0, 'a'},
|
||||||
|
{"initial-clock-correction", 1, 0, 'i'},
|
||||||
|
{"steps", 1, 0, 't'},
|
||||||
|
{0, 0, 0, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
c = getopt_long(argc, argv, "hs:c:m:b:d:a:i:t:",
|
||||||
|
long_options, &option_index);
|
||||||
|
if (c == -1)
|
||||||
|
break;
|
||||||
|
switch (c) {
|
||||||
|
case 'h':
|
||||||
|
print_usage();
|
||||||
|
print_help();
|
||||||
|
exit(0);
|
||||||
|
case 's':
|
||||||
|
source = find_value(clk_source,
|
||||||
|
ARRAY_SIZE(clk_source), optarg);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
calib = find_value(clk_source,
|
||||||
|
ARRAY_SIZE(clk_source), optarg);
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
action = find_value(modes,
|
||||||
|
ARRAY_SIZE(modes), optarg);
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
band = find_value(bands,
|
||||||
|
ARRAY_SIZE(bands), optarg);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
dsp_flags = strtol(optarg, NULL, 16);
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
cal_arfcn = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
initial_cor = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
steps = atoi(optarg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("Unhandled option, terminating.\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source == calib) {
|
||||||
|
printf("Clock source and reference clock may not be the same.\n");
|
||||||
|
exit(-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calib == SuperFemto_ClkSrcId_NetList) {
|
||||||
|
printf("Clock may not be network listen.\n");
|
||||||
|
exit(-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == ACTION_CALIB) {
|
||||||
|
if (cal_arfcn == 0) {
|
||||||
|
printf("Please specify the reference ARFCN.\n");
|
||||||
|
exit(-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cal_arfcn < arfcns[band].min || cal_arfcn > arfcns[band].max) {
|
||||||
|
printf("ARFCN(%d) is not in the given band.\n", cal_arfcn);
|
||||||
|
exit(-6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHECK_RC(rc) \
|
||||||
|
if (rc != 0) \
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
#define CHECK_RC_MSG(rc, msg) \
|
||||||
|
if (rc != 0) { \
|
||||||
|
printf("%s: %d\n", msg, rc); \
|
||||||
|
return EXIT_FAILURE; \
|
||||||
|
}
|
||||||
|
#define CHECK_COND_MSG(cond, rc, msg) \
|
||||||
|
if (cond) { \
|
||||||
|
printf("%s: %d\n", msg, rc); \
|
||||||
|
return EXIT_FAILURE; \
|
||||||
|
}
|
||||||
|
|
||||||
|
struct scan_result
|
||||||
|
{
|
||||||
|
uint16_t arfcn;
|
||||||
|
float rssi;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int scan_cmp(const void *arg1, const void *arg2)
|
||||||
|
{
|
||||||
|
struct scan_result *elem1 = (struct scan_result *) arg1;
|
||||||
|
struct scan_result *elem2 = (struct scan_result * )arg2;
|
||||||
|
|
||||||
|
float diff = elem1->rssi - elem2->rssi;
|
||||||
|
if (diff > 0.0)
|
||||||
|
return 1;
|
||||||
|
else if (diff < 0.0)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int scan_band()
|
||||||
|
{
|
||||||
|
int arfcn, rc, i;
|
||||||
|
|
||||||
|
/* Scan results.. at most 400 items */
|
||||||
|
struct scan_result results[400];
|
||||||
|
memset(&results, 0, sizeof(results));
|
||||||
|
int num_scan_results = 0;
|
||||||
|
|
||||||
|
printf("Going to scan bands.\n");
|
||||||
|
|
||||||
|
for (arfcn = arfcns[band].min; arfcn <= arfcns[band].max; ++arfcn) {
|
||||||
|
float mean_rssi;
|
||||||
|
|
||||||
|
printf(".");
|
||||||
|
fflush(stdout);
|
||||||
|
rc = power_scan(band, arfcn, 10, &mean_rssi);
|
||||||
|
CHECK_RC_MSG(rc, "Power Measurement failed");
|
||||||
|
|
||||||
|
results[num_scan_results].arfcn = arfcn;
|
||||||
|
results[num_scan_results].rssi = mean_rssi;
|
||||||
|
num_scan_results++;
|
||||||
|
}
|
||||||
|
|
||||||
|
qsort(results, num_scan_results, sizeof(struct scan_result), scan_cmp);
|
||||||
|
printf("\nSorted scan results (weakest first):\n");
|
||||||
|
for (i = 0; i < num_scan_results; ++i)
|
||||||
|
printf("ARFCN %3d: %.4f\n", results[i].arfcn, results[i].rssi);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int calib_clock_after_sync(HANDLE *layer1)
|
||||||
|
{
|
||||||
|
int rc, clkErr, clkErrRes, iteration, cor;
|
||||||
|
|
||||||
|
iteration = 0;
|
||||||
|
cor = initial_cor;
|
||||||
|
|
||||||
|
printf("Trying to calibrate now and reducing clock error.\n");
|
||||||
|
|
||||||
|
for (iteration = 0; iteration < steps || steps <= 0; ++iteration) {
|
||||||
|
if (steps > 0)
|
||||||
|
printf("Iteration %d/%d with correction: %d\n", iteration, steps, cor);
|
||||||
|
else
|
||||||
|
printf("Iteration %d with correction: %d\n", iteration, cor);
|
||||||
|
|
||||||
|
rc = rf_clock_info(layer1, &clkErr, &clkErrRes);
|
||||||
|
CHECK_RC_MSG(rc, "Clock info failed.\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: use the clock error resolution here, implement it as a
|
||||||
|
* a PID controller..
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Picocell class requires 0.1ppm.. but that is 'too easy' */
|
||||||
|
if (fabs(clkErr / 1000.0f) <= 0.05f) {
|
||||||
|
printf("The calibration value is: %d\n", cor);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cor -= clkErr / 2;
|
||||||
|
rc = set_clock_cor(cor, calib, source);
|
||||||
|
CHECK_RC_MSG(rc, "Clock correction failed.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_initial_clock(HANDLE layer1, int *clock)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
printf("Trying to find an initial clock value.\n");
|
||||||
|
|
||||||
|
for (i = 0; i < 1000; ++i) {
|
||||||
|
int rc;
|
||||||
|
int cor = i * 150;
|
||||||
|
rc = wait_for_sync(layer1, cor, calib, source);
|
||||||
|
if (rc == 1) {
|
||||||
|
printf("Found initial clock offset: %d\n", cor);
|
||||||
|
*clock = cor;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
CHECK_RC_MSG(rc, "Failed to set new clock value.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
cor = i * -150;
|
||||||
|
rc = wait_for_sync(layer1, cor, calib, source);
|
||||||
|
if (rc == 1) {
|
||||||
|
printf("Found initial clock offset: %d\n", cor);
|
||||||
|
*clock = cor;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
CHECK_RC_MSG(rc, "Failed to set new clock value.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int calib_clock(void)
|
||||||
|
{
|
||||||
|
int rc, cor = initial_cor;
|
||||||
|
float mean_rssi;
|
||||||
|
HANDLE layer1;
|
||||||
|
|
||||||
|
rc = power_scan(band, cal_arfcn, 10, &mean_rssi);
|
||||||
|
CHECK_RC_MSG(rc, "ARFCN measurement scan failed");
|
||||||
|
if (mean_rssi < -118.0f)
|
||||||
|
printf("ARFCN has weak signal for calibration: %f\n", mean_rssi);
|
||||||
|
|
||||||
|
/* initial lock */
|
||||||
|
rc = follow_sch(band, cal_arfcn, calib, source, &layer1);
|
||||||
|
if (rc == -23)
|
||||||
|
rc = find_initial_clock(layer1, &cor);
|
||||||
|
CHECK_RC_MSG(rc, "Following SCH failed");
|
||||||
|
|
||||||
|
/* now try to calibrate it */
|
||||||
|
rc = set_clock_cor(cor, calib, source);
|
||||||
|
CHECK_RC_MSG(rc, "Clock setup failed.");
|
||||||
|
|
||||||
|
calib_clock_after_sync(&layer1);
|
||||||
|
|
||||||
|
rc = mph_close(layer1);
|
||||||
|
CHECK_RC_MSG(rc, "MPH-Close");
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcch_follow(void)
|
||||||
|
{
|
||||||
|
int rc, cor = initial_cor;
|
||||||
|
float mean_rssi;
|
||||||
|
HANDLE layer1;
|
||||||
|
|
||||||
|
rc = power_scan(band, cal_arfcn, 10, &mean_rssi);
|
||||||
|
CHECK_RC_MSG(rc, "ARFCN measurement scan failed");
|
||||||
|
if (mean_rssi < -118.0f)
|
||||||
|
printf("ARFCN has weak signal for calibration: %f\n", mean_rssi);
|
||||||
|
|
||||||
|
/* initial lock */
|
||||||
|
rc = follow_sch(band, cal_arfcn, calib, source, &layer1);
|
||||||
|
if (rc == -23)
|
||||||
|
rc = find_initial_clock(layer1, &cor);
|
||||||
|
CHECK_RC_MSG(rc, "Following SCH failed");
|
||||||
|
|
||||||
|
/* identify the BSIC and set it as TSC */
|
||||||
|
rc = find_bsic();
|
||||||
|
CHECK_COND_MSG(rc < 0, rc, "Identifying the BSIC failed");
|
||||||
|
rc = set_tsc_from_bsic(layer1, rc);
|
||||||
|
CHECK_RC_MSG(rc, "Setting the TSC failed");
|
||||||
|
|
||||||
|
|
||||||
|
/* follow the bcch */
|
||||||
|
rc = follow_bcch(layer1);
|
||||||
|
CHECK_RC_MSG(rc, "Follow BCCH");
|
||||||
|
|
||||||
|
/* now wait for the PhDataInd */
|
||||||
|
for (;;) {
|
||||||
|
uint8_t data[23];
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
rc = wait_for_data(data, &size);
|
||||||
|
if (rc == 1)
|
||||||
|
continue;
|
||||||
|
CHECK_RC_MSG(rc, "No Data Indication");
|
||||||
|
printf("Data: %s\n", osmo_hexdump(data, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = mph_close(layer1);
|
||||||
|
CHECK_RC_MSG(rc, "MPH-Close");
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
handle_options(argc, argv);
|
||||||
|
printf("Initializing the Layer1\n");
|
||||||
|
rc = initialize_layer1(dsp_flags);
|
||||||
|
CHECK_RC(rc);
|
||||||
|
|
||||||
|
printf("Fetching system info.\n");
|
||||||
|
rc = print_system_info();
|
||||||
|
CHECK_RC(rc);
|
||||||
|
|
||||||
|
printf("Opening RF frontend with clock(%d) and correction(%d)\n",
|
||||||
|
calib, initial_cor);
|
||||||
|
rc = activate_rf_frontend(calib, initial_cor);
|
||||||
|
CHECK_RC(rc);
|
||||||
|
|
||||||
|
if (action == ACTION_SCAN)
|
||||||
|
return scan_band();
|
||||||
|
else if (action == ACTION_BCCH)
|
||||||
|
return bcch_follow();
|
||||||
|
else
|
||||||
|
return calib_clock();
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
771
contrib/sysmobts-calib/sysmobts-layer1.c
Normal file
771
contrib/sysmobts-calib/sysmobts-layer1.c
Normal file
@@ -0,0 +1,771 @@
|
|||||||
|
/* Layer1 handling for the DSP/FPGA */
|
||||||
|
/*
|
||||||
|
* (C) 2012 Holger Hans Peter Freyther
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <sysmocom/femtobts/superfemto.h>
|
||||||
|
#include <sysmocom/femtobts/gsml1prim.h>
|
||||||
|
|
||||||
|
#include "sysmobts-layer1.h"
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(ar) (sizeof(ar)/sizeof((ar)[0]))
|
||||||
|
|
||||||
|
#define BTS_DSP2ARM "/dev/msgq/superfemto_dsp2arm"
|
||||||
|
#define BTS_ARM2DSP "/dev/msgq/superfemto_arm2dsp"
|
||||||
|
#define L1_SIG_ARM2DSP "/dev/msgq/gsml1_sig_arm2dsp"
|
||||||
|
#define L1_SIG_DSP2ARM "/dev/msgq/gsml1_sig_dsp2arm"
|
||||||
|
|
||||||
|
int set_clock_cor(int clock_cor, int calib, int source);
|
||||||
|
static int wait_read_ignore(int seconds);
|
||||||
|
|
||||||
|
static int sys_dsp2arm = -1,
|
||||||
|
sys_arm2dsp = -1,
|
||||||
|
sig_dsp2arm = -1,
|
||||||
|
sig_arm2dsp = -1;
|
||||||
|
|
||||||
|
static int sync_indicated = 0;
|
||||||
|
static int time_indicated = 0;
|
||||||
|
|
||||||
|
static int open_devices()
|
||||||
|
{
|
||||||
|
sys_dsp2arm = open(BTS_DSP2ARM, O_RDONLY);
|
||||||
|
if (sys_dsp2arm == -1) {
|
||||||
|
perror("Failed to open dsp2arm system queue");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_arm2dsp = open(BTS_ARM2DSP, O_WRONLY);
|
||||||
|
if (sys_arm2dsp == -1) {
|
||||||
|
perror("Failed to open arm2dsp system queue");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
sig_dsp2arm = open(L1_SIG_DSP2ARM, O_RDONLY);
|
||||||
|
if (sig_dsp2arm == -1) {
|
||||||
|
perror("Failed to open dsp2arm sig queue");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
sig_arm2dsp = open(L1_SIG_ARM2DSP, O_WRONLY);
|
||||||
|
if (sig_arm2dsp == -1) {
|
||||||
|
perror("Failed to open arm2dsp sig queue");
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a primitive to the system queue
|
||||||
|
*/
|
||||||
|
static int send_primitive(int primitive, SuperFemto_Prim_t *prim)
|
||||||
|
{
|
||||||
|
prim->id = primitive;
|
||||||
|
return write(sys_arm2dsp, prim, sizeof(*prim)) != sizeof(*prim);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for a confirmation
|
||||||
|
*/
|
||||||
|
static int wait_primitive(int wait_for, SuperFemto_Prim_t *prim)
|
||||||
|
{
|
||||||
|
memset(prim, 0, sizeof(*prim));
|
||||||
|
int rc = read(sys_dsp2arm, prim, sizeof(*prim));
|
||||||
|
if (rc != sizeof(*prim)) {
|
||||||
|
printf("Short read in %s: %d\n", __func__, rc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prim->id != wait_for) {
|
||||||
|
printf("Got primitive %d but waited for %d\n",
|
||||||
|
prim->id, wait_for);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Cnf for the Req, assume it is a +1 */
|
||||||
|
static int answer_for(int primitive)
|
||||||
|
{
|
||||||
|
return primitive + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int send_recv_primitive(int p, SuperFemto_Prim_t *prim)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
rc = send_primitive(p, prim);
|
||||||
|
if (rc != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
rc = wait_primitive(answer_for(p), prim);
|
||||||
|
if (rc != 0)
|
||||||
|
return -2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int answer_for_sig(int prim)
|
||||||
|
{
|
||||||
|
static const GsmL1_PrimId_t cnf[] = {
|
||||||
|
[GsmL1_PrimId_MphInitReq] = GsmL1_PrimId_MphInitCnf,
|
||||||
|
[GsmL1_PrimId_MphCloseReq] = GsmL1_PrimId_MphCloseCnf,
|
||||||
|
[GsmL1_PrimId_MphConnectReq] = GsmL1_PrimId_MphConnectCnf,
|
||||||
|
[GsmL1_PrimId_MphActivateReq] = GsmL1_PrimId_MphActivateCnf,
|
||||||
|
[GsmL1_PrimId_MphConfigReq] = GsmL1_PrimId_MphConfigCnf,
|
||||||
|
[GsmL1_PrimId_MphMeasureReq] = GsmL1_PrimId_MphMeasureCnf,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (prim < 0 || prim >= ARRAY_SIZE(cnf)) {
|
||||||
|
printf("Unknown primitive: %d\n", prim);
|
||||||
|
exit(-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnf[prim];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_indication(int prim)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
prim == GsmL1_PrimId_MphTimeInd ||
|
||||||
|
prim == GsmL1_PrimId_MphSyncInd ||
|
||||||
|
prim == GsmL1_PrimId_PhConnectInd ||
|
||||||
|
prim == GsmL1_PrimId_PhReadyToSendInd ||
|
||||||
|
prim == GsmL1_PrimId_PhDataInd ||
|
||||||
|
prim == GsmL1_PrimId_PhRaInd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int send_recv_sig_prim(int p, GsmL1_Prim_t *prim)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
prim->id = p;
|
||||||
|
rc = write(sig_arm2dsp, prim, sizeof(*prim));
|
||||||
|
if (rc != sizeof(*prim)) {
|
||||||
|
printf("Failed to write: %d\n", rc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc = read(sig_dsp2arm, prim, sizeof(*prim));
|
||||||
|
if (rc != sizeof(*prim)) {
|
||||||
|
printf("Failed to read: %d\n", rc);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
} while (is_indication(prim->id));
|
||||||
|
|
||||||
|
if (prim->id != answer_for_sig(p)) {
|
||||||
|
printf("Wrong L1 result got %d wanted %d for prim: %d\n",
|
||||||
|
prim->id, answer_for_sig(p), p);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wait_for_indication(int p, GsmL1_Prim_t *prim)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
memset(prim, 0, sizeof(*prim));
|
||||||
|
|
||||||
|
struct timespec start_time, now_time;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: select.... with timeout. The below will work 99% as we will
|
||||||
|
* get time indications very soonish after the connect
|
||||||
|
*/
|
||||||
|
for (;;) {
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now_time);
|
||||||
|
if (now_time.tv_sec - start_time.tv_sec > 10) {
|
||||||
|
printf("Timeout waiting for indication.\n");
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = read(sig_dsp2arm, prim, sizeof(*prim));
|
||||||
|
if (rc != sizeof(*prim)) {
|
||||||
|
printf("Failed to read.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_indication(prim->id)) {
|
||||||
|
printf("No indication: %d\n", prim->id);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p != prim->id && prim->id == GsmL1_PrimId_MphSyncInd) {
|
||||||
|
printf("Got sync.\n");
|
||||||
|
sync_indicated = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (p != prim->id && prim->id == GsmL1_PrimId_MphTimeInd) {
|
||||||
|
time_indicated = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p != prim->id) {
|
||||||
|
printf("Wrong indication got %d wanted %d\n",
|
||||||
|
prim->id, p);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_trace_flags(uint32_t dsp)
|
||||||
|
{
|
||||||
|
SuperFemto_Prim_t prim;
|
||||||
|
memset(&prim, 0, sizeof(prim));
|
||||||
|
|
||||||
|
prim.u.setTraceFlagsReq.u32Tf = dsp;
|
||||||
|
return send_primitive(SuperFemto_PrimId_SetTraceFlagsReq, &prim);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reset_and_wait()
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
SuperFemto_Prim_t prim;
|
||||||
|
memset(&prim, 0, sizeof(prim));
|
||||||
|
|
||||||
|
rc = send_recv_primitive(SuperFemto_PrimId_Layer1ResetReq, &prim);
|
||||||
|
if (rc != 0)
|
||||||
|
return -1;
|
||||||
|
if (prim.u.layer1ResetCnf.status != GsmL1_Status_Success)
|
||||||
|
return -2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the message queues and (re-)initialize the DSP and FPGA
|
||||||
|
*/
|
||||||
|
int initialize_layer1(uint32_t dsp_flags)
|
||||||
|
{
|
||||||
|
if (open_devices() != 0) {
|
||||||
|
printf("Failed to open devices.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set_trace_flags(dsp_flags) != 0) {
|
||||||
|
printf("Failed to set dsp flags.\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
if (reset_and_wait() != 0) {
|
||||||
|
printf("Failed to reset the firmware.\n");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print systems infos
|
||||||
|
*/
|
||||||
|
int print_system_info()
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
SuperFemto_Prim_t prim;
|
||||||
|
memset(&prim, 0, sizeof(prim));
|
||||||
|
|
||||||
|
rc = send_recv_primitive(SuperFemto_PrimId_SystemInfoReq, &prim);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Failed to send SystemInfoRequest.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prim.u.systemInfoCnf.status != GsmL1_Status_Success) {
|
||||||
|
printf("Failed to request SystemInfoRequest.\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INFO_DSP(x) x.u.systemInfoCnf.dspVersion
|
||||||
|
#define INFO_FPGA(x) x.u.systemInfoCnf.fpgaVersion
|
||||||
|
#ifdef FEMTOBTS_NO_BOARD_VERSION
|
||||||
|
#define BOARD_REV(x) -1
|
||||||
|
#define BOARD_OPT(x) -1
|
||||||
|
#else
|
||||||
|
#define BOARD_REV(x) x.u.systemInfoCnf.boardVersion.rev
|
||||||
|
#define BOARD_OPT(x) x.u.systemInfoCnf.boardVersion.option
|
||||||
|
#endif
|
||||||
|
|
||||||
|
printf("DSP v%d.%d.%d FPGA v%d.%d.%d Rev: %d Option: %d\n",
|
||||||
|
INFO_DSP(prim).major, INFO_DSP(prim).minor, INFO_DSP(prim).build,
|
||||||
|
INFO_FPGA(prim).major, INFO_FPGA(prim).minor, INFO_FPGA(prim).build,
|
||||||
|
BOARD_REV(prim), BOARD_OPT(prim));
|
||||||
|
#undef INFO_DSP
|
||||||
|
#undef INFO_FPGA
|
||||||
|
#undef BOARD_REV
|
||||||
|
#undef BOARD_OPT
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int activate_rf_frontend(int clock_source, int initial_cor)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
SuperFemto_Prim_t prim;
|
||||||
|
memset(&prim, 0, sizeof(prim));
|
||||||
|
|
||||||
|
prim.u.activateRfReq.timing.u8TimSrc = 1;
|
||||||
|
prim.u.activateRfReq.msgq.u8UseTchMsgq = 0;
|
||||||
|
prim.u.activateRfReq.msgq.u8UsePdtchMsgq = 0;
|
||||||
|
|
||||||
|
prim.u.activateRfReq.rfTrx.iClkCor = initial_cor;
|
||||||
|
prim.u.activateRfReq.rfTrx.clkSrc = clock_source;
|
||||||
|
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
|
||||||
|
prim.u.activateRfReq.rfRx.iClkCor = initial_cor;
|
||||||
|
prim.u.activateRfReq.rfRx.clkSrc = clock_source;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
rc = send_recv_primitive(SuperFemto_PrimId_ActivateRfReq, &prim);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mph_init(int band, int arfcn, HANDLE *layer1)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GsmL1_Prim_t prim;
|
||||||
|
memset(&prim, 0, sizeof(prim));
|
||||||
|
|
||||||
|
prim.u.mphInitReq.deviceParam.devType = GsmL1_DevType_Rxd;
|
||||||
|
prim.u.mphInitReq.deviceParam.freqBand = band;
|
||||||
|
prim.u.mphInitReq.deviceParam.u16Arfcn = arfcn;
|
||||||
|
prim.u.mphInitReq.deviceParam.u16BcchArfcn = arfcn;
|
||||||
|
prim.u.mphInitReq.deviceParam.fRxPowerLevel = -75.f;
|
||||||
|
prim.u.mphInitReq.deviceParam.u8AutoTA = 1;
|
||||||
|
|
||||||
|
rc = send_recv_sig_prim(GsmL1_PrimId_MphInitReq, &prim);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Failed to initialize the physical channel.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prim.u.mphInitCnf.status != GsmL1_Status_Success) {
|
||||||
|
printf("MPH Init failed.\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (prim.u.mphInitCnf.freqBand != band) {
|
||||||
|
printf("Layer1 ignored the band: %d\n",
|
||||||
|
prim.u.mphInitCnf.freqBand);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*layer1 = prim.u.mphInitCnf.hLayer1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mph_close(HANDLE layer1)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GsmL1_Prim_t prim;
|
||||||
|
memset(&prim, 0, sizeof(prim));
|
||||||
|
|
||||||
|
prim.u.mphCloseReq.hLayer1 = layer1;
|
||||||
|
rc = send_recv_sig_prim(GsmL1_PrimId_MphCloseReq, &prim);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Failed to close the MPH\n");
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
if (prim.u.mphCloseCnf.status != GsmL1_Status_Success) {
|
||||||
|
printf("MPH Close failed.\n");
|
||||||
|
return -7;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int follow_sch(int band, int arfcn, int clock, int ref, HANDLE *layer1)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GsmL1_Prim_t prim;
|
||||||
|
|
||||||
|
time_indicated = 0;
|
||||||
|
sync_indicated = 0;
|
||||||
|
|
||||||
|
rc = mph_init(band, arfcn, layer1);
|
||||||
|
if (rc != 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* 1.) Connect */
|
||||||
|
memset(&prim, 0, sizeof(prim));
|
||||||
|
prim.u.mphConnectReq.hLayer1 = *layer1;
|
||||||
|
prim.u.mphConnectReq.u8Tn = 0;
|
||||||
|
prim.u.mphConnectReq.logChComb = GsmL1_LogChComb_IV;
|
||||||
|
printf("FIVE\n");
|
||||||
|
rc = send_recv_sig_prim(GsmL1_PrimId_MphConnectReq, &prim);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Failed to connect.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (prim.u.mphConnectCnf.status != GsmL1_Status_Success) {
|
||||||
|
printf("Connect failed.\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
if (prim.u.mphConnectCnf.u8Tn != 0) {
|
||||||
|
printf("Wrong timeslot.\n");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2.) Activate */
|
||||||
|
memset(&prim, 0, sizeof(prim));
|
||||||
|
prim.u.mphActivateReq.hLayer1 = *layer1;
|
||||||
|
prim.u.mphActivateReq.u8Tn = 0;
|
||||||
|
prim.u.mphActivateReq.sapi = GsmL1_Sapi_Sch;
|
||||||
|
prim.u.mphActivateReq.dir = GsmL1_Dir_RxDownlink;
|
||||||
|
rc = send_recv_sig_prim(GsmL1_PrimId_MphActivateReq, &prim);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Activation failed.\n");
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
if (prim.u.mphActivateCnf.status != GsmL1_Status_Success) {
|
||||||
|
printf("Activation not successful.\n");
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3.) Wait for indication... TODO: check... */
|
||||||
|
printf("Waiting for connect indication.\n");
|
||||||
|
rc = wait_for_indication(GsmL1_PrimId_PhConnectInd, &prim);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Didn't get a connect indication.\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4.) Indication Syndication TODO: check... */
|
||||||
|
if (!sync_indicated) {
|
||||||
|
printf("Waiting for sync indication.\n");
|
||||||
|
rc = wait_for_indication(GsmL1_PrimId_MphSyncInd, &prim);
|
||||||
|
if (rc < 0) {
|
||||||
|
printf("Didn't get a sync indication.\n");
|
||||||
|
return -23;
|
||||||
|
} else if (rc == 0) {
|
||||||
|
if (!prim.u.mphSyncInd.u8Synced) {
|
||||||
|
printf("Failed to get sync.\n");
|
||||||
|
return -23;
|
||||||
|
} else {
|
||||||
|
printf("Synced.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("Already synced.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int follow_bcch(HANDLE layer1)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GsmL1_Prim_t prim;
|
||||||
|
|
||||||
|
/* 1.) Activate BCCH... */
|
||||||
|
memset(&prim, 0, sizeof(prim));
|
||||||
|
prim.u.mphActivateReq.hLayer1 = layer1;
|
||||||
|
prim.u.mphActivateReq.u8Tn = 0;
|
||||||
|
prim.u.mphActivateReq.sapi = GsmL1_Sapi_Bcch;
|
||||||
|
prim.u.mphActivateReq.dir = GsmL1_Dir_RxDownlink;
|
||||||
|
|
||||||
|
rc = send_recv_sig_prim(GsmL1_PrimId_MphActivateReq, &prim);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Activation failed.\n");
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
if (prim.u.mphActivateCnf.status != GsmL1_Status_Success) {
|
||||||
|
printf("Activation not successful.\n");
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2.) Wait for indication... */
|
||||||
|
printf("Waiting for connect indication.\n");
|
||||||
|
rc = wait_for_indication(GsmL1_PrimId_PhConnectInd, &prim);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Didn't get a connect indication.\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prim.u.phConnectInd.sapi != GsmL1_Sapi_Bcch) {
|
||||||
|
printf("Got a connect indication for the wrong type: %d\n",
|
||||||
|
prim.u.phConnectInd.sapi);
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3.) Wait for PhDataInd... */
|
||||||
|
printf("Waiting for BCCH data.\n");
|
||||||
|
rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Didn't get BCCH data.\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_bsic(void)
|
||||||
|
{
|
||||||
|
int rc, i;
|
||||||
|
GsmL1_Prim_t prim;
|
||||||
|
|
||||||
|
printf("Waiting for SCH data.\n");
|
||||||
|
for (i = 0; i < 10; ++i) {
|
||||||
|
uint8_t bsic;
|
||||||
|
rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim);
|
||||||
|
if (rc < 0) {
|
||||||
|
printf("Didn't get SCH data.\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
if (prim.u.phDataInd.sapi != GsmL1_Sapi_Sch)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bsic = (prim.u.phDataInd.msgUnitParam.u8Buffer[0] >> 2) & 0xFF;
|
||||||
|
return bsic;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Giving up finding the SCH\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_tsc_from_bsic(HANDLE layer1, int bsic)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
int tsc = bsic & 0x7;
|
||||||
|
GsmL1_Prim_t prim;
|
||||||
|
|
||||||
|
memset(&prim, 0, sizeof(prim));
|
||||||
|
prim.u.mphConfigReq.hLayer3 = 0x23;
|
||||||
|
prim.u.mphConfigReq.hLayer1 = layer1;
|
||||||
|
prim.u.mphConfigReq.cfgParamId = GsmL1_ConfigParamId_SetNbTsc;
|
||||||
|
prim.u.mphConfigReq.cfgParams.setNbTsc.u8NbTsc = tsc;
|
||||||
|
rc = send_recv_sig_prim(GsmL1_PrimId_MphConfigReq, &prim);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Failed to send configure.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prim.u.mphConfigCnf.status != GsmL1_Status_Success) {
|
||||||
|
printf("Failed to set the config cnf.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_clock_cor(int clock_cor, int calib, int source)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
SuperFemto_Prim_t prim;
|
||||||
|
memset(&prim, 0, sizeof(prim));
|
||||||
|
|
||||||
|
prim.u.rfClockSetupReq.rfTrx.iClkCor = clock_cor;
|
||||||
|
prim.u.rfClockSetupReq.rfTrx.clkSrc = calib;
|
||||||
|
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
|
||||||
|
prim.u.rfClockSetupReq.rfRx.iClkCor = clock_cor;
|
||||||
|
prim.u.rfClockSetupReq.rfRx.clkSrc = calib;
|
||||||
|
#endif
|
||||||
|
prim.u.rfClockSetupReq.rfTrxClkCal.clkSrc = source;
|
||||||
|
|
||||||
|
rc = send_recv_primitive(SuperFemto_PrimId_RfClockSetupReq, &prim);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Failed to set the clock setup.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (prim.u.rfClockSetupCnf.status != GsmL1_Status_Success) {
|
||||||
|
printf("Clock setup was not successfull.\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rf_clock_info(HANDLE *layer1, int *clkErr, int *clkErrRes)
|
||||||
|
{
|
||||||
|
SuperFemto_Prim_t prim;
|
||||||
|
memset(&prim, 0, sizeof(prim));
|
||||||
|
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* reset the counter */
|
||||||
|
prim.u.rfClockInfoReq.u8RstClkCal = 1;
|
||||||
|
rc = send_recv_primitive(SuperFemto_PrimId_RfClockInfoReq, &prim);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Failed to reset the clock info.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wait for a value */
|
||||||
|
wait_read_ignore(15);
|
||||||
|
|
||||||
|
/* ask for the current counter/error */
|
||||||
|
memset(&prim, 0, sizeof(prim));
|
||||||
|
prim.u.rfClockInfoReq.u8RstClkCal = 0;
|
||||||
|
rc = send_recv_primitive(SuperFemto_PrimId_RfClockInfoReq, &prim);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Failed to get the clock info.\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Error: %d Res: %d\n",
|
||||||
|
prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErr,
|
||||||
|
prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErrRes);
|
||||||
|
*clkErr = prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErr;
|
||||||
|
*clkErrRes = prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErrRes;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int power_scan(int band, int arfcn, int duration, float *mean_rssi)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
HANDLE layer1;
|
||||||
|
GsmL1_Prim_t prim;
|
||||||
|
|
||||||
|
/* init */
|
||||||
|
rc = mph_init(band, arfcn, &layer1);
|
||||||
|
if (rc != 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* mph measure request */
|
||||||
|
memset(&prim, 0, sizeof(prim));
|
||||||
|
prim.u.mphMeasureReq.hLayer1 = layer1;
|
||||||
|
prim.u.mphMeasureReq.u32Duration = duration;
|
||||||
|
rc = send_recv_sig_prim(GsmL1_PrimId_MphMeasureReq, &prim);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Failed to send measurement request.\n");
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prim.u.mphMeasureCnf.status != GsmL1_Status_Success) {
|
||||||
|
printf("MphMeasureReq was not confirmed.\n");
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
*mean_rssi = prim.u.mphMeasureCnf.fMeanRssi;
|
||||||
|
|
||||||
|
/* close */
|
||||||
|
rc = mph_close(layer1);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for indication...
|
||||||
|
*/
|
||||||
|
int wait_for_sync(HANDLE layer1, int cor, int calib, int source)
|
||||||
|
{
|
||||||
|
GsmL1_Prim_t prim;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = set_clock_cor(cor, calib, source);
|
||||||
|
if (rc != 0) {
|
||||||
|
printf("Failed to set the clock correction.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_indicated = 0;
|
||||||
|
rc = wait_for_indication(GsmL1_PrimId_MphSyncInd, &prim);
|
||||||
|
if (rc < 0 && rc != -4) {
|
||||||
|
return rc;
|
||||||
|
} else if (rc == 0) {
|
||||||
|
if (!prim.u.mphSyncInd.u8Synced) {
|
||||||
|
printf("Failed to get sync.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
printf("Synced.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wait_for_data(uint8_t *data, size_t *size)
|
||||||
|
{
|
||||||
|
GsmL1_Prim_t prim;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
if (prim.u.phDataInd.sapi == GsmL1_Sapi_Sch)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
*size = prim.u.phDataInd.msgUnitParam.u8Size;
|
||||||
|
memcpy(data, prim.u.phDataInd.msgUnitParam.u8Buffer, *size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure the pipe is not running full.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int wait_read_ignore(int seconds)
|
||||||
|
{
|
||||||
|
int max, rc;
|
||||||
|
fd_set fds;
|
||||||
|
struct timeval timeout;
|
||||||
|
|
||||||
|
max = sys_dsp2arm > sig_dsp2arm ? sys_dsp2arm : sig_dsp2arm;
|
||||||
|
|
||||||
|
timeout.tv_sec = seconds;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(sys_dsp2arm, &fds);
|
||||||
|
FD_SET(sig_dsp2arm, &fds);
|
||||||
|
|
||||||
|
|
||||||
|
rc = select(max + 1, &fds, NULL, NULL, &timeout);
|
||||||
|
if (rc == -1) {
|
||||||
|
printf("Failed to select.\n");
|
||||||
|
return -1;
|
||||||
|
} else if (rc) {
|
||||||
|
if (FD_ISSET(sys_dsp2arm, &fds)) {
|
||||||
|
SuperFemto_Prim_t prim;
|
||||||
|
rc = read(sys_dsp2arm, &prim, sizeof(prim));
|
||||||
|
if (rc != sizeof(prim)) {
|
||||||
|
perror("Failed to read system primitive");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (FD_ISSET(sig_dsp2arm, &fds)) {
|
||||||
|
GsmL1_Prim_t prim;
|
||||||
|
rc = read(sig_dsp2arm, &prim, sizeof(prim));
|
||||||
|
if (rc != sizeof(prim)) {
|
||||||
|
perror("Failed to read signal primitiven");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (timeout.tv_sec <= 0 && timeout.tv_usec <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __linux__
|
||||||
|
#error "Non portable code"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
31
contrib/sysmobts-calib/sysmobts-layer1.h
Normal file
31
contrib/sysmobts-calib/sysmobts-layer1.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#ifndef SYSMOBTS_LAYER_H
|
||||||
|
#define SYSMOBTS_LAYER_H
|
||||||
|
|
||||||
|
#include <sysmocom/femtobts/superfemto.h>
|
||||||
|
|
||||||
|
/* older header files don't have this */
|
||||||
|
#ifndef SUPERFEMTO_API
|
||||||
|
#define SUPERFEMTO_API(x,y,z) ((x << 16) + (y << 8) + z)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SUPERFEMTO_API_VERSION
|
||||||
|
#define SUPERFEMTO_API_VERSION SUPERFEMTO_API(2,2,0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
extern int initialize_layer1(uint32_t dsp_flags);
|
||||||
|
extern int print_system_info();
|
||||||
|
extern int activate_rf_frontend(int clock_source, int clock_cor);
|
||||||
|
extern int power_scan(int band, int arfcn, int duration, float *mean_rssi);
|
||||||
|
extern int follow_sch(int band, int arfcn, int calib, int reference, HANDLE *layer1);
|
||||||
|
extern int follow_bch(HANDLE layer1);
|
||||||
|
extern int find_bsic(void);
|
||||||
|
extern int set_tsc_from_bsic(HANDLE layer1, int bsic);
|
||||||
|
extern int set_clock_cor(int clock_corr, int calib, int source);
|
||||||
|
extern int rf_clock_info(HANDLE *layer1, int *clkErr, int *clkErrRes);
|
||||||
|
extern int mph_close(HANDLE layer1);
|
||||||
|
extern int wait_for_sync(HANDLE layer1, int cor, int calib, int source);
|
||||||
|
extern int follow_bcch(HANDLE layer1);
|
||||||
|
extern int wait_for_data(uint8_t *data, size_t *size);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -9,8 +9,6 @@
|
|||||||
# Description:
|
# Description:
|
||||||
### END INIT INFO
|
### END INIT INFO
|
||||||
|
|
||||||
. /etc/default/rcS
|
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
start)
|
start)
|
||||||
/usr/bin/screen -d -m -c /etc/osmocom/screenrc-sysmobts
|
/usr/bin/screen -d -m -c /etc/osmocom/screenrc-sysmobts
|
||||||
|
|||||||
11
contrib/sysmobts.service
Normal file
11
contrib/sysmobts.service
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=sysmocom sysmoBTS
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStartPre=/bin/sh -c 'echo 0 > /sys/class/leds/activity_led/brightness'
|
||||||
|
ExecStartPre=/bin/sh -c 'cat /lib/firmware/sysmobts-v?.out > /dev/dspdl_dm644x_0'
|
||||||
|
ExecStart=/usr/bin/sysmobts -s -c /etc/osmocom/osmo-bts.cfg
|
||||||
|
ExecStartPost=/bin/sh -c 'echo 0 > /sys/class/leds/activity_led/brightness'
|
||||||
|
Restart=always
|
||||||
|
RestartSec=2
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
!!
|
!!
|
||||||
!
|
!
|
||||||
log stderr
|
log stderr
|
||||||
logging color 1
|
logging color 0
|
||||||
logging timestamp 0
|
logging timestamp 0
|
||||||
logging level all everything
|
logging level all everything
|
||||||
logging level rsl info
|
logging level rsl info
|
||||||
@@ -13,15 +13,9 @@ log stderr
|
|||||||
logging level meas notice
|
logging level meas notice
|
||||||
logging level pag info
|
logging level pag info
|
||||||
logging level l1c info
|
logging level l1c info
|
||||||
logging level l1p debug
|
logging level l1p info
|
||||||
logging level dsp debug
|
logging level dsp debug
|
||||||
logging level abis notice
|
logging level abis notice
|
||||||
logging level lglobal notice
|
|
||||||
logging level llapdm notice
|
|
||||||
logging level linp notice
|
|
||||||
logging level lmux notice
|
|
||||||
logging level lmi notice
|
|
||||||
logging level lmib notice
|
|
||||||
!
|
!
|
||||||
line vty
|
line vty
|
||||||
no login
|
no login
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \
|
noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \
|
||||||
oml.h paging.h rsl.h signal.h vty.h
|
oml.h paging.h rsl.h signal.h vty.h amr.h pcu_if.h
|
||||||
|
|||||||
14
include/osmo-bts/amr.h
Normal file
14
include/osmo-bts/amr.h
Normal 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 */
|
||||||
@@ -18,13 +18,12 @@ int trx_link_estab(struct gsm_bts_trx *trx);
|
|||||||
void bts_new_si(void *arg);
|
void bts_new_si(void *arg);
|
||||||
void bts_setup_slot(struct gsm_bts_trx_ts *slot, uint8_t comb);
|
void bts_setup_slot(struct gsm_bts_trx_ts *slot, uint8_t comb);
|
||||||
|
|
||||||
int lchan_init_lapdm(struct gsm_lchan *lchan);
|
|
||||||
|
|
||||||
int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg);
|
int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg);
|
||||||
struct msgb *bts_agch_dequeue(struct gsm_bts *bts);
|
struct msgb *bts_agch_dequeue(struct gsm_bts *bts);
|
||||||
|
|
||||||
uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time);
|
uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time);
|
||||||
uint8_t *lchan_sacch_get(struct gsm_lchan *lchan, struct gsm_time *g_time);
|
uint8_t *lchan_sacch_get(struct gsm_lchan *lchan, struct gsm_time *g_time);
|
||||||
|
int lchan_init_lapdm(struct gsm_lchan *lchan);
|
||||||
|
|
||||||
#endif /* _BTS_H */
|
#endif /* _BTS_H */
|
||||||
|
|
||||||
|
|||||||
@@ -30,10 +30,16 @@ int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
|||||||
int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp);
|
int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp);
|
||||||
int bts_model_rsl_chan_rel(struct gsm_lchan *lchan);
|
int bts_model_rsl_chan_rel(struct gsm_lchan *lchan);
|
||||||
int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan);
|
int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan);
|
||||||
|
int bts_model_rsl_mode_modify(struct gsm_lchan *lchan);
|
||||||
|
|
||||||
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx);
|
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx);
|
||||||
|
|
||||||
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, uint8_t *rtp_pl,
|
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
||||||
unsigned int rtp_pl_len);
|
unsigned int rtp_pl_len);
|
||||||
|
|
||||||
|
int bts_model_vty_init(struct gsm_bts *bts);
|
||||||
|
|
||||||
|
void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts);
|
||||||
|
void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -7,9 +7,13 @@
|
|||||||
|
|
||||||
#include <osmo-bts/paging.h>
|
#include <osmo-bts/paging.h>
|
||||||
|
|
||||||
|
struct pcu_sock_state;
|
||||||
|
|
||||||
struct gsm_network {
|
struct gsm_network {
|
||||||
struct llist_head bts_list;
|
struct llist_head bts_list;
|
||||||
unsigned int num_bts;
|
unsigned int num_bts;
|
||||||
|
uint16_t mcc, mnc;
|
||||||
|
struct pcu_sock_state *pcu_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* data structure for BTS related data specific to the BTS role */
|
/* data structure for BTS related data specific to the BTS role */
|
||||||
@@ -36,6 +40,10 @@ struct gsm_bts_role_bts {
|
|||||||
/* Input parameters from OML */
|
/* Input parameters from OML */
|
||||||
int16_t busy_thresh; /* in dBm */
|
int16_t busy_thresh; /* in dBm */
|
||||||
uint16_t averaging_slots;
|
uint16_t averaging_slots;
|
||||||
|
/* Internal data */
|
||||||
|
unsigned int total; /* total nr */
|
||||||
|
unsigned int busy; /* above busy_thresh */
|
||||||
|
unsigned int access; /* access bursts */
|
||||||
} rach;
|
} rach;
|
||||||
} load;
|
} load;
|
||||||
uint8_t ny1;
|
uint8_t ny1;
|
||||||
@@ -44,6 +52,21 @@ struct gsm_bts_role_bts {
|
|||||||
struct paging_state *paging_state;
|
struct paging_state *paging_state;
|
||||||
char *bsc_oml_host;
|
char *bsc_oml_host;
|
||||||
char *rtp_bind_host;
|
char *rtp_bind_host;
|
||||||
|
unsigned int rtp_jitter_buf_ms;
|
||||||
|
struct {
|
||||||
|
uint8_t ciphers;
|
||||||
|
} support;
|
||||||
|
struct {
|
||||||
|
uint8_t tc4_ctr;
|
||||||
|
} si;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum lchan_ciph_state {
|
||||||
|
LCHAN_CIPH_NONE,
|
||||||
|
LCHAN_CIPH_RX_REQ,
|
||||||
|
LCHAN_CIPH_RX_CONF,
|
||||||
|
LCHAN_CIPH_TXRX_REQ,
|
||||||
|
LCHAN_CIPH_TXRX_CONF,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define bts_role_bts(x) ((struct gsm_bts_role_bts *)(x)->role)
|
#define bts_role_bts(x) ((struct gsm_bts_role_bts *)(x)->role)
|
||||||
@@ -57,4 +80,7 @@ static inline struct femtol1_hdl *trx_femtol1_hdl(struct gsm_bts_trx *trx)
|
|||||||
return trx->role_bts.l1h;
|
return trx->role_bts.l1h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state);
|
||||||
|
|
||||||
#endif /* _GSM_DATA_H */
|
#endif /* _GSM_DATA_H */
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ enum {
|
|||||||
DL1C,
|
DL1C,
|
||||||
DL1P,
|
DL1P,
|
||||||
DDSP,
|
DDSP,
|
||||||
|
DPCU,
|
||||||
DABIS,
|
DABIS,
|
||||||
DRTP,
|
DRTP,
|
||||||
DSUM,
|
DSUM,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ int oml_send_msg(struct msgb *msg, int is_mauf);
|
|||||||
int oml_mo_send_msg(struct gsm_abis_mo *mo, struct msgb *msg, uint8_t msg_type);
|
int oml_mo_send_msg(struct gsm_abis_mo *mo, struct msgb *msg, uint8_t msg_type);
|
||||||
int oml_mo_opstart_ack(struct gsm_abis_mo *mo);
|
int oml_mo_opstart_ack(struct gsm_abis_mo *mo);
|
||||||
int oml_mo_opstart_nack(struct gsm_abis_mo *mo, uint8_t nack_cause);
|
int oml_mo_opstart_nack(struct gsm_abis_mo *mo, uint8_t nack_cause);
|
||||||
|
int oml_mo_statechg_ack(struct gsm_abis_mo *mo);
|
||||||
|
|
||||||
/* Change the state and send STATE CHG REP */
|
/* Change the state and send STATE CHG REP */
|
||||||
int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state);
|
int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state);
|
||||||
|
|||||||
@@ -6,9 +6,11 @@
|
|||||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||||
|
|
||||||
struct paging_state;
|
struct paging_state;
|
||||||
|
struct gsm_bts_role_bts;
|
||||||
|
|
||||||
/* initialize paging code */
|
/* initialize paging code */
|
||||||
struct paging_state *paging_init(void *ctx, unsigned int num_paging_max,
|
struct paging_state *paging_init(struct gsm_bts_role_bts *btsb,
|
||||||
|
unsigned int num_paging_max,
|
||||||
unsigned int paging_lifetime);
|
unsigned int paging_lifetime);
|
||||||
|
|
||||||
void paging_reset(struct paging_state *ps);
|
void paging_reset(struct paging_state *ps);
|
||||||
@@ -20,7 +22,17 @@ int paging_si_update(struct paging_state *ps, struct gsm48_control_channel_descr
|
|||||||
int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
|
int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
|
||||||
const uint8_t *identity_lv, uint8_t chan_needed);
|
const uint8_t *identity_lv, uint8_t chan_needed);
|
||||||
|
|
||||||
|
/* Add an IMM.ASS message to the paging queue */
|
||||||
|
int paging_add_imm_ass(struct paging_state *ps, const uint8_t *data,
|
||||||
|
uint8_t len);
|
||||||
|
|
||||||
/* generate paging message for given gsm time */
|
/* generate paging message for given gsm time */
|
||||||
int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt);
|
int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt);
|
||||||
|
|
||||||
|
|
||||||
|
/* inspection methods below */
|
||||||
|
int paging_group_queue_empty(struct paging_state *ps, uint8_t group);
|
||||||
|
int paging_queue_length(struct paging_state *ps);
|
||||||
|
int paging_buffer_space(struct paging_state *ps);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
17
include/osmo-bts/pcu_if.h
Normal file
17
include/osmo-bts/pcu_if.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef _PCU_IF_H
|
||||||
|
#define _PCU_IF_H
|
||||||
|
|
||||||
|
int pcu_tx_info_ind(void);
|
||||||
|
int pcu_tx_rts_req(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
|
||||||
|
uint16_t arfcn, uint8_t block_nr);
|
||||||
|
int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
|
||||||
|
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
|
||||||
|
int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint8_t ra, uint32_t fn);
|
||||||
|
int pcu_tx_time_ind(uint32_t fn);
|
||||||
|
int pcu_tx_pag_req(uint8_t *identity_lv, uint8_t chan_needed);
|
||||||
|
int pcu_tx_pch_data_cnf(uint32_t fn, uint8_t *data, uint8_t len);
|
||||||
|
|
||||||
|
int pcu_sock_init(void);
|
||||||
|
void pcu_sock_exit(void);
|
||||||
|
|
||||||
|
#endif /* _PCU_IF_H */
|
||||||
152
include/osmo-bts/pcuif_proto.h
Normal file
152
include/osmo-bts/pcuif_proto.h
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
#ifndef _PCUIF_PROTO_H
|
||||||
|
#define _PCUIF_PROTO_H
|
||||||
|
|
||||||
|
#define PCU_IF_VERSION 0x04
|
||||||
|
|
||||||
|
/* msg_type */
|
||||||
|
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
|
||||||
|
#define PCU_IF_MSG_DATA_CNF 0x01 /* confirm (e.g. transmission on PCH) */
|
||||||
|
#define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */
|
||||||
|
#define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send request */
|
||||||
|
#define PCU_IF_MSG_RACH_IND 0x22 /* receive RACH */
|
||||||
|
#define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */
|
||||||
|
#define PCU_IF_MSG_ACT_REQ 0x40 /* activate/deactivate PDCH */
|
||||||
|
#define PCU_IF_MSG_TIME_IND 0x52 /* GSM time indication */
|
||||||
|
#define PCU_IF_MSG_PAG_REQ 0x60 /* paging request */
|
||||||
|
|
||||||
|
/* sapi */
|
||||||
|
#define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */
|
||||||
|
#define PCU_IF_SAPI_AGCH 0x02 /* assignment on AGCH */
|
||||||
|
#define PCU_IF_SAPI_PCH 0x03 /* paging/assignment on PCH */
|
||||||
|
#define PCU_IF_SAPI_BCCH 0x04 /* SI on BCCH */
|
||||||
|
#define PCU_IF_SAPI_PDTCH 0x05 /* packet data/control/ccch block */
|
||||||
|
#define PCU_IF_SAPI_PRACH 0x06 /* packet random access channel */
|
||||||
|
#define PCU_IF_SAPI_PTCCH 0x07 /* packet TA control channel */
|
||||||
|
|
||||||
|
/* flags */
|
||||||
|
#define PCU_IF_FLAG_ACTIVE (1 << 0)/* BTS is active */
|
||||||
|
#define PCU_IF_FLAG_SYSMO (1 << 1)/* access PDCH of sysmoBTS directly */
|
||||||
|
#define PCU_IF_FLAG_CS1 (1 << 16)
|
||||||
|
#define PCU_IF_FLAG_CS2 (1 << 17)
|
||||||
|
#define PCU_IF_FLAG_CS3 (1 << 18)
|
||||||
|
#define PCU_IF_FLAG_CS4 (1 << 19)
|
||||||
|
#define PCU_IF_FLAG_MCS1 (1 << 20)
|
||||||
|
#define PCU_IF_FLAG_MCS2 (1 << 21)
|
||||||
|
#define PCU_IF_FLAG_MCS3 (1 << 22)
|
||||||
|
#define PCU_IF_FLAG_MCS4 (1 << 23)
|
||||||
|
#define PCU_IF_FLAG_MCS5 (1 << 24)
|
||||||
|
#define PCU_IF_FLAG_MCS6 (1 << 25)
|
||||||
|
#define PCU_IF_FLAG_MCS7 (1 << 26)
|
||||||
|
#define PCU_IF_FLAG_MCS8 (1 << 27)
|
||||||
|
#define PCU_IF_FLAG_MCS9 (1 << 28)
|
||||||
|
|
||||||
|
struct gsm_pcu_if_data {
|
||||||
|
uint8_t sapi;
|
||||||
|
uint8_t len;
|
||||||
|
uint8_t data[162];
|
||||||
|
uint32_t fn;
|
||||||
|
uint16_t arfcn;
|
||||||
|
uint8_t trx_nr;
|
||||||
|
uint8_t ts_nr;
|
||||||
|
uint8_t block_nr;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_rts_req {
|
||||||
|
uint8_t sapi;
|
||||||
|
uint8_t spare[3];
|
||||||
|
uint32_t fn;
|
||||||
|
uint16_t arfcn;
|
||||||
|
uint8_t trx_nr;
|
||||||
|
uint8_t ts_nr;
|
||||||
|
uint8_t block_nr;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_rach_ind {
|
||||||
|
uint8_t sapi;
|
||||||
|
uint8_t ra;
|
||||||
|
int16_t qta;
|
||||||
|
uint32_t fn;
|
||||||
|
uint16_t arfcn;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_info_trx {
|
||||||
|
uint16_t arfcn;
|
||||||
|
uint8_t pdch_mask; /* PDCH channels per TS */
|
||||||
|
uint8_t spare;
|
||||||
|
uint8_t tsc[8]; /* TSC per channel */
|
||||||
|
uint32_t hlayer1;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_info_ind {
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t flags;
|
||||||
|
struct gsm_pcu_if_info_trx trx[8]; /* TRX infos per BTS */
|
||||||
|
uint8_t bsic;
|
||||||
|
/* RAI */
|
||||||
|
uint16_t mcc, mnc, lac, rac;
|
||||||
|
/* NSE */
|
||||||
|
uint16_t nsei;
|
||||||
|
uint8_t nse_timer[7];
|
||||||
|
uint8_t cell_timer[11];
|
||||||
|
/* cell */
|
||||||
|
uint16_t cell_id;
|
||||||
|
uint16_t repeat_time;
|
||||||
|
uint8_t repeat_count;
|
||||||
|
uint16_t bvci;
|
||||||
|
uint8_t t3142;
|
||||||
|
uint8_t t3169;
|
||||||
|
uint8_t t3191;
|
||||||
|
uint8_t t3193_10ms;
|
||||||
|
uint8_t t3195;
|
||||||
|
uint8_t n3101;
|
||||||
|
uint8_t n3103;
|
||||||
|
uint8_t n3105;
|
||||||
|
uint8_t cv_countdown;
|
||||||
|
uint16_t dl_tbf_ext;
|
||||||
|
uint16_t ul_tbf_ext;
|
||||||
|
uint8_t initial_cs;
|
||||||
|
uint8_t initial_mcs;
|
||||||
|
/* NSVC */
|
||||||
|
uint16_t nsvci[2];
|
||||||
|
uint16_t local_port[2];
|
||||||
|
uint16_t remote_port[2];
|
||||||
|
uint32_t remote_ip[2];
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_act_req {
|
||||||
|
uint8_t activate;
|
||||||
|
uint8_t trx_nr;
|
||||||
|
uint8_t ts_nr;
|
||||||
|
uint8_t spare;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_time_ind {
|
||||||
|
uint32_t fn;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_pag_req {
|
||||||
|
uint8_t sapi;
|
||||||
|
uint8_t chan_needed;
|
||||||
|
uint8_t identity_lv[9];
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if {
|
||||||
|
/* context based information */
|
||||||
|
uint8_t msg_type; /* message type */
|
||||||
|
uint8_t bts_nr; /* bts number */
|
||||||
|
uint8_t spare[2];
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct gsm_pcu_if_data data_req;
|
||||||
|
struct gsm_pcu_if_data data_cnf;
|
||||||
|
struct gsm_pcu_if_data data_ind;
|
||||||
|
struct gsm_pcu_if_rts_req rts_req;
|
||||||
|
struct gsm_pcu_if_rach_ind rach_ind;
|
||||||
|
struct gsm_pcu_if_info_ind info_ind;
|
||||||
|
struct gsm_pcu_if_act_req act_req;
|
||||||
|
struct gsm_pcu_if_time_ind time_ind;
|
||||||
|
struct gsm_pcu_if_pag_req pag_req;
|
||||||
|
} u;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
#endif /* _PCUIF_PROTO_H */
|
||||||
@@ -8,12 +8,16 @@ int rsl_tx_chan_rqd(struct gsm_bts_trx *trx, struct gsm_time *gtime,
|
|||||||
int rsl_tx_est_ind(struct gsm_lchan *lchan, uint8_t link_id, uint8_t *data, int len);
|
int rsl_tx_est_ind(struct gsm_lchan *lchan, uint8_t link_id, uint8_t *data, int len);
|
||||||
|
|
||||||
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime);
|
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime);
|
||||||
|
int rsl_tx_chan_act_nack(struct gsm_lchan *lchan, uint8_t cause);
|
||||||
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan);
|
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan);
|
||||||
|
|
||||||
/* call-back for LAPDm code, called when it wants to send msgs UP */
|
/* call-back for LAPDm code, called when it wants to send msgs UP */
|
||||||
int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx);
|
int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx);
|
||||||
|
|
||||||
int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause);
|
int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause);
|
||||||
|
int rsl_tx_ccch_load_ind_pch(struct gsm_bts *bts, uint16_t paging_avail);
|
||||||
|
int rsl_tx_ccch_load_ind_rach(struct gsm_bts *bts, uint16_t total,
|
||||||
|
uint16_t busy, uint16_t access);
|
||||||
|
|
||||||
struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr);
|
struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr);
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ enum sig_subsys {
|
|||||||
|
|
||||||
enum signals_global {
|
enum signals_global {
|
||||||
S_NEW_SYSINFO,
|
S_NEW_SYSINFO,
|
||||||
|
S_NEW_OP_STATE,
|
||||||
|
S_NEW_NSE_ATTR,
|
||||||
|
S_NEW_CELL_ATTR,
|
||||||
|
S_NEW_NSVC_ATTR,
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
enum bts_vty_node {
|
enum bts_vty_node {
|
||||||
BTS_NODE = _LAST_OSMOVTY_NODE + 1,
|
BTS_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||||
TRX_NODE,
|
TRX_NODE,
|
||||||
TS_NODE,
|
|
||||||
LCHAN_NODE,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct cmd_element ournode_exit_cmd;
|
extern struct cmd_element ournode_exit_cmd;
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOTRAU_LIBS)
|
|||||||
|
|
||||||
noinst_LIBRARIES = libbts.a
|
noinst_LIBRARIES = libbts.a
|
||||||
libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \
|
libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \
|
||||||
rsl.c vty.c paging.c measurement.c
|
rsl.c vty.c paging.c measurement.c amr.c lchan.c \
|
||||||
|
load_indication.c pcu_sock.c
|
||||||
|
|||||||
@@ -43,7 +43,6 @@
|
|||||||
#include <osmo-bts/rsl.h>
|
#include <osmo-bts/rsl.h>
|
||||||
#include <osmo-bts/oml.h>
|
#include <osmo-bts/oml.h>
|
||||||
|
|
||||||
extern char *software_version;
|
|
||||||
extern uint8_t abis_mac[6];
|
extern uint8_t abis_mac[6];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -55,7 +54,7 @@ extern uint8_t abis_mac[6];
|
|||||||
/* send message to BSC */
|
/* send message to BSC */
|
||||||
int abis_tx(struct ipabis_link *link, struct msgb *msg)
|
int abis_tx(struct ipabis_link *link, struct msgb *msg)
|
||||||
{
|
{
|
||||||
if (link->state != LINK_STATE_CONNECT) {
|
if (!link || link->state != LINK_STATE_CONNECT) {
|
||||||
LOGP(DABIS, LOGL_NOTICE, "Link down, dropping message.\n");
|
LOGP(DABIS, LOGL_NOTICE, "Link down, dropping message.\n");
|
||||||
msgb_free(msg);
|
msgb_free(msg);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
@@ -174,7 +173,7 @@ static int abis_rx_ipa_id_get(struct ipabis_link *link, uint8_t *data, int len)
|
|||||||
break;
|
break;
|
||||||
case IPAC_IDTAG_EQUIPVERS:
|
case IPAC_IDTAG_EQUIPVERS:
|
||||||
case IPAC_IDTAG_SWVERSION:
|
case IPAC_IDTAG_SWVERSION:
|
||||||
strcpy(str, software_version);
|
strcpy(str, PACKAGE_VERSION);
|
||||||
break;
|
break;
|
||||||
case IPAC_IDTAG_UNITNAME:
|
case IPAC_IDTAG_UNITNAME:
|
||||||
sprintf(str, "osmoBTS-%02x-%02x-%02x-%02x-%02x-%02x",
|
sprintf(str, "osmoBTS-%02x-%02x-%02x-%02x-%02x-%02x",
|
||||||
@@ -368,7 +367,7 @@ static int abis_sock_cb(struct osmo_fd *bfd, unsigned int what)
|
|||||||
|
|
||||||
if ((what & BSC_FD_READ)) {
|
if ((what & BSC_FD_READ)) {
|
||||||
if (!link->rx_msg) {
|
if (!link->rx_msg) {
|
||||||
link->rx_msg = msgb_alloc(ABIS_ALLOC_SIZE, "Abis/IP");
|
link->rx_msg = abis_msgb_alloc(128);
|
||||||
if (!link->rx_msg)
|
if (!link->rx_msg)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|||||||
117
src/common/amr.c
Normal file
117
src/common/amr.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
290
src/common/bts.c
290
src/common/bts.c
@@ -57,6 +57,9 @@ int bts_init(struct gsm_bts *bts)
|
|||||||
struct gsm_bts_trx *trx;
|
struct gsm_bts_trx *trx;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
/* add to list of BTSs */
|
||||||
|
llist_add_tail(&bts->list, &bts_gsmnet.bts_list);
|
||||||
|
|
||||||
bts->band = GSM_BAND_1800;
|
bts->band = GSM_BAND_1800;
|
||||||
|
|
||||||
bts->role = btsb = talloc_zero(bts, struct gsm_bts_role_bts);
|
bts->role = btsb = talloc_zero(bts, struct gsm_bts_role_bts);
|
||||||
@@ -65,9 +68,18 @@ int bts_init(struct gsm_bts *bts)
|
|||||||
|
|
||||||
/* FIXME: make those parameters configurable */
|
/* FIXME: make those parameters configurable */
|
||||||
btsb->paging_state = paging_init(btsb, 200, 0);
|
btsb->paging_state = paging_init(btsb, 200, 0);
|
||||||
|
btsb->load.ccch.load_ind_period = 60*100;
|
||||||
|
load_timer_start(bts);
|
||||||
|
|
||||||
|
btsb->rtp_jitter_buf_ms = 100;
|
||||||
|
btsb->max_ta = 63;
|
||||||
|
|
||||||
/* set BTS to dependency */
|
/* set BTS to dependency */
|
||||||
oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_DEPENDENCY);
|
oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_DEPENDENCY);
|
||||||
|
oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_DEPENDENCY);
|
||||||
|
oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_DEPENDENCY);
|
||||||
|
oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_DEPENDENCY);
|
||||||
|
oml_mo_state_chg(&bts->gprs.nsvc[1].mo, -1, NM_AVSTATE_DEPENDENCY);
|
||||||
|
|
||||||
/* initialize bts data structure */
|
/* initialize bts data structure */
|
||||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||||
@@ -86,11 +98,11 @@ int bts_init(struct gsm_bts *bts)
|
|||||||
osmo_rtp_init(tall_bts_ctx);
|
osmo_rtp_init(tall_bts_ctx);
|
||||||
|
|
||||||
rc = bts_model_init(bts);
|
rc = bts_model_init(bts);
|
||||||
if (rc < 0)
|
if (rc < 0) {
|
||||||
|
llist_del(&bts->list);
|
||||||
return rc;
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/* add to list of BTSs */
|
|
||||||
llist_add_tail(&bts->list, &bts_gsmnet.bts_list);
|
|
||||||
bts_gsmnet.num_bts++;
|
bts_gsmnet.num_bts++;
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
@@ -121,227 +133,6 @@ void bts_shutdown(struct gsm_bts *bts, const char *reason)
|
|||||||
osmo_timer_schedule(&shutdown_timer, 3, 0);
|
osmo_timer_schedule(&shutdown_timer, 3, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
struct osmobts_lchan *lchan_by_channelnr(struct osmobts_trx *trx,
|
|
||||||
uint8_t channelnr)
|
|
||||||
{
|
|
||||||
uint8_t ts = channelnr & ~RSL_CHAN_NR_MASK;
|
|
||||||
uint8_t type = channelnr & RSL_CHAN_NR_MASK;
|
|
||||||
uint8_t sub = 0;
|
|
||||||
struct osmobts_slot *slot = trx->slot[ts];
|
|
||||||
|
|
||||||
if ((type & 0xf0) == RSL_CHAN_Lm_ACCHs) {
|
|
||||||
sub = (channelnr >> 3) & 1;
|
|
||||||
type = RSL_CHAN_Lm_ACCHs;
|
|
||||||
} else
|
|
||||||
if ((type & 0xe0) == RSL_CHAN_SDCCH4_ACCH) {
|
|
||||||
sub = (channelnr >> 3) & 3;
|
|
||||||
type = RSL_CHAN_SDCCH4_ACCH;
|
|
||||||
} else
|
|
||||||
if ((type & 0xc0) == RSL_CHAN_SDCCH8_ACCH) {
|
|
||||||
sub = (channelnr >> 3) & 7;
|
|
||||||
type = RSL_CHAN_SDCCH8_ACCH;
|
|
||||||
} else
|
|
||||||
if (type == RSL_CHAN_BCCH
|
|
||||||
|| type == RSL_CHAN_RACH
|
|
||||||
|| type == RSL_CHAN_PCH_AGCH) {
|
|
||||||
if (!slot->has_bcch) {
|
|
||||||
LOGP(DSUM, LOGL_NOTICE, "Slot %d has no BCCH\n", ts);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return slot->lchan[0];
|
|
||||||
} else {
|
|
||||||
LOGP(DSUM, LOGL_NOTICE, "Unknown channel type 0x%02x\n", type);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slot->acch_type == type)
|
|
||||||
return slot->lchan[sub];
|
|
||||||
}
|
|
||||||
LOGP(DSUM, LOGL_NOTICE, "No slot found with channel type 0x%02x\n", type);
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
struct osmocom_bts *create_bts(uint8_t num_trx, char *id)
|
|
||||||
{
|
|
||||||
struct osmocom_bts *bts;
|
|
||||||
struct osmobts_trx *trx;
|
|
||||||
int i, j;
|
|
||||||
|
|
||||||
LOGP(DSUM, LOGL_INFO, "Creating BTS\n");
|
|
||||||
bts = talloc_zero(l23_ctx, struct osmocom_bts);
|
|
||||||
if (!bts)
|
|
||||||
return NULL;
|
|
||||||
bts->link.bts = bts;
|
|
||||||
bts->id = id;
|
|
||||||
for (i = 0; i < num_trx; i++) {
|
|
||||||
LOGP(DSUM, LOGL_INFO, "Creating TRX %d\n", i);
|
|
||||||
trx = talloc_zero(l23_ctx, struct osmobts_trx);
|
|
||||||
if (!trx)
|
|
||||||
return NULL;
|
|
||||||
trx->bts = bts;
|
|
||||||
trx->trx_nr = i;
|
|
||||||
INIT_LLIST_HEAD(&trx->ms_list);
|
|
||||||
for (j = 0; j < 8; j++) {
|
|
||||||
trx->slot[j].trx = trx;
|
|
||||||
trx->slot[j].slot_nr = j;
|
|
||||||
trx->slot[j].chan_comb = 0xff; /* not set */
|
|
||||||
}
|
|
||||||
trx->link.trx = trx;
|
|
||||||
bts->trx[i] = trx;
|
|
||||||
}
|
|
||||||
bts->num_trx = num_trx;
|
|
||||||
|
|
||||||
return bts;
|
|
||||||
}
|
|
||||||
|
|
||||||
int create_ms(struct osmobts_trx *trx, int maskc, uint8_t *maskv_tx,
|
|
||||||
uint8_t *maskv_rx)
|
|
||||||
{
|
|
||||||
struct osmobts_ms *tx_ms, *rx_ms;
|
|
||||||
int i, j;
|
|
||||||
int rc;
|
|
||||||
static char sock_path[] = "/tmp/osmocom_l2.1";
|
|
||||||
|
|
||||||
for (i = 0; i < maskc; i++) {
|
|
||||||
/* create MS */
|
|
||||||
tx_ms = talloc_zero(l23_ctx, struct osmobts_ms);
|
|
||||||
printf("%p\n", tx_ms);
|
|
||||||
if (!tx_ms)
|
|
||||||
return -ENOMEM;
|
|
||||||
tx_ms->trx = trx;
|
|
||||||
rc = layer2_open(&tx_ms->ms, sock_path);
|
|
||||||
strcpy(tx_ms->ms.name, strchr(sock_path, '.') + 1);
|
|
||||||
sock_path[strlen(sock_path) - 1]++;
|
|
||||||
if (rc < 0) {
|
|
||||||
talloc_free(tx_ms);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
llist_add_tail(&tx_ms->entry, &trx->ms_list);
|
|
||||||
rx_ms = talloc_zero(l23_ctx, struct osmobts_ms);
|
|
||||||
rx_ms->trx = trx;
|
|
||||||
if (!rx_ms)
|
|
||||||
return -ENOMEM;
|
|
||||||
rc = layer2_open(&rx_ms->ms, sock_path);
|
|
||||||
strcpy(rx_ms->ms.name, strchr(sock_path, '.') + 1);
|
|
||||||
sock_path[strlen(sock_path) - 1]++;
|
|
||||||
if (rc < 0) {
|
|
||||||
talloc_free(rx_ms);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
llist_add_tail(&rx_ms->entry, &trx->ms_list);
|
|
||||||
/* assign to SLOT */
|
|
||||||
for (j = 0; j < 8; j++) {
|
|
||||||
if ((maskv_tx[i] & (1 << j)))
|
|
||||||
trx->slot[j].tx_ms = tx_ms;
|
|
||||||
if ((maskv_rx[i] & (1 << j)))
|
|
||||||
trx->slot[j].rx_ms = rx_ms;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_lchan(struct osmobts_lchan *lchan)
|
|
||||||
{
|
|
||||||
LOGP(DSUM, LOGL_INFO, "Destroying logical channel. (trx=%d ts=%d ss=%d)\n", lchan->slot->trx->trx_nr, lchan->slot->slot_nr, lchan->lchan_nr);
|
|
||||||
lapdm_exit(&lchan->l2_entity.lapdm_acch);
|
|
||||||
lapdm_exit(&lchan->l2_entity.lapdm_acch);
|
|
||||||
if (lchan->rtp.socket_created)
|
|
||||||
rtp_close_socket(&lchan->rtp);
|
|
||||||
talloc_free(lchan);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* create and destroy lchan */
|
|
||||||
void bts_setup_slot(struct osmobts_slot *slot, uint8_t comb)
|
|
||||||
{
|
|
||||||
int i, ii;
|
|
||||||
struct osmobts_lchan *lchan;
|
|
||||||
uint8_t cbits;
|
|
||||||
|
|
||||||
if (slot->chan_comb == comb)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* destroy old */
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
if (slot->lchan[i])
|
|
||||||
destroy_lchan(slot->lchan[i]);
|
|
||||||
slot->lchan[i] = NULL;
|
|
||||||
}
|
|
||||||
switch(comb) {
|
|
||||||
case 0xff:
|
|
||||||
return;
|
|
||||||
case NM_CHANC_TCHFull:
|
|
||||||
cbits = 0x01;
|
|
||||||
ii = 1;
|
|
||||||
break;
|
|
||||||
case NM_CHANC_TCHHalf:
|
|
||||||
cbits = 0x02;
|
|
||||||
ii = 2;
|
|
||||||
break;
|
|
||||||
case NM_CHANC_BCCHComb:
|
|
||||||
cbits = 0x04;
|
|
||||||
ii = 4;
|
|
||||||
break;
|
|
||||||
case NM_CHANC_SDCCH:
|
|
||||||
cbits = 0x08;
|
|
||||||
ii = 8;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
cbits = 0x10;
|
|
||||||
ii = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!slot->tx_ms) {
|
|
||||||
LOGP(DSUM, LOGL_ERROR, "Slot is not available\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < ii; i++) {
|
|
||||||
LOGP(DSUM, LOGL_INFO, "Creating logical channel. (trx=%d ts=%d ss=%d)\n", slot->trx->trx_nr, slot->slot_nr, i);
|
|
||||||
lchan = talloc_zero(l23_ctx, struct osmobts_lchan);
|
|
||||||
lchan->slot = slot;
|
|
||||||
lchan->lchan_nr = i;
|
|
||||||
lchan->chan_nr = ((cbits | i) << 3) | slot->slot_nr;
|
|
||||||
lapdm_init(&lchan->l2_entity.lapdm_dcch, &lchan->l2_entity, &slot->tx_ms->ms);
|
|
||||||
lapdm_init(&lchan->l2_entity.lapdm_acch, &lchan->l2_entity, &slot->tx_ms->ms);
|
|
||||||
lapdm_set_bts();
|
|
||||||
osmol2_register_handler(&lchan->l2_entity, &rsl_tx_rll);
|
|
||||||
slot->lchan[i] = lchan;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void destroy_ms(struct osmobts_ms *ms)
|
|
||||||
{
|
|
||||||
layer2_close(&ms->ms);
|
|
||||||
llist_del(&ms->entry);
|
|
||||||
talloc_free(ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_bts(struct osmocom_bts *bts)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct osmobts_ms *ms;
|
|
||||||
|
|
||||||
for (i = 0; i < bts->num_trx; i++) {
|
|
||||||
abis_close(&bts->trx[i]->link);
|
|
||||||
while(!llist_empty(&bts->trx[i]->ms_list)) {
|
|
||||||
ms = llist_entry(bts->trx[i]->ms_list.next,
|
|
||||||
struct osmobts_ms, entry);
|
|
||||||
destroy_ms(ms);
|
|
||||||
}
|
|
||||||
if (osmo_timer_pending(&bts->trx[i]->si.timer))
|
|
||||||
osmo_timer_del(&bts->trx[i]->si.timer);
|
|
||||||
talloc_free(bts->trx[i]);
|
|
||||||
bts->trx[i] = NULL;
|
|
||||||
}
|
|
||||||
abis_close(&bts->link);
|
|
||||||
talloc_free(bts);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* main link is established, send status report */
|
/* main link is established, send status report */
|
||||||
int bts_link_estab(struct gsm_bts *bts)
|
int bts_link_estab(struct gsm_bts *bts)
|
||||||
{
|
{
|
||||||
@@ -353,6 +144,12 @@ int bts_link_estab(struct gsm_bts *bts)
|
|||||||
oml_tx_state_changed(&bts->site_mgr.mo);
|
oml_tx_state_changed(&bts->site_mgr.mo);
|
||||||
oml_tx_state_changed(&bts->mo);
|
oml_tx_state_changed(&bts->mo);
|
||||||
|
|
||||||
|
/* those should all be in DEPENDENCY */
|
||||||
|
oml_tx_state_changed(&bts->gprs.nse.mo);
|
||||||
|
oml_tx_state_changed(&bts->gprs.cell.mo);
|
||||||
|
oml_tx_state_changed(&bts->gprs.nsvc[0].mo);
|
||||||
|
oml_tx_state_changed(&bts->gprs.nsvc[1].mo);
|
||||||
|
|
||||||
/* All other objects start off-line until the BTS Model code says otherwise */
|
/* All other objects start off-line until the BTS Model code says otherwise */
|
||||||
for (i = 0; i < bts->num_trx; i++) {
|
for (i = 0; i < bts->num_trx; i++) {
|
||||||
struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, i);
|
struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, i);
|
||||||
@@ -387,36 +184,6 @@ int trx_link_estab(struct gsm_bts_trx *trx)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bts_new_si(void *arg)
|
|
||||||
{
|
|
||||||
struct osmobts_trx *trx = arg;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (osmo_timer_pending(&trx->si.timer))
|
|
||||||
return;
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while(i < BTS_SI_NUM) {
|
|
||||||
if ((trx->si.flags[i] & BTS_SI_NEW))
|
|
||||||
break;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (i == BTS_SI_NUM)
|
|
||||||
return;
|
|
||||||
if ((trx->si.flags[i] & BTS_SI_USE))
|
|
||||||
LOGP(DSUM, LOGL_INFO, "Setting SYSTEM INFORMATION %s.\n", bts_si_name[i]);
|
|
||||||
else
|
|
||||||
LOGP(DSUM, LOGL_INFO, "Removing SYSTEM INFORMATION %s.\n", bts_si_name[i]);
|
|
||||||
trx->si.flags[i] &= ~BTS_SI_NEW;
|
|
||||||
/* distribute */
|
|
||||||
printf("TODO: send SI update to L1\n");
|
|
||||||
/* delay until next SI */
|
|
||||||
trx->si.timer.cb = bts_new_si;
|
|
||||||
trx->si.timer.data = trx;
|
|
||||||
osmo_timer_schedule(&trx->si.timer, 0, 200000);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int lchan_init_lapdm(struct gsm_lchan *lchan)
|
int lchan_init_lapdm(struct gsm_lchan *lchan)
|
||||||
{
|
{
|
||||||
struct lapdm_channel *lc = &lchan->lapdm_ch;
|
struct lapdm_channel *lc = &lchan->lapdm_ch;
|
||||||
@@ -431,24 +198,17 @@ int lchan_init_lapdm(struct gsm_lchan *lchan)
|
|||||||
|
|
||||||
int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg)
|
int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg)
|
||||||
{
|
{
|
||||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);;
|
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||||
|
|
||||||
/* FIXME: implement max queue length */
|
/* FIXME: implement max queue length */
|
||||||
llist_add_tail(&msg->list, &btsb->agch_queue);
|
msgb_enqueue(&btsb->agch_queue, msg);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct msgb *bts_agch_dequeue(struct gsm_bts *bts)
|
struct msgb *bts_agch_dequeue(struct gsm_bts *bts)
|
||||||
{
|
{
|
||||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);;
|
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||||
struct msgb *msg;
|
|
||||||
|
|
||||||
if (llist_empty(&btsb->agch_queue))
|
return msgb_dequeue(&btsb->agch_queue);
|
||||||
return NULL;
|
|
||||||
|
|
||||||
msg = llist_entry(btsb->agch_queue.next, struct msgb, list);
|
|
||||||
llist_del(&msg->list);
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
}
|
||||||
|
|||||||
27
src/common/lchan.c
Normal file
27
src/common/lchan.c
Normal 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;
|
||||||
|
}
|
||||||
@@ -19,12 +19,19 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <rsl.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <osmocom/core/timer.h>
|
#include <osmocom/core/timer.h>
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
|
|
||||||
static void reset_load_counters(void)
|
#include <osmo-bts/gsm_data.h>
|
||||||
|
#include <osmo-bts/rsl.h>
|
||||||
|
#include <osmo-bts/paging.h>
|
||||||
|
|
||||||
|
static void reset_load_counters(struct gsm_bts *bts)
|
||||||
{
|
{
|
||||||
|
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||||
|
|
||||||
/* re-set the counters */
|
/* re-set the counters */
|
||||||
btsb->load.ccch.pch_used = btsb->load.ccch.pch_total = 0;
|
btsb->load.ccch.pch_used = btsb->load.ccch.pch_total = 0;
|
||||||
}
|
}
|
||||||
@@ -32,38 +39,56 @@ static void reset_load_counters(void)
|
|||||||
static void load_timer_cb(void *data)
|
static void load_timer_cb(void *data)
|
||||||
{
|
{
|
||||||
struct gsm_bts *bts = data;
|
struct gsm_bts *bts = data;
|
||||||
struct gsm_bts_role_bts *btsb = FIXME;
|
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||||
unsigned int pch_percent;
|
unsigned int pch_percent, rach_percent;
|
||||||
|
|
||||||
/* compute percentages */
|
/* compute percentages */
|
||||||
pch_percent = (btsb->load.ccch.pch_used * 100) / btsb->load.ccch.pch_total;
|
if (btsb->load.ccch.pch_total == 0)
|
||||||
|
pch_percent = 0;
|
||||||
|
else
|
||||||
|
pch_percent = (btsb->load.ccch.pch_used * 100) /
|
||||||
|
btsb->load.ccch.pch_total;
|
||||||
|
|
||||||
if (pch_percent >= btsb->load.ccch.load_ind_thresh) {
|
if (pch_percent >= btsb->load.ccch.load_ind_thresh) {
|
||||||
/* send RSL load indication message to BSC */
|
/* send RSL load indication message to BSC */
|
||||||
uint16_t paging_buffer_space = FIXME;
|
uint16_t buffer_space = paging_buffer_space(btsb->paging_state);
|
||||||
rsl_tx_ccch_load_ind_pch(bts, paging_buffer_space);
|
rsl_tx_ccch_load_ind_pch(bts, buffer_space);
|
||||||
}
|
}
|
||||||
|
|
||||||
reset_load_counters();
|
if (btsb->load.rach.total == 0)
|
||||||
|
rach_percent = 0;
|
||||||
|
else
|
||||||
|
rach_percent = (btsb->load.rach.busy * 100) /
|
||||||
|
btsb->load.rach.total;
|
||||||
|
|
||||||
|
if (rach_percent >= btsb->load.ccch.load_ind_thresh) {
|
||||||
|
/* send RSL load indication message to BSC */
|
||||||
|
rsl_tx_ccch_load_ind_rach(bts, btsb->load.rach.total,
|
||||||
|
btsb->load.rach.busy,
|
||||||
|
btsb->load.rach.access);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_load_counters(bts);
|
||||||
|
|
||||||
/* re-schedule the timer */
|
/* re-schedule the timer */
|
||||||
osmo_timer_schedule(&btsb->load.ccch.timer,
|
osmo_timer_schedule(&btsb->load.ccch.timer,
|
||||||
btsb->load.ccch.load_ind_period, 0);
|
btsb->load.ccch.load_ind_period, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void load_timer_start(struct gsm_bts *bts)
|
void load_timer_start(struct gsm_bts *bts)
|
||||||
{
|
{
|
||||||
struct gsm_bts_role_bts *btsb = FIXME;
|
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||||
|
|
||||||
btsb->load.ccch.timer.data = bts;
|
btsb->load.ccch.timer.data = bts;
|
||||||
reset_load_counters();
|
btsb->load.ccch.timer.cb = load_timer_cb;
|
||||||
|
reset_load_counters(bts);
|
||||||
osmo_timer_schedule(&btsb->load.ccch.timer,
|
osmo_timer_schedule(&btsb->load.ccch.timer,
|
||||||
btsb->load.ccch.load_ind_period, 0);
|
btsb->load.ccch.load_ind_period, 0);
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void load_timer_stop(struct gsm_bts *bts)
|
void load_timer_stop(struct gsm_bts *bts)
|
||||||
{
|
{
|
||||||
|
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||||
|
|
||||||
osmo_timer_del(&btsb->load.ccch.timer);
|
osmo_timer_del(&btsb->load.ccch.timer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,12 @@ static struct log_info_cat bts_log_info_cat[] = {
|
|||||||
.loglevel = LOGL_NOTICE,
|
.loglevel = LOGL_NOTICE,
|
||||||
.enabled = 1,
|
.enabled = 1,
|
||||||
},
|
},
|
||||||
|
[DPCU] = {
|
||||||
|
.name = "DPCU",
|
||||||
|
.description = "PCU interface",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
#if 0
|
#if 0
|
||||||
[DNS] = {
|
[DNS] = {
|
||||||
.name = "DNS",
|
.name = "DNS",
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ int ts_meas_check_compute(struct gsm_bts_trx_ts *ts, uint32_t fn)
|
|||||||
case GSM_LCHAN_SDCCH:
|
case GSM_LCHAN_SDCCH:
|
||||||
case GSM_LCHAN_TCH_F:
|
case GSM_LCHAN_TCH_F:
|
||||||
case GSM_LCHAN_TCH_H:
|
case GSM_LCHAN_TCH_H:
|
||||||
|
case GSM_LCHAN_PDTCH:
|
||||||
lchan_meas_check_compute(lchan, fn);
|
lchan_meas_check_compute(lchan, fn);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
225
src/common/oml.c
225
src/common/oml.c
@@ -37,6 +37,8 @@
|
|||||||
#include <osmo-bts/abis.h>
|
#include <osmo-bts/abis.h>
|
||||||
#include <osmo-bts/oml.h>
|
#include <osmo-bts/oml.h>
|
||||||
#include <osmo-bts/bts_model.h>
|
#include <osmo-bts/bts_model.h>
|
||||||
|
#include <osmo-bts/bts.h>
|
||||||
|
#include <osmo-bts/signal.h>
|
||||||
|
|
||||||
/* FIXME: move this to libosmocore */
|
/* FIXME: move this to libosmocore */
|
||||||
static struct tlv_definition abis_nm_att_tlvdef_ipa = {
|
static struct tlv_definition abis_nm_att_tlvdef_ipa = {
|
||||||
@@ -89,6 +91,7 @@ static struct tlv_definition abis_nm_att_tlvdef_ipa = {
|
|||||||
|
|
||||||
/* ip.access nanoBTS specific commands */
|
/* ip.access nanoBTS specific commands */
|
||||||
static const char ipaccess_magic[] = "com.ipaccess";
|
static const char ipaccess_magic[] = "com.ipaccess";
|
||||||
|
static int oml_ipa_set_attr(struct gsm_bts *bts, struct msgb *msg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* support
|
* support
|
||||||
@@ -245,6 +248,7 @@ int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state)
|
|||||||
abis_nm_opstate_name(mo->nm_state.operational),
|
abis_nm_opstate_name(mo->nm_state.operational),
|
||||||
abis_nm_opstate_name(op_state));
|
abis_nm_opstate_name(op_state));
|
||||||
mo->nm_state.operational = op_state;
|
mo->nm_state.operational = op_state;
|
||||||
|
osmo_signal_dispatch(SS_GLOBAL, S_NEW_OP_STATE, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send state change report */
|
/* send state change report */
|
||||||
@@ -273,6 +277,19 @@ int oml_mo_fom_ack_nack(struct gsm_abis_mo *mo, uint8_t orig_msg_type,
|
|||||||
return oml_mo_send_msg(mo, msg, new_msg_type);
|
return oml_mo_send_msg(mo, msg, new_msg_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int oml_mo_statechg_ack(struct gsm_abis_mo *mo)
|
||||||
|
{
|
||||||
|
struct msgb *msg;
|
||||||
|
|
||||||
|
msg = oml_msgb_alloc();
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
msgb_tv_put(msg, NM_ATT_ADM_STATE, mo->nm_state.administrative);
|
||||||
|
|
||||||
|
return oml_mo_send_msg(mo, msg, NM_MT_CHG_ADM_STATE_ACK);
|
||||||
|
}
|
||||||
|
|
||||||
int oml_mo_opstart_ack(struct gsm_abis_mo *mo)
|
int oml_mo_opstart_ack(struct gsm_abis_mo *mo)
|
||||||
{
|
{
|
||||||
return oml_mo_fom_ack_nack(mo, NM_MT_OPSTART, 0);
|
return oml_mo_fom_ack_nack(mo, NM_MT_OPSTART, 0);
|
||||||
@@ -287,10 +304,14 @@ int oml_fom_ack_nack(struct msgb *old_msg, uint8_t cause)
|
|||||||
{
|
{
|
||||||
struct abis_om_hdr *old_oh = msgb_l2(old_msg);
|
struct abis_om_hdr *old_oh = msgb_l2(old_msg);
|
||||||
struct abis_om_fom_hdr *old_foh = msgb_l3(old_msg);
|
struct abis_om_fom_hdr *old_foh = msgb_l3(old_msg);
|
||||||
struct msgb *msg = oml_msgb_alloc();
|
struct msgb *msg;
|
||||||
struct abis_om_fom_hdr *foh;
|
struct abis_om_fom_hdr *foh;
|
||||||
int is_manuf = 0;
|
int is_manuf = 0;
|
||||||
|
|
||||||
|
msg = oml_msgb_alloc();
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
/* make sure to respond with MANUF if request was MANUF */
|
/* make sure to respond with MANUF if request was MANUF */
|
||||||
if (old_oh->mdisc == ABIS_OM_MDISC_MANUF)
|
if (old_oh->mdisc == ABIS_OM_MDISC_MANUF)
|
||||||
is_manuf = 1;
|
is_manuf = 1;
|
||||||
@@ -378,8 +399,13 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
|
|||||||
|
|
||||||
/* Test for globally unsupported stuff here */
|
/* Test for globally unsupported stuff here */
|
||||||
if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) {
|
if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) {
|
||||||
const uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN);
|
const uint16_t *value = (const uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN);
|
||||||
uint16_t arfcn = ntohs(*value);
|
uint16_t arfcn = ntohs(*value);
|
||||||
|
|
||||||
|
LOGP(DOML, LOGL_NOTICE, "MSG: %s\n", osmo_hexdump(msgb_l3(msg), msgb_l3len(msg)));
|
||||||
|
LOGP(DOML, LOGL_NOTICE, "L3=%p, VAL=%p, DIF=%tu\n", msgb_l3(msg), value,
|
||||||
|
(void *)value - (void *) msgb_l3(msg));
|
||||||
|
|
||||||
if (arfcn > 1024) {
|
if (arfcn > 1024) {
|
||||||
LOGP(DOML, LOGL_NOTICE, "Given ARFCN %d is not supported.\n", arfcn);
|
LOGP(DOML, LOGL_NOTICE, "Given ARFCN %d is not supported.\n", arfcn);
|
||||||
return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL);
|
return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL);
|
||||||
@@ -430,10 +456,8 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 9.4.31 Maximum Timing Advance */
|
/* 9.4.31 Maximum Timing Advance */
|
||||||
if (TLVP_PRESENT(&tp, NM_ATT_MAX_TA)) {
|
if (TLVP_PRESENT(&tp, NM_ATT_MAX_TA))
|
||||||
uint16_t *fn = (uint16_t *) TLVP_VAL(&tp, NM_ATT_MAX_TA);
|
btsb->max_ta = *TLVP_VAL(&tp, NM_ATT_MAX_TA);
|
||||||
btsb->max_ta = ntohs(*fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 9.4.39 Overload Period */
|
/* 9.4.39 Overload Period */
|
||||||
if (TLVP_PRESENT(&tp, NM_ATT_OVERL_PERIOD))
|
if (TLVP_PRESENT(&tp, NM_ATT_OVERL_PERIOD))
|
||||||
@@ -514,8 +538,9 @@ static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg)
|
|||||||
|
|
||||||
/* 9.4.47 RF Max Power Reduction */
|
/* 9.4.47 RF Max Power Reduction */
|
||||||
if (TLVP_PRESENT(&tp, NM_ATT_RF_MAXPOWR_R)) {
|
if (TLVP_PRESENT(&tp, NM_ATT_RF_MAXPOWR_R)) {
|
||||||
trx->max_power_red = *TLVP_VAL(&tp, NM_ATT_RF_MAXPOWR_R);
|
trx->max_power_red = *TLVP_VAL(&tp, NM_ATT_RF_MAXPOWR_R) * 2;
|
||||||
LOGP(DOML, LOGL_INFO, "Set RF Max Power Reduction = %d\n", trx->max_power_red);
|
LOGP(DOML, LOGL_INFO, "Set RF Max Power Reduction = %d dBm\n",
|
||||||
|
trx->max_power_red);
|
||||||
}
|
}
|
||||||
/* 9.4.5 ARFCN List */
|
/* 9.4.5 ARFCN List */
|
||||||
#if 0
|
#if 0
|
||||||
@@ -571,6 +596,10 @@ static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts)
|
|||||||
lchan->type = GSM_LCHAN_SDCCH;
|
lchan->type = GSM_LCHAN_SDCCH;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case GSM_PCHAN_PDCH:
|
||||||
|
lchan = &ts->lchan[0];
|
||||||
|
lchan->type = GSM_LCHAN_PDTCH;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
/* FIXME */
|
/* FIXME */
|
||||||
break;
|
break;
|
||||||
@@ -607,17 +636,21 @@ static int oml_rx_set_chan_attr(struct gsm_bts_trx_ts *ts, struct msgb *msg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* merge existing BTS attributes with new attributes */
|
/* merge existing BTS attributes with new attributes */
|
||||||
tp_merged = tlvp_copy(bts->mo.nm_attr, bts);
|
tp_merged = tlvp_copy(ts->mo.nm_attr, bts);
|
||||||
tlvp_merge(tp_merged, &tp);
|
tlvp_merge(tp_merged, &tp);
|
||||||
|
|
||||||
/* Call into BTS driver to check attribute values */
|
/* Call into BTS driver to check attribute values */
|
||||||
rc = bts_model_check_oml(bts, foh->msg_type, ts->mo.nm_attr, tp_merged, ts);
|
rc = bts_model_check_oml(bts, foh->msg_type, ts->mo.nm_attr, tp_merged, ts);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
talloc_free(&tp_merged);
|
talloc_free(tp_merged);
|
||||||
/* FIXME: Send NACK */
|
/* FIXME: Send NACK */
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Success: replace old BTS attributes with new */
|
||||||
|
talloc_free(ts->mo.nm_attr);
|
||||||
|
ts->mo.nm_attr = tp_merged;
|
||||||
|
|
||||||
/* 9.4.13 Channel Combination */
|
/* 9.4.13 Channel Combination */
|
||||||
if (TLVP_PRESENT(&tp, NM_ATT_CHAN_COMB)) {
|
if (TLVP_PRESENT(&tp, NM_ATT_CHAN_COMB)) {
|
||||||
uint8_t comb = *TLVP_VAL(&tp, NM_ATT_CHAN_COMB);
|
uint8_t comb = *TLVP_VAL(&tp, NM_ATT_CHAN_COMB);
|
||||||
@@ -702,7 +735,7 @@ static int oml_rx_chg_adm_state(struct gsm_bts *bts, struct msgb *msg)
|
|||||||
if (mo->nm_state.administrative == adm_state) {
|
if (mo->nm_state.administrative == adm_state) {
|
||||||
DEBUGP(DOML, "... automatic ACK, ADM state already was %s\n",
|
DEBUGP(DOML, "... automatic ACK, ADM state already was %s\n",
|
||||||
get_value_string(abis_nm_adm_state_names, adm_state));
|
get_value_string(abis_nm_adm_state_names, adm_state));
|
||||||
return oml_fom_ack_nack(msg, 0);
|
return oml_mo_statechg_ack(mo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 3: Ask BTS driver to apply the state chg */
|
/* Step 3: Ask BTS driver to apply the state chg */
|
||||||
@@ -749,6 +782,9 @@ static int down_fom(struct gsm_bts *bts, struct msgb *msg)
|
|||||||
case NM_MT_CHG_ADM_STATE:
|
case NM_MT_CHG_ADM_STATE:
|
||||||
ret = oml_rx_chg_adm_state(bts, msg);
|
ret = oml_rx_chg_adm_state(bts, msg);
|
||||||
break;
|
break;
|
||||||
|
case NM_MT_IPACC_SET_ATTR:
|
||||||
|
ret = oml_ipa_set_attr(bts, msg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOGP(DOML, LOGL_INFO, "unknown Formatted O&M msg_type 0x%02x\n",
|
LOGP(DOML, LOGL_INFO, "unknown Formatted O&M msg_type 0x%02x\n",
|
||||||
foh->msg_type);
|
foh->msg_type);
|
||||||
@@ -762,6 +798,170 @@ static int down_fom(struct gsm_bts *bts, struct msgb *msg)
|
|||||||
* manufacturer related messages
|
* manufacturer related messages
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define TLVP_PRES_LEN(tp, tag, min_len) \
|
||||||
|
(TLVP_PRESENT(tp, tag) && TLVP_LEN(tp, tag) >= min_len)
|
||||||
|
|
||||||
|
static int oml_ipa_mo_set_attr_nse(void *obj, struct tlv_parsed *tp)
|
||||||
|
{
|
||||||
|
struct gsm_bts *bts = container_of(obj, struct gsm_bts, gprs.nse);
|
||||||
|
|
||||||
|
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NSEI, 2)) {
|
||||||
|
bts->gprs.nse.nsei =
|
||||||
|
ntohs(*(uint16_t *) TLVP_VAL(tp, NM_ATT_IPACC_NSEI));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NS_CFG, 7)) {
|
||||||
|
memcpy(&bts->gprs.nse.timer,
|
||||||
|
TLVP_VAL(tp, NM_ATT_IPACC_NS_CFG), 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_BSSGP_CFG, 11)) {
|
||||||
|
memcpy(&bts->gprs.cell.timer,
|
||||||
|
TLVP_VAL(tp, NM_ATT_IPACC_BSSGP_CFG), 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
osmo_signal_dispatch(SS_GLOBAL, S_NEW_NSE_ATTR, bts);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int oml_ipa_mo_set_attr_cell(void *obj, struct tlv_parsed *tp)
|
||||||
|
{
|
||||||
|
struct gsm_bts *bts = container_of(obj, struct gsm_bts, gprs.cell);
|
||||||
|
struct gprs_rlc_cfg *rlcc = &bts->gprs.cell.rlc_cfg;
|
||||||
|
const uint8_t *cur;
|
||||||
|
|
||||||
|
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RAC, 1))
|
||||||
|
bts->gprs.rac = *TLVP_VAL(tp, NM_ATT_IPACC_RAC);
|
||||||
|
|
||||||
|
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_GPRS_PAGING_CFG, 2)) {
|
||||||
|
cur = TLVP_VAL(tp, NM_ATT_IPACC_GPRS_PAGING_CFG);
|
||||||
|
rlcc->paging.repeat_time = cur[0] * 50;
|
||||||
|
rlcc->paging.repeat_count = cur[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_BVCI, 2)) {
|
||||||
|
bts->gprs.cell.bvci =
|
||||||
|
ntohs(*(uint16_t *)TLVP_VAL(tp, NM_ATT_IPACC_BVCI));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG, 9)) {
|
||||||
|
cur = TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG);
|
||||||
|
rlcc->parameter[RLC_T3142] = cur[0];
|
||||||
|
rlcc->parameter[RLC_T3169] = cur[1];
|
||||||
|
rlcc->parameter[RLC_T3191] = cur[2];
|
||||||
|
rlcc->parameter[RLC_T3193] = cur[3];
|
||||||
|
rlcc->parameter[RLC_T3195] = cur[4];
|
||||||
|
rlcc->parameter[RLC_N3101] = cur[5];
|
||||||
|
rlcc->parameter[RLC_N3103] = cur[6];
|
||||||
|
rlcc->parameter[RLC_N3105] = cur[7];
|
||||||
|
rlcc->parameter[CV_COUNTDOWN] = cur[8];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_CODING_SCHEMES, 2)) {
|
||||||
|
int i;
|
||||||
|
rlcc->cs_mask = 0;
|
||||||
|
cur = TLVP_VAL(tp, NM_ATT_IPACC_CODING_SCHEMES);
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
if (cur[0] & (1 << i))
|
||||||
|
rlcc->cs_mask |= (1 << (GPRS_CS1+i));
|
||||||
|
}
|
||||||
|
if (cur[0] & 0x80)
|
||||||
|
rlcc->cs_mask |= (1 << GPRS_MCS9);
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
if (cur[1] & (1 << i))
|
||||||
|
rlcc->cs_mask |= (1 << (GPRS_MCS1+i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG_2, 5)) {
|
||||||
|
cur = TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG_2);
|
||||||
|
rlcc->parameter[T_DL_TBF_EXT] = ntohs(*(uint16_t *)cur) * 10;
|
||||||
|
cur += 2;
|
||||||
|
rlcc->parameter[T_UL_TBF_EXT] = ntohs(*(uint16_t *)cur) * 10;
|
||||||
|
cur += 2;
|
||||||
|
rlcc->initial_cs = *cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG_3, 1)) {
|
||||||
|
rlcc->initial_mcs = *TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
osmo_signal_dispatch(SS_GLOBAL, S_NEW_CELL_ATTR, bts);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int oml_ipa_mo_set_attr_nsvc(struct gsm_bts_gprs_nsvc *nsvc,
|
||||||
|
struct tlv_parsed *tp)
|
||||||
|
{
|
||||||
|
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NSVCI, 2))
|
||||||
|
nsvc->nsvci =
|
||||||
|
ntohs(*(uint16_t *)TLVP_VAL(tp, NM_ATT_IPACC_NSVCI));
|
||||||
|
|
||||||
|
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NS_LINK_CFG, 8)) {
|
||||||
|
const uint8_t *cur = TLVP_VAL(tp, NM_ATT_IPACC_NS_LINK_CFG);
|
||||||
|
nsvc->remote_port = ntohs(*(uint16_t *)cur);
|
||||||
|
cur += 2;
|
||||||
|
nsvc->remote_ip = ntohl(*(uint32_t *)cur);
|
||||||
|
cur += 4;
|
||||||
|
nsvc->local_port = ntohs(*(uint16_t *)cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
osmo_signal_dispatch(SS_GLOBAL, S_NEW_NSVC_ATTR, nsvc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int oml_ipa_mo_set_attr(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
||||||
|
void *obj, struct tlv_parsed *tp)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
switch (mo->obj_class) {
|
||||||
|
case NM_OC_GPRS_NSE:
|
||||||
|
rc = oml_ipa_mo_set_attr_nse(obj, tp);
|
||||||
|
break;
|
||||||
|
case NM_OC_GPRS_CELL:
|
||||||
|
rc = oml_ipa_mo_set_attr_cell(obj, tp);
|
||||||
|
break;
|
||||||
|
case NM_OC_GPRS_NSVC:
|
||||||
|
rc = oml_ipa_mo_set_attr_nsvc(obj, tp);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rc = NM_NACK_OBJINST_UNKN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int oml_ipa_set_attr(struct gsm_bts *bts, struct msgb *msg)
|
||||||
|
{
|
||||||
|
struct abis_om_fom_hdr *foh = msgb_l3(msg);
|
||||||
|
struct gsm_abis_mo *mo;
|
||||||
|
struct tlv_parsed tp;
|
||||||
|
void *obj;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
abis_nm_debugp_foh(DOML, foh);
|
||||||
|
DEBUGPC(DOML, "Rx IPA SET ATTR\n");
|
||||||
|
|
||||||
|
rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh));
|
||||||
|
if (rc < 0)
|
||||||
|
return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT);
|
||||||
|
|
||||||
|
/* Resolve MO by obj_class/obj_inst */
|
||||||
|
mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
|
||||||
|
obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst);
|
||||||
|
if (!mo || !obj)
|
||||||
|
return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN);
|
||||||
|
|
||||||
|
rc = oml_ipa_mo_set_attr(bts, mo, obj, &tp);
|
||||||
|
|
||||||
|
return oml_fom_ack_nack(msg, rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int rx_oml_ipa_rsl_connect(struct gsm_bts_trx *trx, struct msgb *msg,
|
static int rx_oml_ipa_rsl_connect(struct gsm_bts_trx *trx, struct msgb *msg,
|
||||||
struct tlv_parsed *tp)
|
struct tlv_parsed *tp)
|
||||||
@@ -847,6 +1047,9 @@ static int down_mom(struct gsm_bts *bts, struct msgb *msg)
|
|||||||
trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
|
trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
|
||||||
ret = rx_oml_ipa_rsl_connect(trx, msg, &tp);
|
ret = rx_oml_ipa_rsl_connect(trx, msg, &tp);
|
||||||
break;
|
break;
|
||||||
|
case NM_MT_IPACC_SET_ATTR:
|
||||||
|
ret = oml_ipa_set_attr(bts, msg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOGP(DOML, LOGL_INFO, "Manufacturer Formatted O&M msg_type 0x%02x\n",
|
LOGP(DOML, LOGL_INFO, "Manufacturer Formatted O&M msg_type 0x%02x\n",
|
||||||
foh->msg_type);
|
foh->msg_type);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* Paging message encoding + queue management */
|
/* Paging message encoding + queue management */
|
||||||
|
|
||||||
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
/* (C) 2011-2012 by Harald Welte <laforge@gnumonks.org>
|
||||||
*
|
*
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*
|
*
|
||||||
@@ -41,18 +41,34 @@
|
|||||||
#include <osmo-bts/logging.h>
|
#include <osmo-bts/logging.h>
|
||||||
#include <osmo-bts/paging.h>
|
#include <osmo-bts/paging.h>
|
||||||
#include <osmo-bts/signal.h>
|
#include <osmo-bts/signal.h>
|
||||||
|
#include <osmo-bts/pcu_if.h>
|
||||||
|
|
||||||
#define MAX_PAGING_BLOCKS_CCCH 9
|
#define MAX_PAGING_BLOCKS_CCCH 9
|
||||||
#define MAX_BS_PA_MFRMS 9
|
#define MAX_BS_PA_MFRMS 9
|
||||||
|
|
||||||
|
enum paging_record_type {
|
||||||
|
PAGING_RECORD_PAGING,
|
||||||
|
PAGING_RECORD_IMM_ASS
|
||||||
|
};
|
||||||
|
|
||||||
struct paging_record {
|
struct paging_record {
|
||||||
struct llist_head list;
|
struct llist_head list;
|
||||||
time_t expiration_time;
|
enum paging_record_type type;
|
||||||
uint8_t chan_needed;
|
union {
|
||||||
uint8_t identity_lv[9];
|
struct {
|
||||||
|
time_t expiration_time;
|
||||||
|
uint8_t chan_needed;
|
||||||
|
uint8_t identity_lv[9];
|
||||||
|
} paging;
|
||||||
|
struct {
|
||||||
|
uint8_t msg[GSM_MACBLOCK_LEN];
|
||||||
|
} imm_ass;
|
||||||
|
} u;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct paging_state {
|
struct paging_state {
|
||||||
|
struct gsm_bts_role_bts *btsb;
|
||||||
|
|
||||||
/* parameters taken / interpreted from BCCH/CCCH configuration */
|
/* parameters taken / interpreted from BCCH/CCCH configuration */
|
||||||
struct gsm48_control_channel_descr chan_desc;
|
struct gsm48_control_channel_descr chan_desc;
|
||||||
|
|
||||||
@@ -128,6 +144,13 @@ static int get_pag_subch_nr(struct paging_state *ps, struct gsm_time *gt)
|
|||||||
return pag_idx + mfrm_part;
|
return pag_idx + mfrm_part;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int paging_buffer_space(struct paging_state *ps)
|
||||||
|
{
|
||||||
|
if (ps->num_paging >= ps->num_paging_max)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return ps->num_paging_max - ps->num_paging;
|
||||||
|
}
|
||||||
|
|
||||||
/* Add an identity to the paging queue */
|
/* Add an identity to the paging queue */
|
||||||
int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
|
int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
|
||||||
@@ -144,10 +167,14 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
|
|||||||
|
|
||||||
/* Check if we already have this identity */
|
/* Check if we already have this identity */
|
||||||
llist_for_each_entry(pr, group_q, list) {
|
llist_for_each_entry(pr, group_q, list) {
|
||||||
if (identity_lv[0] == pr->identity_lv[0] &&
|
if (pr->type != PAGING_RECORD_PAGING)
|
||||||
!memcmp(identity_lv+1, pr->identity_lv+1, identity_lv[0])) {
|
continue;
|
||||||
|
if (identity_lv[0] == pr->u.paging.identity_lv[0] &&
|
||||||
|
!memcmp(identity_lv+1, pr->u.paging.identity_lv+1,
|
||||||
|
identity_lv[0])) {
|
||||||
LOGP(DPAG, LOGL_INFO, "Ignoring duplicate paging\n");
|
LOGP(DPAG, LOGL_INFO, "Ignoring duplicate paging\n");
|
||||||
pr->expiration_time = time(NULL) + ps->paging_lifetime;
|
pr->u.paging.expiration_time =
|
||||||
|
time(NULL) + ps->paging_lifetime;
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,8 +182,9 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
|
|||||||
pr = talloc_zero(ps, struct paging_record);
|
pr = talloc_zero(ps, struct paging_record);
|
||||||
if (!pr)
|
if (!pr)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
pr->type = PAGING_RECORD_PAGING;
|
||||||
|
|
||||||
if (*identity_lv + 1 > sizeof(pr->identity_lv)) {
|
if (*identity_lv + 1 > sizeof(pr->u.paging.identity_lv)) {
|
||||||
talloc_free(pr);
|
talloc_free(pr);
|
||||||
return -E2BIG;
|
return -E2BIG;
|
||||||
}
|
}
|
||||||
@@ -164,9 +192,9 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
|
|||||||
LOGP(DPAG, LOGL_INFO, "Add paging to queue (group=%u, queue_len=%u)\n",
|
LOGP(DPAG, LOGL_INFO, "Add paging to queue (group=%u, queue_len=%u)\n",
|
||||||
paging_group, ps->num_paging+1);
|
paging_group, ps->num_paging+1);
|
||||||
|
|
||||||
pr->expiration_time = time(NULL) + ps->paging_lifetime;
|
pr->u.paging.expiration_time = time(NULL) + ps->paging_lifetime;
|
||||||
pr->chan_needed = chan_needed;
|
pr->u.paging.chan_needed = chan_needed;
|
||||||
memcpy(&pr->identity_lv, identity_lv, identity_lv[0]+1);
|
memcpy(&pr->u.paging.identity_lv, identity_lv, identity_lv[0]+1);
|
||||||
|
|
||||||
/* enqueue the new identity to the HEAD of the queue,
|
/* enqueue the new identity to the HEAD of the queue,
|
||||||
* to ensure it will be paged quickly at least once. */
|
* to ensure it will be paged quickly at least once. */
|
||||||
@@ -176,6 +204,42 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add an IMM.ASS message to the paging queue */
|
||||||
|
int paging_add_imm_ass(struct paging_state *ps, const uint8_t *data,
|
||||||
|
uint8_t len)
|
||||||
|
{
|
||||||
|
struct llist_head *group_q;
|
||||||
|
struct paging_record *pr;
|
||||||
|
uint16_t imsi, paging_group;
|
||||||
|
|
||||||
|
if (len != GSM_MACBLOCK_LEN + 3) {
|
||||||
|
LOGP(DPAG, LOGL_ERROR, "IMM.ASS invalid length %d\n", len);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
len -= 3;
|
||||||
|
|
||||||
|
imsi = 100 * ((*(data++)) - '0');
|
||||||
|
imsi += 10 * ((*(data++)) - '0');
|
||||||
|
imsi += (*(data++)) - '0';
|
||||||
|
paging_group = gsm0502_calc_paging_group(&ps->chan_desc, imsi);
|
||||||
|
|
||||||
|
group_q = &ps->paging_queue[paging_group];
|
||||||
|
|
||||||
|
pr = talloc_zero(ps, struct paging_record);
|
||||||
|
if (!pr)
|
||||||
|
return -ENOMEM;
|
||||||
|
pr->type = PAGING_RECORD_IMM_ASS;
|
||||||
|
|
||||||
|
LOGP(DPAG, LOGL_INFO, "Add IMM.ASS to queue (group=%u)\n",
|
||||||
|
paging_group);
|
||||||
|
memcpy(pr->u.imm_ass.msg, data, GSM_MACBLOCK_LEN);
|
||||||
|
|
||||||
|
/* enqueue the new message to the HEAD of the queue */
|
||||||
|
llist_add(&pr->list, group_q);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#define L2_PLEN(len) (((len - 1) << 2) | 0x01)
|
#define L2_PLEN(len) (((len - 1) << 2) | 0x01)
|
||||||
|
|
||||||
static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv,
|
static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv,
|
||||||
@@ -266,7 +330,7 @@ static struct paging_record *dequeue_pr(struct llist_head *group_q)
|
|||||||
|
|
||||||
static int pr_is_imsi(struct paging_record *pr)
|
static int pr_is_imsi(struct paging_record *pr)
|
||||||
{
|
{
|
||||||
if ((pr->identity_lv[1] & 7) == GSM_MI_TYPE_IMSI)
|
if ((pr->u.paging.identity_lv[1] & 7) == GSM_MI_TYPE_IMSI)
|
||||||
return 1;
|
return 1;
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
@@ -295,10 +359,22 @@ static void sort_pr_tmsi_imsi(struct paging_record *pr[], unsigned int n)
|
|||||||
/* generate paging message for given gsm time */
|
/* generate paging message for given gsm time */
|
||||||
int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt)
|
int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt)
|
||||||
{
|
{
|
||||||
unsigned int group = get_pag_subch_nr(ps, gt);
|
struct llist_head *group_q;
|
||||||
struct llist_head *group_q = &ps->paging_queue[group];
|
int group;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
|
ps->btsb->load.ccch.pch_total += 1;
|
||||||
|
|
||||||
|
group = get_pag_subch_nr(ps, gt);
|
||||||
|
if (group < 0) {
|
||||||
|
LOGP(DPAG, LOGL_ERROR,
|
||||||
|
"Paging called for GSM wrong time: FN %d/%d/%d/%d.\n",
|
||||||
|
gt->fn, gt->t1, gt->t2, gt->t3);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
group_q = &ps->paging_queue[group];
|
||||||
|
|
||||||
/* There is nobody to be paged, send Type1 with two empty ID */
|
/* There is nobody to be paged, send Type1 with two empty ID */
|
||||||
if (llist_empty(group_q)) {
|
if (llist_empty(group_q)) {
|
||||||
//DEBUGP(DPAG, "Tx PAGING TYPE 1 (empty)\n");
|
//DEBUGP(DPAG, "Tx PAGING TYPE 1 (empty)\n");
|
||||||
@@ -306,15 +382,24 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
|
|||||||
NULL, 0);
|
NULL, 0);
|
||||||
} else {
|
} else {
|
||||||
struct paging_record *pr[4];
|
struct paging_record *pr[4];
|
||||||
unsigned int num_pr = 0;
|
unsigned int num_pr = 0, imm_ass = 0;
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
unsigned int i, num_imsi = 0;
|
unsigned int i, num_imsi = 0;
|
||||||
|
|
||||||
|
ps->btsb->load.ccch.pch_used += 1;
|
||||||
|
|
||||||
/* get (if we have) up to four paging records */
|
/* get (if we have) up to four paging records */
|
||||||
for (i = 0; i < ARRAY_SIZE(pr); i++) {
|
for (i = 0; i < ARRAY_SIZE(pr); i++) {
|
||||||
if (llist_empty(group_q))
|
if (llist_empty(group_q))
|
||||||
break;
|
break;
|
||||||
pr[i] = dequeue_pr(group_q);
|
pr[i] = dequeue_pr(group_q);
|
||||||
|
|
||||||
|
/* check for IMM.ASS */
|
||||||
|
if (pr[i]->type == PAGING_RECORD_IMM_ASS) {
|
||||||
|
imm_ass = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
num_pr++;
|
num_pr++;
|
||||||
|
|
||||||
/* count how many IMSIs are among them */
|
/* count how many IMSIs are among them */
|
||||||
@@ -322,27 +407,43 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
|
|||||||
num_imsi++;
|
num_imsi++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if we have an IMMEDIATE ASSIGNMENT */
|
||||||
|
if (imm_ass) {
|
||||||
|
/* re-add paging records */
|
||||||
|
for (i = 0; i < num_pr; i++)
|
||||||
|
llist_add(&pr[i]->list, group_q);
|
||||||
|
|
||||||
|
/* get message and free record */
|
||||||
|
memcpy(out_buf, pr[num_pr]->u.imm_ass.msg,
|
||||||
|
GSM_MACBLOCK_LEN);
|
||||||
|
pcu_tx_pch_data_cnf(gt->fn, pr[num_pr]->u.imm_ass.msg,
|
||||||
|
GSM_MACBLOCK_LEN);
|
||||||
|
talloc_free(pr[num_pr]);
|
||||||
|
return GSM_MACBLOCK_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
/* make sure the TMSIs are ahead of the IMSIs in the array */
|
/* make sure the TMSIs are ahead of the IMSIs in the array */
|
||||||
sort_pr_tmsi_imsi(pr, num_pr);
|
sort_pr_tmsi_imsi(pr, num_pr);
|
||||||
|
|
||||||
if (num_pr == 4 && num_imsi == 0) {
|
if (num_pr == 4 && num_imsi == 0) {
|
||||||
/* No IMSI: easy case, can use TYPE 3 */
|
/* No IMSI: easy case, can use TYPE 3 */
|
||||||
DEBUGP(DPAG, "Tx PAGING TYPE 3 (4 TMSI)\n");
|
DEBUGP(DPAG, "Tx PAGING TYPE 3 (4 TMSI)\n");
|
||||||
len = fill_paging_type_3(out_buf, pr[0]->identity_lv,
|
len = fill_paging_type_3(out_buf,
|
||||||
pr[0]->chan_needed,
|
pr[0]->u.paging.identity_lv,
|
||||||
pr[1]->identity_lv,
|
pr[0]->u.paging.chan_needed,
|
||||||
pr[1]->chan_needed,
|
pr[1]->u.paging.identity_lv,
|
||||||
pr[2]->identity_lv,
|
pr[1]->u.paging.chan_needed,
|
||||||
pr[3]->identity_lv);
|
pr[2]->u.paging.identity_lv,
|
||||||
|
pr[3]->u.paging.identity_lv);
|
||||||
} else if (num_pr >= 3 && num_imsi <= 1) {
|
} else if (num_pr >= 3 && num_imsi <= 1) {
|
||||||
/* 3 or 4, of which only up to 1 is IMSI */
|
/* 3 or 4, of which only up to 1 is IMSI */
|
||||||
DEBUGP(DPAG, "Tx PAGING TYPE 2 (2 TMSI,1 xMSI)\n");
|
DEBUGP(DPAG, "Tx PAGING TYPE 2 (2 TMSI,1 xMSI)\n");
|
||||||
len = fill_paging_type_2(out_buf,
|
len = fill_paging_type_2(out_buf,
|
||||||
pr[0]->identity_lv,
|
pr[0]->u.paging.identity_lv,
|
||||||
pr[0]->chan_needed,
|
pr[0]->u.paging.chan_needed,
|
||||||
pr[1]->identity_lv,
|
pr[1]->u.paging.identity_lv,
|
||||||
pr[1]->chan_needed,
|
pr[1]->u.paging.chan_needed,
|
||||||
pr[2]->identity_lv);
|
pr[2]->u.paging.identity_lv);
|
||||||
if (num_pr == 4) {
|
if (num_pr == 4) {
|
||||||
/* re-add #4 for next time */
|
/* re-add #4 for next time */
|
||||||
llist_add(&pr[3]->list, group_q);
|
llist_add(&pr[3]->list, group_q);
|
||||||
@@ -350,16 +451,19 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
|
|||||||
}
|
}
|
||||||
} else if (num_pr == 1) {
|
} else if (num_pr == 1) {
|
||||||
DEBUGP(DPAG, "Tx PAGING TYPE 1 (1 xMSI,1 empty)\n");
|
DEBUGP(DPAG, "Tx PAGING TYPE 1 (1 xMSI,1 empty)\n");
|
||||||
len = fill_paging_type_1(out_buf, pr[0]->identity_lv,
|
len = fill_paging_type_1(out_buf,
|
||||||
pr[0]->chan_needed, NULL, 0);
|
pr[0]->u.paging.identity_lv,
|
||||||
|
pr[0]->u.paging.chan_needed,
|
||||||
|
NULL, 0);
|
||||||
} else {
|
} else {
|
||||||
/* 2 (any type) or
|
/* 2 (any type) or
|
||||||
* 3 or 4, of which only 2 will be sent */
|
* 3 or 4, of which only 2 will be sent */
|
||||||
DEBUGP(DPAG, "Tx PAGING TYPE 1 (2 xMSI)\n");
|
DEBUGP(DPAG, "Tx PAGING TYPE 1 (2 xMSI)\n");
|
||||||
len = fill_paging_type_1(out_buf, pr[0]->identity_lv,
|
len = fill_paging_type_1(out_buf,
|
||||||
pr[0]->chan_needed,
|
pr[0]->u.paging.identity_lv,
|
||||||
pr[1]->identity_lv,
|
pr[0]->u.paging.chan_needed,
|
||||||
pr[1]->chan_needed);
|
pr[1]->u.paging.identity_lv,
|
||||||
|
pr[1]->u.paging.chan_needed);
|
||||||
if (num_pr >= 3) {
|
if (num_pr >= 3) {
|
||||||
/* re-add #4 for next time */
|
/* re-add #4 for next time */
|
||||||
llist_add(&pr[2]->list, group_q);
|
llist_add(&pr[2]->list, group_q);
|
||||||
@@ -378,7 +482,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
|
|||||||
continue;
|
continue;
|
||||||
/* check if we can expire the paging record,
|
/* check if we can expire the paging record,
|
||||||
* or if we need to re-queue it */
|
* or if we need to re-queue it */
|
||||||
if (pr[i]->expiration_time >= now) {
|
if (pr[i]->u.paging.expiration_time <= now) {
|
||||||
talloc_free(pr[i]);
|
talloc_free(pr[i]);
|
||||||
ps->num_paging--;
|
ps->num_paging--;
|
||||||
LOGP(DPAG, LOGL_INFO, "Removed paging record, queue_len=%u\n",
|
LOGP(DPAG, LOGL_INFO, "Removed paging record, queue_len=%u\n",
|
||||||
@@ -418,16 +522,18 @@ static int paging_signal_cbfn(unsigned int subsys, unsigned int signal, void *hd
|
|||||||
|
|
||||||
static int initialized = 0;
|
static int initialized = 0;
|
||||||
|
|
||||||
struct paging_state *paging_init(void *ctx, unsigned int num_paging_max,
|
struct paging_state *paging_init(struct gsm_bts_role_bts *btsb,
|
||||||
|
unsigned int num_paging_max,
|
||||||
unsigned int paging_lifetime)
|
unsigned int paging_lifetime)
|
||||||
{
|
{
|
||||||
struct paging_state *ps;
|
struct paging_state *ps;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
ps = talloc_zero(ctx, struct paging_state);
|
ps = talloc_zero(btsb, struct paging_state);
|
||||||
if (!ps)
|
if (!ps)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
ps->btsb = btsb;
|
||||||
ps->paging_lifetime = paging_lifetime;
|
ps->paging_lifetime = paging_lifetime;
|
||||||
ps->num_paging_max = num_paging_max;
|
ps->num_paging_max = num_paging_max;
|
||||||
|
|
||||||
@@ -460,3 +566,18 @@ void paging_reset(struct paging_state *ps)
|
|||||||
|
|
||||||
ps->num_paging = 0;
|
ps->num_paging = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Helper for the unit tests
|
||||||
|
*/
|
||||||
|
int paging_group_queue_empty(struct paging_state *ps, uint8_t grp)
|
||||||
|
{
|
||||||
|
if (grp >= ARRAY_SIZE(ps->paging_queue))
|
||||||
|
return 1;
|
||||||
|
return llist_empty(&ps->paging_queue[grp]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int paging_queue_length(struct paging_state *ps)
|
||||||
|
{
|
||||||
|
return ps->num_paging;
|
||||||
|
}
|
||||||
|
|||||||
921
src/common/pcu_sock.c
Normal file
921
src/common/pcu_sock.c
Normal file
@@ -0,0 +1,921 @@
|
|||||||
|
/* pcu_sock.c: Connect from PCU via unix domain socket */
|
||||||
|
|
||||||
|
/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
|
||||||
|
* (C) 2009-2012 by Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
* (C) 2012 by Holger Hans Peter Freyther
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/talloc.h>
|
||||||
|
#include <osmocom/core/select.h>
|
||||||
|
#include <osmo-bts/logging.h>
|
||||||
|
#include <osmo-bts/gsm_data.h>
|
||||||
|
#include <osmo-bts/pcu_if.h>
|
||||||
|
#include <osmo-bts/pcuif_proto.h>
|
||||||
|
#include <osmo-bts/bts.h>
|
||||||
|
#include <osmo-bts/rsl.h>
|
||||||
|
#include <osmo-bts/signal.h>
|
||||||
|
#include <osmo-bts/bts_model.h>
|
||||||
|
|
||||||
|
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx);
|
||||||
|
|
||||||
|
extern struct gsm_network bts_gsmnet;
|
||||||
|
extern int pcu_direct;
|
||||||
|
static int avail_lai = 0, avail_nse = 0, avail_cell = 0, avail_nsvc[2] = {0, 0};
|
||||||
|
|
||||||
|
static const char *sapi_string[] = {
|
||||||
|
[PCU_IF_SAPI_RACH] = "RACH",
|
||||||
|
[PCU_IF_SAPI_AGCH] = "AGCH",
|
||||||
|
[PCU_IF_SAPI_PCH] = "PCH",
|
||||||
|
[PCU_IF_SAPI_BCCH] = "BCCH",
|
||||||
|
[PCU_IF_SAPI_PDTCH] = "PDTCH",
|
||||||
|
[PCU_IF_SAPI_PRACH] = "PRACH",
|
||||||
|
[PCU_IF_SAPI_PTCCH] = "PTCCH",
|
||||||
|
};
|
||||||
|
|
||||||
|
/* FIXME: common l1if include ? */
|
||||||
|
int l1if_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
|
||||||
|
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
|
||||||
|
|
||||||
|
static int pcu_sock_send(struct gsm_network *net, struct msgb *msg);
|
||||||
|
/* FIXME: move this to libosmocore */
|
||||||
|
int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path);
|
||||||
|
|
||||||
|
|
||||||
|
static struct gsm_bts_trx *trx_by_nr(struct gsm_bts *bts, uint8_t trx_nr)
|
||||||
|
{
|
||||||
|
struct gsm_bts_trx *trx;
|
||||||
|
|
||||||
|
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||||
|
if (trx->nr == trx_nr)
|
||||||
|
return trx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PCU messages
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
|
||||||
|
{
|
||||||
|
struct msgb *msg;
|
||||||
|
struct gsm_pcu_if *pcu_prim;
|
||||||
|
|
||||||
|
msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx");
|
||||||
|
if (!msg)
|
||||||
|
return NULL;
|
||||||
|
msgb_put(msg, sizeof(struct gsm_pcu_if));
|
||||||
|
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||||
|
pcu_prim->msg_type = msg_type;
|
||||||
|
pcu_prim->bts_nr = bts_nr;
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pcu_tx_info_ind(void)
|
||||||
|
{
|
||||||
|
struct gsm_network *net = &bts_gsmnet;
|
||||||
|
struct msgb *msg;
|
||||||
|
struct gsm_pcu_if *pcu_prim;
|
||||||
|
struct gsm_pcu_if_info_ind *info_ind;
|
||||||
|
struct gsm_bts *bts;
|
||||||
|
struct gprs_rlc_cfg *rlcc;
|
||||||
|
struct gsm_bts_gprs_nsvc *nsvc;
|
||||||
|
struct gsm_bts_trx *trx;
|
||||||
|
struct gsm_bts_trx_ts *ts;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
LOGP(DPCU, LOGL_INFO, "Sending info\n");
|
||||||
|
|
||||||
|
/* FIXME: allow multiple BTS */
|
||||||
|
bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
|
||||||
|
rlcc = &bts->gprs.cell.rlc_cfg;
|
||||||
|
|
||||||
|
msg = pcu_msgb_alloc(PCU_IF_MSG_INFO_IND, bts->nr);
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||||
|
info_ind = &pcu_prim->u.info_ind;
|
||||||
|
info_ind->version = PCU_IF_VERSION;
|
||||||
|
|
||||||
|
if (avail_lai && avail_nse && avail_cell && avail_nsvc[0]) {
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_ACTIVE;
|
||||||
|
LOGP(DPCU, LOGL_INFO, "BTS is up\n");
|
||||||
|
} else
|
||||||
|
LOGP(DPCU, LOGL_INFO, "BTS is down\n");
|
||||||
|
|
||||||
|
if (pcu_direct)
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_SYSMO;
|
||||||
|
|
||||||
|
/* RAI */
|
||||||
|
info_ind->mcc = net->mcc;
|
||||||
|
info_ind->mnc = net->mnc;
|
||||||
|
info_ind->lac = bts->location_area_code;
|
||||||
|
info_ind->rac = bts->gprs.rac;
|
||||||
|
|
||||||
|
/* NSE */
|
||||||
|
info_ind->nsei = bts->gprs.nse.nsei;
|
||||||
|
memcpy(info_ind->nse_timer, bts->gprs.nse.timer, 7);
|
||||||
|
memcpy(info_ind->cell_timer, bts->gprs.cell.timer, 11);
|
||||||
|
|
||||||
|
/* cell attributes */
|
||||||
|
info_ind->cell_id = bts->cell_identity;
|
||||||
|
info_ind->repeat_time = rlcc->paging.repeat_time;
|
||||||
|
info_ind->repeat_count = rlcc->paging.repeat_count;
|
||||||
|
info_ind->bvci = bts->gprs.cell.bvci;
|
||||||
|
info_ind->t3142 = rlcc->parameter[RLC_T3142];
|
||||||
|
info_ind->t3169 = rlcc->parameter[RLC_T3169];
|
||||||
|
info_ind->t3191 = rlcc->parameter[RLC_T3191];
|
||||||
|
info_ind->t3193_10ms = rlcc->parameter[RLC_T3193];
|
||||||
|
info_ind->t3195 = rlcc->parameter[RLC_T3195];
|
||||||
|
info_ind->n3101 = rlcc->parameter[RLC_N3101];
|
||||||
|
info_ind->n3103 = rlcc->parameter[RLC_N3103];
|
||||||
|
info_ind->n3105 = rlcc->parameter[RLC_N3105];
|
||||||
|
info_ind->cv_countdown = rlcc->parameter[CV_COUNTDOWN];
|
||||||
|
if (rlcc->cs_mask & (1 << GPRS_CS1))
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_CS1;
|
||||||
|
if (rlcc->cs_mask & (1 << GPRS_CS2))
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_CS2;
|
||||||
|
if (rlcc->cs_mask & (1 << GPRS_CS3))
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_CS3;
|
||||||
|
if (rlcc->cs_mask & (1 << GPRS_CS4))
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_CS4;
|
||||||
|
if (rlcc->cs_mask & (1 << GPRS_MCS1))
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_MCS1;
|
||||||
|
if (rlcc->cs_mask & (1 << GPRS_MCS2))
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_MCS2;
|
||||||
|
if (rlcc->cs_mask & (1 << GPRS_MCS3))
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_MCS3;
|
||||||
|
if (rlcc->cs_mask & (1 << GPRS_MCS4))
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_MCS4;
|
||||||
|
if (rlcc->cs_mask & (1 << GPRS_MCS5))
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_MCS5;
|
||||||
|
if (rlcc->cs_mask & (1 << GPRS_MCS6))
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_MCS6;
|
||||||
|
if (rlcc->cs_mask & (1 << GPRS_MCS7))
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_MCS7;
|
||||||
|
if (rlcc->cs_mask & (1 << GPRS_MCS8))
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_MCS8;
|
||||||
|
if (rlcc->cs_mask & (1 << GPRS_MCS9))
|
||||||
|
info_ind->flags |= PCU_IF_FLAG_MCS9;
|
||||||
|
#warning "isn't dl_tbf_ext wrong?: * 10 and no ntohs"
|
||||||
|
info_ind->dl_tbf_ext = rlcc->parameter[T_DL_TBF_EXT];
|
||||||
|
#warning "isn't ul_tbf_ext wrong?: * 10 and no ntohs"
|
||||||
|
info_ind->ul_tbf_ext = rlcc->parameter[T_UL_TBF_EXT];
|
||||||
|
info_ind->initial_cs = rlcc->initial_cs;
|
||||||
|
info_ind->initial_mcs = rlcc->initial_mcs;
|
||||||
|
|
||||||
|
/* NSVC */
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
nsvc = &bts->gprs.nsvc[i];
|
||||||
|
info_ind->nsvci[i] = nsvc->nsvci;
|
||||||
|
info_ind->local_port[i] = nsvc->local_port;
|
||||||
|
info_ind->remote_port[i] = nsvc->remote_port;
|
||||||
|
info_ind->remote_ip[i] = nsvc->remote_ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
trx = trx_by_nr(bts, i);
|
||||||
|
if (!trx)
|
||||||
|
break;
|
||||||
|
info_ind->trx[i].pdch_mask = 0;
|
||||||
|
info_ind->trx[i].arfcn = trx->arfcn;
|
||||||
|
info_ind->trx[i].hlayer1 = trx_get_hlayer1(trx);
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
ts = &trx->ts[j];
|
||||||
|
if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
|
||||||
|
&& ts->pchan == GSM_PCHAN_PDCH) {
|
||||||
|
info_ind->trx[i].pdch_mask |= (1 << j);
|
||||||
|
info_ind->trx[i].tsc[j] =
|
||||||
|
(ts->tsc >= 0) ? ts->tsc : bts->tsc;
|
||||||
|
LOGP(DPCU, LOGL_INFO, "trx=%d ts=%d: "
|
||||||
|
"available (tsc=%d arfcn=%d)\n",
|
||||||
|
trx->nr, ts->nr,
|
||||||
|
info_ind->trx[i].tsc[j],
|
||||||
|
info_ind->trx[i].arfcn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pcu_sock_send(net, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pcu_if_signal_cb(unsigned int subsys, unsigned int signal,
|
||||||
|
void *hdlr_data, void *signal_data)
|
||||||
|
{
|
||||||
|
struct gsm_network *net = &bts_gsmnet;
|
||||||
|
struct gsm_bts_gprs_nsvc *nsvc;
|
||||||
|
struct gsm_bts *bts;
|
||||||
|
struct gsm48_system_information_type_3 *si3;
|
||||||
|
int id;
|
||||||
|
|
||||||
|
if (subsys != SS_GLOBAL)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
switch(signal) {
|
||||||
|
case S_NEW_SYSINFO:
|
||||||
|
bts = signal_data;
|
||||||
|
if (!(bts->si_valid & (1 << SYSINFO_TYPE_3)))
|
||||||
|
break;
|
||||||
|
si3 = (struct gsm48_system_information_type_3 *)
|
||||||
|
bts->si_buf[SYSINFO_TYPE_3];
|
||||||
|
net->mcc = ((si3->lai.digits[0] & 0x0f) << 8)
|
||||||
|
| (si3->lai.digits[0] & 0xf0)
|
||||||
|
| (si3->lai.digits[1] & 0x0f);
|
||||||
|
net->mnc = ((si3->lai.digits[2] & 0x0f) << 8)
|
||||||
|
| (si3->lai.digits[2] & 0xf0)
|
||||||
|
| ((si3->lai.digits[1] & 0xf0) >> 4);
|
||||||
|
if ((net->mnc & 0x00f) == 0x00f)
|
||||||
|
net->mnc >>= 4;
|
||||||
|
bts->location_area_code = ntohs(si3->lai.lac);
|
||||||
|
bts->cell_identity = si3->cell_identity;
|
||||||
|
avail_lai = 1;
|
||||||
|
break;
|
||||||
|
case S_NEW_NSE_ATTR:
|
||||||
|
bts = signal_data;
|
||||||
|
avail_nse = 1;
|
||||||
|
break;
|
||||||
|
case S_NEW_CELL_ATTR:
|
||||||
|
bts = signal_data;
|
||||||
|
avail_cell = 1;
|
||||||
|
break;
|
||||||
|
case S_NEW_NSVC_ATTR:
|
||||||
|
nsvc = signal_data;
|
||||||
|
id = nsvc->id;
|
||||||
|
if (id < 0 || id > 1)
|
||||||
|
return -EINVAL;
|
||||||
|
avail_nsvc[id] = 1;
|
||||||
|
break;
|
||||||
|
case S_NEW_OP_STATE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If all infos have been received, of if one info is updated after
|
||||||
|
* all infos have been received, transmit info update. */
|
||||||
|
if (avail_lai && avail_nse && avail_cell && avail_nsvc[0])
|
||||||
|
pcu_tx_info_ind();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int pcu_tx_rts_req(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
|
||||||
|
uint16_t arfcn, uint8_t block_nr)
|
||||||
|
{
|
||||||
|
struct msgb *msg;
|
||||||
|
struct gsm_pcu_if *pcu_prim;
|
||||||
|
struct gsm_pcu_if_rts_req *rts_req;
|
||||||
|
struct gsm_bts *bts = ts->trx->bts;
|
||||||
|
|
||||||
|
LOGP(DPCU, LOGL_DEBUG, "Sending rts request: is_ptcch=%d arfcn=%d "
|
||||||
|
"block=%d\n", is_ptcch, arfcn, block_nr);
|
||||||
|
|
||||||
|
msg = pcu_msgb_alloc(PCU_IF_MSG_RTS_REQ, bts->nr);
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||||
|
rts_req = &pcu_prim->u.rts_req;
|
||||||
|
|
||||||
|
rts_req->sapi = (is_ptcch) ? PCU_IF_SAPI_PTCCH : PCU_IF_SAPI_PDTCH;
|
||||||
|
rts_req->fn = fn;
|
||||||
|
rts_req->arfcn = arfcn;
|
||||||
|
rts_req->trx_nr = ts->trx->nr;
|
||||||
|
rts_req->ts_nr = ts->nr;
|
||||||
|
rts_req->block_nr = block_nr;
|
||||||
|
|
||||||
|
return pcu_sock_send(&bts_gsmnet, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
|
||||||
|
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
|
||||||
|
{
|
||||||
|
struct msgb *msg;
|
||||||
|
struct gsm_pcu_if *pcu_prim;
|
||||||
|
struct gsm_pcu_if_data *data_ind;
|
||||||
|
struct gsm_bts *bts = ts->trx->bts;
|
||||||
|
|
||||||
|
LOGP(DPCU, LOGL_DEBUG, "Sending data indication: is_ptcch=%d arfcn=%d "
|
||||||
|
"block=%d data=%s\n", is_ptcch, arfcn, block_nr,
|
||||||
|
osmo_hexdump(data, len));
|
||||||
|
|
||||||
|
msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_IND, bts->nr);
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||||
|
data_ind = &pcu_prim->u.data_ind;
|
||||||
|
|
||||||
|
data_ind->sapi = (is_ptcch) ? PCU_IF_SAPI_PTCCH : PCU_IF_SAPI_PDTCH;
|
||||||
|
data_ind->fn = fn;
|
||||||
|
data_ind->arfcn = arfcn;
|
||||||
|
data_ind->trx_nr = ts->trx->nr;
|
||||||
|
data_ind->ts_nr = ts->nr;
|
||||||
|
data_ind->block_nr = block_nr;
|
||||||
|
memcpy(data_ind->data, data, len);
|
||||||
|
data_ind->len = len;
|
||||||
|
|
||||||
|
return pcu_sock_send(&bts_gsmnet, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint8_t ra, uint32_t fn)
|
||||||
|
{
|
||||||
|
struct msgb *msg;
|
||||||
|
struct gsm_pcu_if *pcu_prim;
|
||||||
|
struct gsm_pcu_if_rach_ind *rach_ind;
|
||||||
|
|
||||||
|
LOGP(DPCU, LOGL_INFO, "Sending RACH indication: qta=%d, ra=%d, "
|
||||||
|
"fn=%d\n", qta, ra, fn);
|
||||||
|
|
||||||
|
msg = pcu_msgb_alloc(PCU_IF_MSG_RACH_IND, bts->nr);
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||||
|
rach_ind = &pcu_prim->u.rach_ind;
|
||||||
|
|
||||||
|
rach_ind->sapi = PCU_IF_SAPI_RACH;
|
||||||
|
rach_ind->ra = ra;
|
||||||
|
rach_ind->qta = qta;
|
||||||
|
rach_ind->fn = fn;
|
||||||
|
|
||||||
|
return pcu_sock_send(&bts_gsmnet, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pcu_tx_time_ind(uint32_t fn)
|
||||||
|
{
|
||||||
|
struct msgb *msg;
|
||||||
|
struct gsm_pcu_if *pcu_prim;
|
||||||
|
struct gsm_pcu_if_time_ind *time_ind;
|
||||||
|
uint8_t fn13 = fn % 13;
|
||||||
|
|
||||||
|
/* omit frame numbers not starting at a MAC block */
|
||||||
|
if (fn13 != 0 && fn13 != 4 && fn13 != 8)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
msg = pcu_msgb_alloc(PCU_IF_MSG_TIME_IND, 0);
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||||
|
time_ind = &pcu_prim->u.time_ind;
|
||||||
|
|
||||||
|
time_ind->fn = fn;
|
||||||
|
|
||||||
|
return pcu_sock_send(&bts_gsmnet, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pcu_tx_pag_req(uint8_t *identity_lv, uint8_t chan_needed)
|
||||||
|
{
|
||||||
|
struct pcu_sock_state *state = bts_gsmnet.pcu_state;
|
||||||
|
struct msgb *msg;
|
||||||
|
struct gsm_pcu_if *pcu_prim;
|
||||||
|
struct gsm_pcu_if_pag_req *pag_req;
|
||||||
|
|
||||||
|
/* check if identity does not fit: length > sizeof(lv) - 1 */
|
||||||
|
if (identity_lv[0] >= sizeof(pag_req->identity_lv)) {
|
||||||
|
LOGP(DPCU, LOGL_ERROR, "Paging identity too large (%d)\n",
|
||||||
|
identity_lv[0]);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* socket not created */
|
||||||
|
if (!state) {
|
||||||
|
LOGP(DPCU, LOGL_DEBUG, "PCU socket not created, ignoring "
|
||||||
|
"paging message\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = pcu_msgb_alloc(PCU_IF_MSG_PAG_REQ, 0);
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||||
|
pag_req = &pcu_prim->u.pag_req;
|
||||||
|
|
||||||
|
pag_req->chan_needed = chan_needed;
|
||||||
|
memcpy(pag_req->identity_lv, identity_lv, identity_lv[0] + 1);
|
||||||
|
|
||||||
|
return pcu_sock_send(&bts_gsmnet, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pcu_tx_pch_data_cnf(uint32_t fn, uint8_t *data, uint8_t len)
|
||||||
|
{
|
||||||
|
struct gsm_network *net = &bts_gsmnet;
|
||||||
|
struct gsm_bts *bts;
|
||||||
|
struct msgb *msg;
|
||||||
|
struct gsm_pcu_if *pcu_prim;
|
||||||
|
struct gsm_pcu_if_data *data_cnf;
|
||||||
|
|
||||||
|
/* FIXME: allow multiple BTS */
|
||||||
|
bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
|
||||||
|
|
||||||
|
LOGP(DPCU, LOGL_INFO, "Sending PCH confirm\n");
|
||||||
|
|
||||||
|
msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_CNF, bts->nr);
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||||
|
data_cnf = &pcu_prim->u.data_cnf;
|
||||||
|
|
||||||
|
data_cnf->sapi = PCU_IF_SAPI_PCH;
|
||||||
|
data_cnf->fn = fn;
|
||||||
|
memcpy(data_cnf->data, data, len);
|
||||||
|
data_cnf->len = len;
|
||||||
|
|
||||||
|
return pcu_sock_send(&bts_gsmnet, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
|
||||||
|
struct gsm_pcu_if_data *data_req)
|
||||||
|
{
|
||||||
|
uint8_t is_ptcch;
|
||||||
|
struct gsm_bts_trx *trx;
|
||||||
|
struct gsm_bts_trx_ts *ts;
|
||||||
|
struct msgb *msg;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
LOGP(DPCU, LOGL_DEBUG, "Data request received: sapi=%s arfcn=%d "
|
||||||
|
"block=%d data=%s\n", sapi_string[data_req->sapi],
|
||||||
|
data_req->arfcn, data_req->block_nr,
|
||||||
|
osmo_hexdump(data_req->data, data_req->len));
|
||||||
|
|
||||||
|
switch (data_req->sapi) {
|
||||||
|
case PCU_IF_SAPI_BCCH:
|
||||||
|
if (data_req->len == 23) {
|
||||||
|
bts->si_valid |= (1 << SYSINFO_TYPE_13);
|
||||||
|
memcpy(bts->si_buf[SYSINFO_TYPE_13], data_req->data,
|
||||||
|
data_req->len);
|
||||||
|
} else {
|
||||||
|
bts->si_valid &= ~(1 << SYSINFO_TYPE_13);
|
||||||
|
}
|
||||||
|
osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts);
|
||||||
|
break;
|
||||||
|
case PCU_IF_SAPI_PCH:
|
||||||
|
if (msg_type == PCU_IF_MSG_PAG_REQ) {
|
||||||
|
/* FIXME: Add function to schedule paging request.
|
||||||
|
* This might not be required, if PCU_IF_MSG_DATA_REQ
|
||||||
|
* is used instead. */
|
||||||
|
} else {
|
||||||
|
struct gsm_bts_role_bts *btsb = bts->role;
|
||||||
|
|
||||||
|
paging_add_imm_ass(btsb->paging_state, data_req->data,
|
||||||
|
data_req->len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PCU_IF_SAPI_AGCH:
|
||||||
|
msg = msgb_alloc(data_req->len, "pcu_agch");
|
||||||
|
if (!msg) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memcpy(msgb_put(msg, data_req->len), data_req->data, data_req->len);
|
||||||
|
if (bts_agch_enqueue(bts, msg) < 0) {
|
||||||
|
msgb_free(msg);
|
||||||
|
rc = -EIO;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PCU_IF_SAPI_PDTCH:
|
||||||
|
case PCU_IF_SAPI_PTCCH:
|
||||||
|
trx = trx_by_nr(bts, data_req->trx_nr);
|
||||||
|
if (!trx) {
|
||||||
|
LOGP(DPCU, LOGL_ERROR, "Received PCU data request with "
|
||||||
|
"not existing TRX %d\n", data_req->trx_nr);
|
||||||
|
rc = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ts = &trx->ts[data_req->ts_nr];
|
||||||
|
is_ptcch = (data_req->sapi == PCU_IF_SAPI_PTCCH);
|
||||||
|
rc = l1if_pdch_req(ts, is_ptcch, data_req->fn, data_req->arfcn,
|
||||||
|
data_req->block_nr, data_req->data, data_req->len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGP(DPCU, LOGL_ERROR, "Received PCU data request with "
|
||||||
|
"unsupported sapi %d\n", data_req->sapi);
|
||||||
|
rc = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pcu_rx_act_req(struct gsm_bts *bts,
|
||||||
|
struct gsm_pcu_if_act_req *act_req)
|
||||||
|
{
|
||||||
|
struct gsm_bts_trx *trx;
|
||||||
|
struct gsm_lchan *lchan;
|
||||||
|
|
||||||
|
LOGP(DPCU, LOGL_INFO, "%s request received: TRX=%d TX=%d\n",
|
||||||
|
(act_req->activate) ? "Activate" : "Deactivate",
|
||||||
|
act_req->trx_nr, act_req->ts_nr);
|
||||||
|
|
||||||
|
trx = trx_by_nr(bts, act_req->trx_nr);
|
||||||
|
if (!trx || act_req->ts_nr >= 8)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
lchan = trx->ts[act_req->ts_nr].lchan;
|
||||||
|
if (lchan->type != GSM_LCHAN_PDTCH) {
|
||||||
|
LOGP(DPCU, LOGL_ERROR, "Lchan is not of type PDCH, but %d.\n",
|
||||||
|
lchan->type);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (act_req->activate)
|
||||||
|
bts_model_rsl_chan_act(lchan, NULL);
|
||||||
|
else
|
||||||
|
bts_model_rsl_chan_rel(lchan);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
|
||||||
|
struct gsm_pcu_if *pcu_prim)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
struct gsm_bts *bts;
|
||||||
|
|
||||||
|
/* FIXME: allow multiple BTS */
|
||||||
|
bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
|
||||||
|
|
||||||
|
switch (msg_type) {
|
||||||
|
case PCU_IF_MSG_DATA_REQ:
|
||||||
|
case PCU_IF_MSG_PAG_REQ:
|
||||||
|
rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req);
|
||||||
|
break;
|
||||||
|
case PCU_IF_MSG_ACT_REQ:
|
||||||
|
rc = pcu_rx_act_req(bts, &pcu_prim->u.act_req);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGP(DPCU, LOGL_ERROR, "Received unknwon PCU msg type %d\n",
|
||||||
|
msg_type);
|
||||||
|
rc = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PCU socket interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct pcu_sock_state {
|
||||||
|
struct gsm_network *net;
|
||||||
|
struct osmo_fd listen_bfd; /* fd for listen socket */
|
||||||
|
struct osmo_fd conn_bfd; /* fd for connection to lcr */
|
||||||
|
struct llist_head upqueue; /* queue for sending messages */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int pcu_sock_send(struct gsm_network *net, struct msgb *msg)
|
||||||
|
{
|
||||||
|
struct pcu_sock_state *state = net->pcu_state;
|
||||||
|
struct osmo_fd *conn_bfd;
|
||||||
|
struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND)
|
||||||
|
LOGP(DPCU, LOGL_INFO, "PCU socket not created, "
|
||||||
|
"dropping message\n");
|
||||||
|
msgb_free(msg);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
conn_bfd = &state->conn_bfd;
|
||||||
|
if (conn_bfd->fd <= 0) {
|
||||||
|
if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND)
|
||||||
|
LOGP(DPCU, LOGL_NOTICE, "PCU socket not connected, "
|
||||||
|
"dropping message\n");
|
||||||
|
msgb_free(msg);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
msgb_enqueue(&state->upqueue, msg);
|
||||||
|
conn_bfd->when |= BSC_FD_WRITE;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pcu_sock_close(struct pcu_sock_state *state)
|
||||||
|
{
|
||||||
|
struct osmo_fd *bfd = &state->conn_bfd;
|
||||||
|
struct gsm_bts *bts;
|
||||||
|
struct gsm_bts_trx *trx;
|
||||||
|
struct gsm_bts_trx_ts *ts;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
/* FIXME: allow multiple BTS */
|
||||||
|
bts = llist_entry(state->net->bts_list.next, struct gsm_bts, list);
|
||||||
|
|
||||||
|
LOGP(DPCU, LOGL_NOTICE, "PCU socket has LOST connection\n");
|
||||||
|
|
||||||
|
close(bfd->fd);
|
||||||
|
bfd->fd = -1;
|
||||||
|
osmo_fd_unregister(bfd);
|
||||||
|
|
||||||
|
/* re-enable the generation of ACCEPT for new connections */
|
||||||
|
state->listen_bfd.when |= BSC_FD_READ;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* remove si13, ... */
|
||||||
|
bts->si_valid &= ~(1 << SYSINFO_TYPE_13);
|
||||||
|
osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* release PDCH */
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
trx = trx_by_nr(bts, i);
|
||||||
|
if (!trx)
|
||||||
|
break;
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
ts = &trx->ts[j];
|
||||||
|
if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
|
||||||
|
&& ts->pchan == GSM_PCHAN_PDCH)
|
||||||
|
bts_model_rsl_chan_rel(ts->lchan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* flush the queue */
|
||||||
|
while (!llist_empty(&state->upqueue)) {
|
||||||
|
struct msgb *msg = msgb_dequeue(&state->upqueue);
|
||||||
|
msgb_free(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pcu_sock_read(struct osmo_fd *bfd)
|
||||||
|
{
|
||||||
|
struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
|
||||||
|
struct gsm_pcu_if *pcu_prim;
|
||||||
|
struct msgb *msg;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pcu_prim = (struct gsm_pcu_if *) msg->tail;
|
||||||
|
|
||||||
|
rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
|
||||||
|
if (rc == 0)
|
||||||
|
goto close;
|
||||||
|
|
||||||
|
if (rc < 0) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return 0;
|
||||||
|
goto close;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim);
|
||||||
|
|
||||||
|
/* as we always synchronously process the message in pcu_rx() and
|
||||||
|
* its callbacks, we can free the message here. */
|
||||||
|
msgb_free(msg);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
close:
|
||||||
|
msgb_free(msg);
|
||||||
|
pcu_sock_close(state);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pcu_sock_write(struct osmo_fd *bfd)
|
||||||
|
{
|
||||||
|
struct pcu_sock_state *state = bfd->data;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
while (!llist_empty(&state->upqueue)) {
|
||||||
|
struct msgb *msg, *msg2;
|
||||||
|
struct gsm_pcu_if *pcu_prim;
|
||||||
|
|
||||||
|
/* peek at the beginning of the queue */
|
||||||
|
msg = llist_entry(state->upqueue.next, struct msgb, list);
|
||||||
|
pcu_prim = (struct gsm_pcu_if *)msg->data;
|
||||||
|
|
||||||
|
bfd->when &= ~BSC_FD_WRITE;
|
||||||
|
|
||||||
|
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
|
||||||
|
if (!msgb_length(msg)) {
|
||||||
|
LOGP(DPCU, LOGL_ERROR, "message type (%d) with ZERO "
|
||||||
|
"bytes!\n", pcu_prim->msg_type);
|
||||||
|
goto dontsend;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* try to send it over the socket */
|
||||||
|
rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
|
||||||
|
if (rc == 0)
|
||||||
|
goto close;
|
||||||
|
if (rc < 0) {
|
||||||
|
if (errno == EAGAIN) {
|
||||||
|
bfd->when |= BSC_FD_WRITE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
goto close;
|
||||||
|
}
|
||||||
|
|
||||||
|
dontsend:
|
||||||
|
/* _after_ we send it, we can deueue */
|
||||||
|
msg2 = msgb_dequeue(&state->upqueue);
|
||||||
|
assert(msg == msg2);
|
||||||
|
msgb_free(msg);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
close:
|
||||||
|
pcu_sock_close(state);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if (flags & BSC_FD_READ)
|
||||||
|
rc = pcu_sock_read(bfd);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (flags & BSC_FD_WRITE)
|
||||||
|
rc = pcu_sock_write(bfd);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* accept connection comming from PCU */
|
||||||
|
static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
|
||||||
|
struct osmo_fd *conn_bfd = &state->conn_bfd;
|
||||||
|
struct sockaddr_un un_addr;
|
||||||
|
socklen_t len;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
len = sizeof(un_addr);
|
||||||
|
rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DPCU, LOGL_ERROR, "Failed to accept a new connection\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn_bfd->fd >= 0) {
|
||||||
|
LOGP(DPCU, LOGL_NOTICE, "PCU connects but we already have "
|
||||||
|
"another active connection ?!?\n");
|
||||||
|
/* We already have one PCU connected, this is all we support */
|
||||||
|
state->listen_bfd.when &= ~BSC_FD_READ;
|
||||||
|
close(rc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn_bfd->fd = rc;
|
||||||
|
conn_bfd->when = BSC_FD_READ;
|
||||||
|
conn_bfd->cb = pcu_sock_cb;
|
||||||
|
conn_bfd->data = state;
|
||||||
|
|
||||||
|
if (osmo_fd_register(conn_bfd) != 0) {
|
||||||
|
LOGP(DPCU, LOGL_ERROR, "Failed to register new connection "
|
||||||
|
"fd\n");
|
||||||
|
close(conn_bfd->fd);
|
||||||
|
conn_bfd->fd = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGP(DPCU, LOGL_NOTICE, "PCU socket has connection with external "
|
||||||
|
"call control application\n");
|
||||||
|
|
||||||
|
/* send current info */
|
||||||
|
pcu_tx_info_ind();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pcu_sock_init(void)
|
||||||
|
{
|
||||||
|
struct pcu_sock_state *state;
|
||||||
|
struct osmo_fd *bfd;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
state = talloc_zero(NULL, struct pcu_sock_state);
|
||||||
|
if (!state)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
INIT_LLIST_HEAD(&state->upqueue);
|
||||||
|
state->net = &bts_gsmnet;
|
||||||
|
state->conn_bfd.fd = -1;
|
||||||
|
|
||||||
|
bfd = &state->listen_bfd;
|
||||||
|
|
||||||
|
rc = osmo_unixsock_listen(bfd, SOCK_SEQPACKET, "/tmp/pcu_bts");
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DPCU, LOGL_ERROR, "Could not create unix socket: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
talloc_free(state);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bfd->when = BSC_FD_READ;
|
||||||
|
bfd->cb = pcu_sock_accept;
|
||||||
|
bfd->data = state;
|
||||||
|
|
||||||
|
rc = osmo_fd_register(bfd);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DPCU, LOGL_ERROR, "Could not register listen fd: %d\n",
|
||||||
|
rc);
|
||||||
|
close(bfd->fd);
|
||||||
|
talloc_free(state);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmo_signal_register_handler(SS_GLOBAL, pcu_if_signal_cb, NULL);
|
||||||
|
|
||||||
|
bts_gsmnet.pcu_state = state;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pcu_sock_exit(void)
|
||||||
|
{
|
||||||
|
struct pcu_sock_state *state = bts_gsmnet.pcu_state;
|
||||||
|
struct osmo_fd *bfd, *conn_bfd;
|
||||||
|
|
||||||
|
if (!state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
osmo_signal_unregister_handler(SS_GLOBAL, pcu_if_signal_cb, NULL);
|
||||||
|
conn_bfd = &state->conn_bfd;
|
||||||
|
if (conn_bfd->fd > 0)
|
||||||
|
pcu_sock_close(state);
|
||||||
|
bfd = &state->listen_bfd;
|
||||||
|
close(bfd->fd);
|
||||||
|
osmo_fd_unregister(bfd);
|
||||||
|
talloc_free(state);
|
||||||
|
bts_gsmnet.pcu_state = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: move this to libosmocore */
|
||||||
|
int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path)
|
||||||
|
{
|
||||||
|
struct sockaddr_un local;
|
||||||
|
unsigned int namelen;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
bfd->fd = socket(AF_UNIX, type, 0);
|
||||||
|
|
||||||
|
if (bfd->fd < 0) {
|
||||||
|
fprintf(stderr, "Failed to create Unix Domain Socket.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
local.sun_family = AF_UNIX;
|
||||||
|
strncpy(local.sun_path, path, sizeof(local.sun_path));
|
||||||
|
local.sun_path[sizeof(local.sun_path) - 1] = '\0';
|
||||||
|
unlink(local.sun_path);
|
||||||
|
|
||||||
|
/* we use the same magic that X11 uses in Xtranssock.c for
|
||||||
|
* calculating the proper length of the sockaddr */
|
||||||
|
#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
|
||||||
|
local.sun_len = strlen(local.sun_path);
|
||||||
|
#endif
|
||||||
|
#if defined(BSD44SOCKETS) || defined(SUN_LEN)
|
||||||
|
namelen = SUN_LEN(&local);
|
||||||
|
#else
|
||||||
|
namelen = strlen(local.sun_path) +
|
||||||
|
offsetof(struct sockaddr_un, sun_path);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
rc = bind(bfd->fd, (struct sockaddr *) &local, namelen);
|
||||||
|
if (rc != 0) {
|
||||||
|
fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n",
|
||||||
|
local.sun_path);
|
||||||
|
close(bfd->fd);
|
||||||
|
bfd->fd = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen(bfd->fd, 0) != 0) {
|
||||||
|
fprintf(stderr, "Failed to listen.\n");
|
||||||
|
close(bfd->fd);
|
||||||
|
bfd->fd = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
279
src/common/rsl.c
279
src/common/rsl.c
@@ -38,6 +38,7 @@
|
|||||||
#include <osmo-bts/bts.h>
|
#include <osmo-bts/bts.h>
|
||||||
#include <osmo-bts/rsl.h>
|
#include <osmo-bts/rsl.h>
|
||||||
#include <osmo-bts/oml.h>
|
#include <osmo-bts/oml.h>
|
||||||
|
#include <osmo-bts/amr.h>
|
||||||
#include <osmo-bts/signal.h>
|
#include <osmo-bts/signal.h>
|
||||||
#include <osmo-bts/bts_model.h>
|
#include <osmo-bts/bts_model.h>
|
||||||
#include <osmo-bts/measurement.h>
|
#include <osmo-bts/measurement.h>
|
||||||
@@ -121,89 +122,18 @@ static void lchan_tchmode_from_cmode(struct gsm_lchan *lchan,
|
|||||||
* support
|
* support
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void log_mr_conf(int ss, int logl, const char *pfx,
|
/**
|
||||||
struct amr_multirate_conf *amr_mrc)
|
* Handle GSM 08.58 7 Error Handling for the given input. This method will
|
||||||
|
* send either a CHANNEL ACTIVATION NACK, MODE MODIFY NACK or ERROR REPORT
|
||||||
|
* depending on the input of the method.
|
||||||
|
*
|
||||||
|
* TODO: actually make the decision
|
||||||
|
*/
|
||||||
|
static int report_error(struct gsm_bts_trx *trx)
|
||||||
{
|
{
|
||||||
int i;
|
return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT);
|
||||||
|
|
||||||
LOGP(ss, logl, "%s AMR MR Conf: num_modes=%u",
|
|
||||||
pfx, amr_mrc->num_modes);
|
|
||||||
|
|
||||||
for (i = 0; i < amr_mrc->num_modes; i++)
|
|
||||||
LOGPC(ss, logl, ", mode[%u] = %u/%u/%u",
|
|
||||||
i, amr_mrc->mode[i].mode, amr_mrc->mode[i].threshold,
|
|
||||||
amr_mrc->mode[i].hysteresis);
|
|
||||||
LOGPC(ss, logl, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* parse a GSM 04.08 MultiRate Config IE (10.5.2.21aa) in a more
|
|
||||||
* comfortable internal data structure */
|
|
||||||
static int parse_mr_conf(struct amr_multirate_conf *amr_mrc,
|
|
||||||
const uint8_t *mr_conf, unsigned int len)
|
|
||||||
{
|
|
||||||
uint8_t mr_version = mr_conf[0] >> 5;
|
|
||||||
uint8_t num_codecs = 0;
|
|
||||||
int i, j = 0;
|
|
||||||
|
|
||||||
if (mr_version != 1) {
|
|
||||||
LOGP(DRSL, LOGL_ERROR, "AMR Multirate Version %u unknonw\n",
|
|
||||||
mr_version);
|
|
||||||
goto ret_einval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check number of active codecs */
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
if (mr_conf[1] & (1 << i))
|
|
||||||
num_codecs++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check for minimum length */
|
|
||||||
if (num_codecs == 0 ||
|
|
||||||
(num_codecs == 1 && len < 2) ||
|
|
||||||
(num_codecs == 2 && len < 4) ||
|
|
||||||
(num_codecs == 3 && len < 5) ||
|
|
||||||
(num_codecs == 4 && len < 6) ||
|
|
||||||
(num_codecs > 4)) {
|
|
||||||
LOGP(DRSL, LOGL_ERROR, "AMR Multirate with %u modes len=%u "
|
|
||||||
"not possible\n", num_codecs, len);
|
|
||||||
goto ret_einval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy the first two octets of the IE */
|
|
||||||
amr_mrc->gsm48_ie[0] = mr_conf[0];
|
|
||||||
amr_mrc->gsm48_ie[1] = mr_conf[1];
|
|
||||||
|
|
||||||
amr_mrc->num_modes = num_codecs;
|
|
||||||
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
if (mr_conf[1] & (1 << i)) {
|
|
||||||
amr_mrc->mode[j++].mode = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (num_codecs >= 2) {
|
|
||||||
amr_mrc->mode[0].threshold = mr_conf[1] & 0x3F;
|
|
||||||
amr_mrc->mode[0].hysteresis = mr_conf[2] >> 4;
|
|
||||||
}
|
|
||||||
if (num_codecs >= 3) {
|
|
||||||
amr_mrc->mode[1].threshold =
|
|
||||||
((mr_conf[2] & 0xF) << 2) | (mr_conf[3] >> 6);
|
|
||||||
amr_mrc->mode[1].hysteresis = (mr_conf[3] >> 2) & 0x7;
|
|
||||||
}
|
|
||||||
if (num_codecs >= 4) {
|
|
||||||
amr_mrc->mode[3].threshold =
|
|
||||||
((mr_conf[3] & 0x3) << 4) | (mr_conf[4] >> 4);
|
|
||||||
amr_mrc->mode[3].hysteresis = mr_conf[4] & 0xF;
|
|
||||||
}
|
|
||||||
|
|
||||||
return num_codecs;
|
|
||||||
|
|
||||||
ret_einval:
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#warning merge lchan_lookup with OpenBSC
|
#warning merge lchan_lookup with OpenBSC
|
||||||
/* determine logical channel based on TRX and channel number IE */
|
/* determine logical channel based on TRX and channel number IE */
|
||||||
struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr)
|
struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr)
|
||||||
@@ -409,16 +339,38 @@ int rsl_tx_ccch_load_ind_pch(struct gsm_bts *bts, uint16_t paging_avail)
|
|||||||
{
|
{
|
||||||
struct msgb *msg;
|
struct msgb *msg;
|
||||||
|
|
||||||
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr));
|
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_cchan_hdr));
|
||||||
if (!msg)
|
if (!msg)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
rsl_trx_push_hdr(msg, RSL_MT_CCCH_LOAD_IND);
|
rsl_cch_push_hdr(msg, RSL_MT_CCCH_LOAD_IND, RSL_CHAN_PCH_AGCH);
|
||||||
msgb_tv16_put(msg, RSL_IE_PAGING_LOAD, paging_avail);
|
msgb_tv16_put(msg, RSL_IE_PAGING_LOAD, paging_avail);
|
||||||
msg->trx = bts->c0;
|
msg->trx = bts->c0;
|
||||||
|
|
||||||
return abis_rsl_sendmsg(msg);
|
return abis_rsl_sendmsg(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 8.5.2 CCCH Load Indication (RACH) */
|
||||||
|
int rsl_tx_ccch_load_ind_rach(struct gsm_bts *bts, uint16_t total,
|
||||||
|
uint16_t busy, uint16_t access)
|
||||||
|
{
|
||||||
|
struct msgb *msg;
|
||||||
|
|
||||||
|
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_cchan_hdr));
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
rsl_cch_push_hdr(msg, RSL_MT_CCCH_LOAD_IND, RSL_CHAN_RACH);
|
||||||
|
/* tag and length */
|
||||||
|
msgb_tv_put(msg, RSL_IE_RACH_LOAD, 6);
|
||||||
|
/* content of the IE */
|
||||||
|
msgb_put_u16(msg, total);
|
||||||
|
msgb_put_u16(msg, busy);
|
||||||
|
msgb_put_u16(msg, access);
|
||||||
|
|
||||||
|
msg->trx = bts->c0;
|
||||||
|
|
||||||
|
return abis_rsl_sendmsg(msg);
|
||||||
|
}
|
||||||
|
|
||||||
/* 8.5.5 PAGING COMMAND */
|
/* 8.5.5 PAGING COMMAND */
|
||||||
static int rsl_rx_paging_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
|
static int rsl_rx_paging_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
|
||||||
{
|
{
|
||||||
@@ -446,30 +398,11 @@ static int rsl_rx_paging_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
|
|||||||
/* FIXME: notfiy the BSC somehow ?*/
|
/* FIXME: notfiy the BSC somehow ?*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pcu_tx_pag_req(identity_lv, chan_needed);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rsl_tx_ccch_load_ind_rach(struct gsm_bts *bts, uint16_t rach_slots,
|
|
||||||
uint16_t rach_busy, uint16_t rach_access)
|
|
||||||
{
|
|
||||||
struct msgb *msg;
|
|
||||||
uint16_t payload[3];
|
|
||||||
|
|
||||||
payload[0] = htons(rach_slots);
|
|
||||||
payload[1] = htons(rach_busy);
|
|
||||||
payload[2] = htons(rach_access);
|
|
||||||
|
|
||||||
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr));
|
|
||||||
if (!msg)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
msgb_tlv_put(msg, RSL_IE_RACH_LOAD, 6, (uint8_t *)payload);
|
|
||||||
rsl_trx_push_hdr(msg, RSL_MT_CCCH_LOAD_IND);
|
|
||||||
msg->trx = bts->c0;
|
|
||||||
|
|
||||||
return abis_rsl_sendmsg(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 8.6.2 SACCH FILLING */
|
/* 8.6.2 SACCH FILLING */
|
||||||
static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg)
|
static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg)
|
||||||
{
|
{
|
||||||
@@ -499,8 +432,8 @@ static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg)
|
|||||||
if (len > sizeof(sysinfo_buf_t)-2)
|
if (len > sizeof(sysinfo_buf_t)-2)
|
||||||
len = sizeof(sysinfo_buf_t)-2;
|
len = sizeof(sysinfo_buf_t)-2;
|
||||||
bts->si_valid |= (1 << osmo_si);
|
bts->si_valid |= (1 << osmo_si);
|
||||||
bts->si_buf[osmo_si][0] = 0x00;
|
bts->si_buf[osmo_si][0] = 0x03; /* C/R + EA */
|
||||||
bts->si_buf[osmo_si][1] = 0x03;
|
bts->si_buf[osmo_si][1] = 0x03; /* UI frame */
|
||||||
memcpy(bts->si_buf[osmo_si]+2,
|
memcpy(bts->si_buf[osmo_si]+2,
|
||||||
TLVP_VAL(&tp, RSL_IE_L3_INFO), len);
|
TLVP_VAL(&tp, RSL_IE_L3_INFO), len);
|
||||||
LOGP(DRSL, LOGL_INFO, " Rx RSL SACCH FILLING (SI%s)\n",
|
LOGP(DRSL, LOGL_INFO, " Rx RSL SACCH FILLING (SI%s)\n",
|
||||||
@@ -544,14 +477,18 @@ static int rsl_rx_imm_ass(struct gsm_bts_trx *trx, struct msgb *msg)
|
|||||||
* dedicated channel related messages
|
* dedicated channel related messages
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* 8.4.19 sebdubg RF CHANnel RELease ACKnowledge */
|
/* 8.4.19 sending RF CHANnel RELease ACKnowledge */
|
||||||
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
|
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
|
||||||
{
|
{
|
||||||
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
struct msgb *msg;
|
||||||
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
||||||
|
|
||||||
LOGP(DRSL, LOGL_NOTICE, "%s Tx RF CHAN REL ACK\n", gsm_lchan_name(lchan));
|
LOGP(DRSL, LOGL_NOTICE, "%s Tx RF CHAN REL ACK\n", gsm_lchan_name(lchan));
|
||||||
|
|
||||||
|
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
rsl_dch_push_hdr(msg, RSL_MT_RF_CHAN_REL_ACK, chan_nr);
|
rsl_dch_push_hdr(msg, RSL_MT_RF_CHAN_REL_ACK, chan_nr);
|
||||||
msg->trx = lchan->ts->trx;
|
msg->trx = lchan->ts->trx;
|
||||||
|
|
||||||
@@ -561,12 +498,16 @@ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
|
|||||||
/* 8.4.2 sending CHANnel ACTIVation ACKnowledge */
|
/* 8.4.2 sending CHANnel ACTIVation ACKnowledge */
|
||||||
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime)
|
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime)
|
||||||
{
|
{
|
||||||
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
struct msgb *msg;
|
||||||
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
||||||
uint8_t ie[2];
|
uint8_t ie[2];
|
||||||
|
|
||||||
LOGP(DRSL, LOGL_NOTICE, "%s Tx CHAN ACT ACK\n", gsm_lchan_name(lchan));
|
LOGP(DRSL, LOGL_NOTICE, "%s Tx CHAN ACT ACK\n", gsm_lchan_name(lchan));
|
||||||
|
|
||||||
|
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
gsm48_gen_starting_time(ie, gtime);
|
gsm48_gen_starting_time(ie, gtime);
|
||||||
msgb_tv_fixed_put(msg, RSL_IE_FRAME_NUMBER, 2, ie);
|
msgb_tv_fixed_put(msg, RSL_IE_FRAME_NUMBER, 2, ie);
|
||||||
rsl_dch_push_hdr(msg, RSL_MT_CHAN_ACTIV_ACK, chan_nr);
|
rsl_dch_push_hdr(msg, RSL_MT_CHAN_ACTIV_ACK, chan_nr);
|
||||||
@@ -579,20 +520,21 @@ int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 8.4.3 sending CHANnel ACTIVation Negative ACK */
|
/* 8.4.3 sending CHANnel ACTIVation Negative ACK */
|
||||||
static int rsl_tx_chan_nack(struct gsm_bts_trx *trx, struct msgb *msg, uint8_t cause)
|
int rsl_tx_chan_act_nack(struct gsm_lchan *lchan, uint8_t cause)
|
||||||
{
|
{
|
||||||
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
|
struct msgb *msg;
|
||||||
uint8_t chan_nr = dch->chan_nr;
|
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
||||||
|
|
||||||
LOGP(DRSL, LOGL_NOTICE, "Sending Channel Activated NACK: cause = 0x%02x\n", cause);
|
LOGP(DRSL, LOGL_NOTICE, "Sending Channel Activated NACK: cause = 0x%02x\n", cause);
|
||||||
|
|
||||||
msg->len = 0;
|
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
||||||
msg->data = msg->tail = msg->l3h;
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
/* 9.3.26 Cause */
|
/* 9.3.26 Cause */
|
||||||
msgb_tlv_put(msg, RSL_IE_CAUSE, 1, &cause);
|
msgb_tlv_put(msg, RSL_IE_CAUSE, 1, &cause);
|
||||||
rsl_dch_push_hdr(msg, RSL_MT_CHAN_ACTIV_NACK, chan_nr);
|
rsl_dch_push_hdr(msg, RSL_MT_CHAN_ACTIV_NACK, chan_nr);
|
||||||
msg->trx = trx;
|
msg->trx = lchan->ts->trx;
|
||||||
|
|
||||||
return abis_rsl_sendmsg(msg);
|
return abis_rsl_sendmsg(msg);
|
||||||
}
|
}
|
||||||
@@ -650,6 +592,12 @@ static void copy_sacch_si_to_lchan(struct gsm_lchan *lchan)
|
|||||||
static int encr_info2lchan(struct gsm_lchan *lchan,
|
static int encr_info2lchan(struct gsm_lchan *lchan,
|
||||||
const uint8_t *val, uint8_t len)
|
const uint8_t *val, uint8_t len)
|
||||||
{
|
{
|
||||||
|
struct gsm_bts_role_bts *btsb = bts_role_bts(lchan->ts->trx->bts);
|
||||||
|
|
||||||
|
/* check if the encryption algorithm sent by BSC is supported! */
|
||||||
|
if (!((1 << *val) & btsb->support.ciphers))
|
||||||
|
return -ENOTSUP;
|
||||||
|
|
||||||
/* length can be '1' in case of no ciphering */
|
/* length can be '1' in case of no ciphering */
|
||||||
if (len < 1)
|
if (len < 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@@ -672,21 +620,26 @@ static int rsl_rx_chan_activ(struct msgb *msg)
|
|||||||
struct tlv_parsed tp;
|
struct tlv_parsed tp;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
|
|
||||||
|
if (lchan->state != LCHAN_S_NONE) {
|
||||||
|
LOGP(DRSL, LOGL_ERROR,
|
||||||
|
"%s: error lchan is not available state: %s.\n",
|
||||||
|
gsm_lchan_name(lchan), gsm_lchans_name(lchan->state));
|
||||||
|
return rsl_tx_chan_act_nack(lchan, RSL_ERR_EQUIPMENT_FAIL);
|
||||||
|
}
|
||||||
|
|
||||||
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
|
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
|
||||||
|
|
||||||
/* 9.3.3 Activation Type */
|
/* 9.3.3 Activation Type */
|
||||||
if (!TLVP_PRESENT(&tp, RSL_IE_ACT_TYPE)) {
|
if (!TLVP_PRESENT(&tp, RSL_IE_ACT_TYPE)) {
|
||||||
LOGP(DRSL, LOGL_NOTICE, "missing Activation Type\n");
|
LOGP(DRSL, LOGL_NOTICE, "missing Activation Type\n");
|
||||||
msgb_free(msg);
|
return rsl_tx_chan_act_nack(lchan, RSL_ERR_MAND_IE_ERROR);
|
||||||
return rsl_tx_chan_nack(msg->trx, msg, RSL_ERR_MAND_IE_ERROR);
|
|
||||||
}
|
}
|
||||||
type = *TLVP_VAL(&tp, RSL_IE_ACT_TYPE);
|
type = *TLVP_VAL(&tp, RSL_IE_ACT_TYPE);
|
||||||
|
|
||||||
/* 9.3.6 Channel Mode */
|
/* 9.3.6 Channel Mode */
|
||||||
if (!TLVP_PRESENT(&tp, RSL_IE_CHAN_MODE)) {
|
if (!TLVP_PRESENT(&tp, RSL_IE_CHAN_MODE)) {
|
||||||
LOGP(DRSL, LOGL_NOTICE, "missing Channel Mode\n");
|
LOGP(DRSL, LOGL_NOTICE, "missing Channel Mode\n");
|
||||||
msgb_free(msg);
|
return rsl_tx_chan_act_nack(lchan, RSL_ERR_MAND_IE_ERROR);
|
||||||
return rsl_tx_chan_nack(msg->trx, msg, RSL_ERR_MAND_IE_ERROR);
|
|
||||||
}
|
}
|
||||||
cm = (struct rsl_ie_chan_mode *) TLVP_VAL(&tp, RSL_IE_CHAN_MODE);
|
cm = (struct rsl_ie_chan_mode *) TLVP_VAL(&tp, RSL_IE_CHAN_MODE);
|
||||||
lchan_tchmode_from_cmode(lchan, cm);
|
lchan_tchmode_from_cmode(lchan, cm);
|
||||||
@@ -698,7 +651,8 @@ static int rsl_rx_chan_activ(struct msgb *msg)
|
|||||||
|
|
||||||
if (encr_info2lchan(lchan, val, len) < 0)
|
if (encr_info2lchan(lchan, val, len) < 0)
|
||||||
return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT);
|
return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT);
|
||||||
}
|
} else
|
||||||
|
memset(&lchan->encr, 0, sizeof(lchan->encr));
|
||||||
|
|
||||||
/* 9.3.9 Handover Reference */
|
/* 9.3.9 Handover Reference */
|
||||||
|
|
||||||
@@ -765,10 +719,10 @@ static int rsl_rx_chan_activ(struct msgb *msg)
|
|||||||
}
|
}
|
||||||
memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
|
memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
|
||||||
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
|
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
|
||||||
parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
|
amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
|
||||||
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
|
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
|
||||||
log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
|
amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
|
||||||
&lchan->tch.amr_mr);
|
&lchan->tch.amr_mr);
|
||||||
}
|
}
|
||||||
/* 9.3.53 MultiRate Control */
|
/* 9.3.53 MultiRate Control */
|
||||||
/* 9.3.54 Supported Codec Types */
|
/* 9.3.54 Supported Codec Types */
|
||||||
@@ -806,11 +760,15 @@ static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan)
|
|||||||
static int tx_ciph_mod_compl_hack(struct gsm_lchan *lchan, uint8_t link_id,
|
static int tx_ciph_mod_compl_hack(struct gsm_lchan *lchan, uint8_t link_id,
|
||||||
const char *imeisv)
|
const char *imeisv)
|
||||||
{
|
{
|
||||||
struct msgb *fake_msg = rsl_msgb_alloc(128);
|
struct msgb *fake_msg;
|
||||||
struct gsm48_hdr *g48h;
|
struct gsm48_hdr *g48h;
|
||||||
uint8_t mid_buf[11];
|
uint8_t mid_buf[11];
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
fake_msg = rsl_msgb_alloc(128);
|
||||||
|
if (!fake_msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
/* generate 04.08 RR message */
|
/* generate 04.08 RR message */
|
||||||
g48h = (struct gsm48_hdr *) msgb_put(fake_msg, sizeof(*g48h));
|
g48h = (struct gsm48_hdr *) msgb_put(fake_msg, sizeof(*g48h));
|
||||||
g48h->proto_discr = GSM48_PDISC_RR;
|
g48h->proto_discr = GSM48_PDISC_RR;
|
||||||
@@ -887,8 +845,6 @@ static int rsl_rx_encr_cmd(struct msgb *msg)
|
|||||||
return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT);
|
return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: check if the encryption algorithm sent by BSC is supported! */
|
|
||||||
|
|
||||||
/* 9.3.2 Link Identifier */
|
/* 9.3.2 Link Identifier */
|
||||||
link_id = *TLVP_VAL(&tp, RSL_IE_LINK_IDENT);
|
link_id = *TLVP_VAL(&tp, RSL_IE_LINK_IDENT);
|
||||||
|
|
||||||
@@ -935,12 +891,16 @@ static int rsl_rx_encr_cmd(struct msgb *msg)
|
|||||||
/* 8.4.11 MODE MODIFY NEGATIVE ACKNOWLEDGE */
|
/* 8.4.11 MODE MODIFY NEGATIVE ACKNOWLEDGE */
|
||||||
static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause)
|
static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause)
|
||||||
{
|
{
|
||||||
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
struct msgb *msg;
|
||||||
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
||||||
|
|
||||||
LOGP(DRSL, LOGL_NOTICE, "%s Tx MODE MODIFY NACK (cause = 0x%02x)\n",
|
LOGP(DRSL, LOGL_NOTICE, "%s Tx MODE MODIFY NACK (cause = 0x%02x)\n",
|
||||||
gsm_lchan_name(lchan), cause);
|
gsm_lchan_name(lchan), cause);
|
||||||
|
|
||||||
|
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
msg->len = 0;
|
msg->len = 0;
|
||||||
msg->data = msg->tail = msg->l3h;
|
msg->data = msg->tail = msg->l3h;
|
||||||
|
|
||||||
@@ -955,11 +915,15 @@ static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause)
|
|||||||
/* 8.4.10 MODE MODIFY ACK */
|
/* 8.4.10 MODE MODIFY ACK */
|
||||||
static int rsl_tx_mode_modif_ack(struct gsm_lchan *lchan)
|
static int rsl_tx_mode_modif_ack(struct gsm_lchan *lchan)
|
||||||
{
|
{
|
||||||
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
struct msgb *msg;
|
||||||
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
||||||
|
|
||||||
LOGP(DRSL, LOGL_INFO, "%s Tx MODE MODIF ACK\n", gsm_lchan_name(lchan));
|
LOGP(DRSL, LOGL_INFO, "%s Tx MODE MODIF ACK\n", gsm_lchan_name(lchan));
|
||||||
|
|
||||||
|
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
rsl_dch_push_hdr(msg, RSL_MT_MODE_MODIFY_ACK, chan_nr);
|
rsl_dch_push_hdr(msg, RSL_MT_MODE_MODIFY_ACK, chan_nr);
|
||||||
msg->trx = lchan->ts->trx;
|
msg->trx = lchan->ts->trx;
|
||||||
|
|
||||||
@@ -1004,10 +968,10 @@ static int rsl_rx_mode_modif(struct msgb *msg)
|
|||||||
}
|
}
|
||||||
memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
|
memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
|
||||||
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
|
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
|
||||||
parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
|
amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
|
||||||
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
|
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
|
||||||
log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
|
amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
|
||||||
&lchan->tch.amr_mr);
|
&lchan->tch.amr_mr);
|
||||||
}
|
}
|
||||||
/* 9.3.53 MultiRate Control */
|
/* 9.3.53 MultiRate Control */
|
||||||
/* 9.3.54 Supported Codec Types */
|
/* 9.3.54 Supported Codec Types */
|
||||||
@@ -1082,12 +1046,12 @@ int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause)
|
|||||||
LOGP(DRSL, LOGL_NOTICE, "%s Sending RTP delete indication: cause=%d\n",
|
LOGP(DRSL, LOGL_NOTICE, "%s Sending RTP delete indication: cause=%d\n",
|
||||||
gsm_lchan_name(lchan), cause);
|
gsm_lchan_name(lchan), cause);
|
||||||
|
|
||||||
nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr));
|
nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
||||||
if (!nmsg)
|
if (!nmsg)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
msgb_tlv_put(nmsg, RSL_IE_CAUSE, 1, &cause);
|
msgb_tlv_put(nmsg, RSL_IE_CAUSE, 1, &cause);
|
||||||
rsl_trx_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND);
|
rsl_ipa_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND, gsm_lchan2chan_nr(lchan));
|
||||||
|
|
||||||
nmsg->trx = lchan->ts->trx;
|
nmsg->trx = lchan->ts->trx;
|
||||||
|
|
||||||
@@ -1098,7 +1062,7 @@ int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause)
|
|||||||
static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
|
static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
|
||||||
uint8_t orig_msgt)
|
uint8_t orig_msgt)
|
||||||
{
|
{
|
||||||
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
struct msgb *msg;
|
||||||
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
||||||
uint32_t *att_ip;
|
uint32_t *att_ip;
|
||||||
const char *name;
|
const char *name;
|
||||||
@@ -1117,6 +1081,11 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
|
|||||||
LOGPC(DRSL, LOGL_INFO, "remote %s:%u)\n",
|
LOGPC(DRSL, LOGL_INFO, "remote %s:%u)\n",
|
||||||
inet_ntoa(ia), lchan->abis_ip.connect_port);
|
inet_ntoa(ia), lchan->abis_ip.connect_port);
|
||||||
|
|
||||||
|
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
|
||||||
/* Connection ID */
|
/* Connection ID */
|
||||||
msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, htons(lchan->abis_ip.conn_id));
|
msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, htons(lchan->abis_ip.conn_id));
|
||||||
|
|
||||||
@@ -1144,12 +1113,16 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
|
|||||||
|
|
||||||
static int rsl_tx_ipac_dlcx_ack(struct gsm_lchan *lchan, int inc_conn_id)
|
static int rsl_tx_ipac_dlcx_ack(struct gsm_lchan *lchan, int inc_conn_id)
|
||||||
{
|
{
|
||||||
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
struct msgb *msg;
|
||||||
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
||||||
|
|
||||||
LOGP(DRSL, LOGL_INFO, "%s RSL Tx IPAC_DLCX_ACK\n",
|
LOGP(DRSL, LOGL_INFO, "%s RSL Tx IPAC_DLCX_ACK\n",
|
||||||
gsm_lchan_name(lchan));
|
gsm_lchan_name(lchan));
|
||||||
|
|
||||||
|
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
if (inc_conn_id)
|
if (inc_conn_id)
|
||||||
msgb_tv_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
|
msgb_tv_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
|
||||||
|
|
||||||
@@ -1162,12 +1135,16 @@ static int rsl_tx_ipac_dlcx_ack(struct gsm_lchan *lchan, int inc_conn_id)
|
|||||||
static int rsl_tx_ipac_dlcx_nack(struct gsm_lchan *lchan, int inc_conn_id,
|
static int rsl_tx_ipac_dlcx_nack(struct gsm_lchan *lchan, int inc_conn_id,
|
||||||
uint8_t cause)
|
uint8_t cause)
|
||||||
{
|
{
|
||||||
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
struct msgb *msg;
|
||||||
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
||||||
|
|
||||||
LOGP(DRSL, LOGL_INFO, "%s RSL Tx IPAC_DLCX_NACK\n",
|
LOGP(DRSL, LOGL_INFO, "%s RSL Tx IPAC_DLCX_NACK\n",
|
||||||
gsm_lchan_name(lchan));
|
gsm_lchan_name(lchan));
|
||||||
|
|
||||||
|
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
if (inc_conn_id)
|
if (inc_conn_id)
|
||||||
msgb_tv_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
|
msgb_tv_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
|
||||||
|
|
||||||
@@ -1185,13 +1162,17 @@ static int rsl_tx_ipac_dlcx_nack(struct gsm_lchan *lchan, int inc_conn_id,
|
|||||||
static int tx_ipac_XXcx_nack(struct gsm_lchan *lchan, uint8_t cause,
|
static int tx_ipac_XXcx_nack(struct gsm_lchan *lchan, uint8_t cause,
|
||||||
int inc_ipport, uint8_t orig_msgtype)
|
int inc_ipport, uint8_t orig_msgtype)
|
||||||
{
|
{
|
||||||
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
struct msgb *msg;
|
||||||
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
||||||
|
|
||||||
/* FIXME: allocate new msgb and copy old over */
|
/* FIXME: allocate new msgb and copy old over */
|
||||||
LOGP(DRSL, LOGL_NOTICE, "%s RSL Tx IPAC_BIND_NACK\n",
|
LOGP(DRSL, LOGL_NOTICE, "%s RSL Tx IPAC_BIND_NACK\n",
|
||||||
gsm_lchan_name(lchan));
|
gsm_lchan_name(lchan));
|
||||||
|
|
||||||
|
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
if (inc_ipport) {
|
if (inc_ipport) {
|
||||||
uint32_t *att_ip;
|
uint32_t *att_ip;
|
||||||
/* remote IP */
|
/* remote IP */
|
||||||
@@ -1273,6 +1254,9 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
|
|||||||
return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
|
return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
|
||||||
inc_ip_port, dch->c.msg_type);
|
inc_ip_port, dch->c.msg_type);
|
||||||
}
|
}
|
||||||
|
osmo_rtp_socket_set_param(lchan->abis_ip.rtp_socket,
|
||||||
|
OSMO_RTP_P_JITBUF,
|
||||||
|
btsb->rtp_jitter_buf_ms);
|
||||||
lchan->abis_ip.rtp_socket->priv = lchan;
|
lchan->abis_ip.rtp_socket->priv = lchan;
|
||||||
lchan->abis_ip.rtp_socket->rx_cb = &bts_model_rtp_rx_cb;
|
lchan->abis_ip.rtp_socket->rx_cb = &bts_model_rtp_rx_cb;
|
||||||
|
|
||||||
@@ -1359,7 +1343,6 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
|
|||||||
|
|
||||||
static int rsl_rx_ipac_dlcx(struct msgb *msg)
|
static int rsl_rx_ipac_dlcx(struct msgb *msg)
|
||||||
{
|
{
|
||||||
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
|
|
||||||
struct tlv_parsed tp;
|
struct tlv_parsed tp;
|
||||||
struct gsm_lchan *lchan = msg->lchan;
|
struct gsm_lchan *lchan = msg->lchan;
|
||||||
int rc, inc_conn_id = 0;
|
int rc, inc_conn_id = 0;
|
||||||
@@ -1398,7 +1381,8 @@ static int rsl_rx_rll(struct gsm_bts_trx *trx, struct msgb *msg)
|
|||||||
if (!lchan) {
|
if (!lchan) {
|
||||||
LOGP(DRLL, LOGL_NOTICE, "Rx RLL %s for unknown lchan\n",
|
LOGP(DRLL, LOGL_NOTICE, "Rx RLL %s for unknown lchan\n",
|
||||||
rsl_msg_name(rh->c.msg_type));
|
rsl_msg_name(rh->c.msg_type));
|
||||||
return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR);
|
msgb_free(msg);
|
||||||
|
return report_error(trx);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUGP(DRLL, "%s Rx RLL %s Abis -> LAPDm\n", gsm_lchan_name(lchan),
|
DEBUGP(DRLL, "%s Rx RLL %s Abis -> LAPDm\n", gsm_lchan_name(lchan),
|
||||||
@@ -1454,11 +1438,15 @@ static int rslms_is_meas_rep(struct msgb *msg)
|
|||||||
/* 8.4.8 MEASUREMENT RESult */
|
/* 8.4.8 MEASUREMENT RESult */
|
||||||
static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len)
|
static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len)
|
||||||
{
|
{
|
||||||
struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
struct msgb *msg;
|
||||||
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
||||||
|
|
||||||
LOGP(DRSL, LOGL_NOTICE, "%s Tx MEAS RES\n", gsm_lchan_name(lchan));
|
LOGP(DRSL, LOGL_NOTICE, "%s Tx MEAS RES\n", gsm_lchan_name(lchan));
|
||||||
|
|
||||||
|
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
||||||
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr++);
|
msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr++);
|
||||||
if (lchan->meas.flags & LC_UL_M_F_RES_VALID) {
|
if (lchan->meas.flags & LC_UL_M_F_RES_VALID) {
|
||||||
uint8_t meas_res[16];
|
uint8_t meas_res[16];
|
||||||
@@ -1525,7 +1513,8 @@ static int rsl_rx_cchan(struct gsm_bts_trx *trx, struct msgb *msg)
|
|||||||
if (!msg->lchan) {
|
if (!msg->lchan) {
|
||||||
LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n",
|
LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n",
|
||||||
rsl_msg_name(cch->c.msg_type));
|
rsl_msg_name(cch->c.msg_type));
|
||||||
//return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR);
|
msgb_free(msg);
|
||||||
|
return report_error(trx);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan),
|
LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan),
|
||||||
@@ -1576,7 +1565,8 @@ static int rsl_rx_dchan(struct gsm_bts_trx *trx, struct msgb *msg)
|
|||||||
if (!msg->lchan) {
|
if (!msg->lchan) {
|
||||||
LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n",
|
LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n",
|
||||||
rsl_msg_name(dch->c.msg_type));
|
rsl_msg_name(dch->c.msg_type));
|
||||||
//return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR);
|
msgb_free(msg);
|
||||||
|
return report_error(trx);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan),
|
LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan),
|
||||||
@@ -1666,7 +1656,8 @@ static int rsl_rx_ipaccess(struct gsm_bts_trx *trx, struct msgb *msg)
|
|||||||
if (!msg->lchan) {
|
if (!msg->lchan) {
|
||||||
LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n",
|
LOGP(DRSL, LOGL_ERROR, "Rx RSL %s for unknow lchan\n",
|
||||||
rsl_msg_name(dch->c.msg_type));
|
rsl_msg_name(dch->c.msg_type));
|
||||||
//return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR);
|
msgb_free(msg);
|
||||||
|
return report_error(trx);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan),
|
LOGP(DRSL, LOGL_INFO, "%s Rx RSL %s\n", gsm_lchan_name(msg->lchan),
|
||||||
|
|||||||
@@ -24,23 +24,103 @@
|
|||||||
|
|
||||||
#include <osmo-bts/gsm_data.h>
|
#include <osmo-bts/gsm_data.h>
|
||||||
|
|
||||||
|
#define BTS_HAS_SI(bts, sinum) ((bts)->si_valid & (1 << sinum))
|
||||||
|
|
||||||
|
/* Apply the rules from 05.02 6.3.1.3 Mapping of BCCH Data */
|
||||||
uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time)
|
uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time)
|
||||||
{
|
{
|
||||||
/* Apply the rules from 05.02 6.3.1.3 Mapping of BCCH Data */
|
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||||
|
unsigned int tc4_cnt = 0;
|
||||||
|
unsigned int tc4_sub[4];
|
||||||
|
|
||||||
|
/* System information type 2 bis or 2 ter messages are sent if
|
||||||
|
* needed, as determined by the system operator. If only one of
|
||||||
|
* them is needed, it is sent when TC = 5. If both are needed,
|
||||||
|
* 2bis is sent when TC = 5 and 2ter is sent at least once
|
||||||
|
* within any of 4 consecutive occurrences of TC = 4. */
|
||||||
|
/* System information type 2 quater is sent if needed, as
|
||||||
|
* determined by the system operator. If sent on BCCH Norm, it
|
||||||
|
* shall be sent when TC = 5 if neither of 2bis and 2ter are
|
||||||
|
* used, otherwise it shall be sent at least once within any of
|
||||||
|
* 4 consecutive occurrences of TC = 4. If sent on BCCH Ext, it
|
||||||
|
* is sent at least once within any of 4 consecutive occurrences
|
||||||
|
* of TC = 5. */
|
||||||
|
/* System Information type 9 is sent in those blocks with
|
||||||
|
* TC = 4 which are specified in system information type 3 as
|
||||||
|
* defined in 3GPP TS 04.08. */
|
||||||
|
/* System Information Type 13 need only be sent if GPRS support
|
||||||
|
* is indicated in one or more of System Information Type 3 or 4
|
||||||
|
* or 7 or 8 messages. These messages also indicate if the
|
||||||
|
* message is sent on the BCCH Norm or if the message is
|
||||||
|
* transmitted on the BCCH Ext. In the case that the message is
|
||||||
|
* sent on the BCCH Norm, it is sent at least once within any of
|
||||||
|
* 4 consecutive occurrences of TC = 4. */
|
||||||
|
|
||||||
|
/* We only implement BCCH Norm at this time */
|
||||||
switch (g_time->tc) {
|
switch (g_time->tc) {
|
||||||
case 0:
|
case 0:
|
||||||
|
/* System Information Type 1 need only be sent if
|
||||||
|
* frequency hopping is in use or when the NCH is
|
||||||
|
* present in a cell. If the MS finds another message
|
||||||
|
* when TC = 0, it can assume that System Information
|
||||||
|
* Type 1 is not in use. */
|
||||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_1);
|
return GSM_BTS_SI(bts, SYSINFO_TYPE_1);
|
||||||
case 1:
|
case 1:
|
||||||
|
/* A SI 2 message will be sent at least every time TC = 1. */
|
||||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_2);
|
return GSM_BTS_SI(bts, SYSINFO_TYPE_2);
|
||||||
case 2:
|
case 2:
|
||||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_3);
|
return GSM_BTS_SI(bts, SYSINFO_TYPE_3);
|
||||||
case 3:
|
case 3:
|
||||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_4);
|
return GSM_BTS_SI(bts, SYSINFO_TYPE_4);
|
||||||
case 4:
|
case 4:
|
||||||
/* 2ter, 2quater, 9, 13 */
|
/* iterate over 2ter, 2quater, 9, 13 */
|
||||||
break;
|
/* determine how many SI we need to send on TC=4,
|
||||||
|
* and which of them we send when */
|
||||||
|
if (BTS_HAS_SI(bts, SYSINFO_TYPE_2ter)) {
|
||||||
|
tc4_sub[tc4_cnt] = SYSINFO_TYPE_2ter;
|
||||||
|
tc4_cnt += 1; /* 2bis */
|
||||||
|
}
|
||||||
|
if (BTS_HAS_SI(bts, SYSINFO_TYPE_2quater) &&
|
||||||
|
(BTS_HAS_SI(bts, SYSINFO_TYPE_2bis) ||
|
||||||
|
BTS_HAS_SI(bts, SYSINFO_TYPE_2bis))) {
|
||||||
|
tc4_sub[tc4_cnt] = SYSINFO_TYPE_2quater;
|
||||||
|
tc4_cnt += 1;
|
||||||
|
}
|
||||||
|
if (BTS_HAS_SI(bts, SYSINFO_TYPE_13)) {
|
||||||
|
tc4_sub[tc4_cnt] = SYSINFO_TYPE_13;
|
||||||
|
tc4_cnt += 1;
|
||||||
|
}
|
||||||
|
if (BTS_HAS_SI(bts, SYSINFO_TYPE_9)) {
|
||||||
|
/* FIXME: check SI3 scheduling info! */
|
||||||
|
tc4_sub[tc4_cnt] = SYSINFO_TYPE_9;
|
||||||
|
tc4_cnt += 1;
|
||||||
|
}
|
||||||
|
/* simply send SI2 if we have nothing else to send */
|
||||||
|
if (tc4_cnt == 0)
|
||||||
|
return GSM_BTS_SI(bts, SYSINFO_TYPE_2);
|
||||||
|
else {
|
||||||
|
/* increment static counter by one, modulo count */
|
||||||
|
btsb->si.tc4_ctr = (btsb->si.tc4_ctr + 1) % tc4_cnt;
|
||||||
|
return GSM_BTS_SI(bts, tc4_sub[btsb->si.tc4_ctr]);
|
||||||
|
}
|
||||||
case 5:
|
case 5:
|
||||||
/* 2ter, 2quater */
|
/* 2bis, 2ter, 2quater */
|
||||||
|
if (BTS_HAS_SI(bts, SYSINFO_TYPE_2bis) &&
|
||||||
|
!BTS_HAS_SI(bts, SYSINFO_TYPE_2ter))
|
||||||
|
return GSM_BTS_SI(bts, SYSINFO_TYPE_2bis);
|
||||||
|
|
||||||
|
else if (BTS_HAS_SI(bts, SYSINFO_TYPE_2ter) &&
|
||||||
|
!BTS_HAS_SI(bts, SYSINFO_TYPE_2bis))
|
||||||
|
return GSM_BTS_SI(bts, SYSINFO_TYPE_2ter);
|
||||||
|
|
||||||
|
else if (BTS_HAS_SI(bts, SYSINFO_TYPE_2bis) &&
|
||||||
|
BTS_HAS_SI(bts, SYSINFO_TYPE_2ter))
|
||||||
|
return GSM_BTS_SI(bts, SYSINFO_TYPE_2bis);
|
||||||
|
|
||||||
|
else if (BTS_HAS_SI(bts, SYSINFO_TYPE_2quater) &&
|
||||||
|
!BTS_HAS_SI(bts, SYSINFO_TYPE_2bis) &&
|
||||||
|
!BTS_HAS_SI(bts, SYSINFO_TYPE_2ter))
|
||||||
|
return GSM_BTS_SI(bts, SYSINFO_TYPE_2quater);
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_3);
|
return GSM_BTS_SI(bts, SYSINFO_TYPE_3);
|
||||||
|
|||||||
184
src/common/vty.c
184
src/common/vty.c
@@ -29,6 +29,8 @@
|
|||||||
#include <osmocom/vty/command.h>
|
#include <osmocom/vty/command.h>
|
||||||
#include <osmocom/vty/logging.h>
|
#include <osmocom/vty/logging.h>
|
||||||
|
|
||||||
|
#include <osmocom/trau/osmo_ortp.h>
|
||||||
|
|
||||||
|
|
||||||
#include <osmo-bts/logging.h>
|
#include <osmo-bts/logging.h>
|
||||||
#include <osmo-bts/gsm_data.h>
|
#include <osmo-bts/gsm_data.h>
|
||||||
@@ -45,6 +47,13 @@
|
|||||||
enum node_type bts_vty_go_parent(struct vty *vty)
|
enum node_type bts_vty_go_parent(struct vty *vty)
|
||||||
{
|
{
|
||||||
switch (vty->node) {
|
switch (vty->node) {
|
||||||
|
case TRX_NODE:
|
||||||
|
vty->node = BTS_NODE;
|
||||||
|
{
|
||||||
|
struct gsm_bts_trx *trx = vty->index;
|
||||||
|
vty->index = trx->bts;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
vty->node = CONFIG_NODE;
|
vty->node = CONFIG_NODE;
|
||||||
}
|
}
|
||||||
@@ -54,6 +63,7 @@ enum node_type bts_vty_go_parent(struct vty *vty)
|
|||||||
int bts_vty_is_config_node(struct vty *vty, int node)
|
int bts_vty_is_config_node(struct vty *vty, int node)
|
||||||
{
|
{
|
||||||
switch (node) {
|
switch (node) {
|
||||||
|
case TRX_NODE:
|
||||||
case BTS_NODE:
|
case BTS_NODE:
|
||||||
return 1;
|
return 1;
|
||||||
default:
|
default:
|
||||||
@@ -65,6 +75,13 @@ gDEFUN(ournode_exit, ournode_exit_cmd, "exit",
|
|||||||
"Exit current node, go down to provious node")
|
"Exit current node, go down to provious node")
|
||||||
{
|
{
|
||||||
switch (vty->node) {
|
switch (vty->node) {
|
||||||
|
case TRX_NODE:
|
||||||
|
vty->node = BTS_NODE;
|
||||||
|
{
|
||||||
|
struct gsm_bts_trx *trx = vty->index;
|
||||||
|
vty->index = trx->bts;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -85,19 +102,20 @@ gDEFUN(ournode_end, ournode_end_cmd, "end",
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct vty_app_info bts_vty_info = {
|
static const char osmobts_copyright[] =
|
||||||
.name = "OsmoBTS",
|
|
||||||
.version = PACKAGE_VERSION,
|
|
||||||
.go_parent_cb = bts_vty_go_parent,
|
|
||||||
.is_config_node = bts_vty_is_config_node,
|
|
||||||
};
|
|
||||||
|
|
||||||
const char *osmobts_copyright =
|
|
||||||
"Copyright (C) 2010, 2011 by Harald Welte, Andreas Eversberg and On-Waves\r\n"
|
"Copyright (C) 2010, 2011 by Harald Welte, Andreas Eversberg and On-Waves\r\n"
|
||||||
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
|
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
|
||||||
"This is free software: you are free to change and redistribute it.\r\n"
|
"This is free software: you are free to change and redistribute it.\r\n"
|
||||||
"There is NO WARRANTY, to the extent permitted by law.\r\n";
|
"There is NO WARRANTY, to the extent permitted by law.\r\n";
|
||||||
|
|
||||||
|
struct vty_app_info bts_vty_info = {
|
||||||
|
.name = "OsmoBTS",
|
||||||
|
.version = PACKAGE_VERSION,
|
||||||
|
.copyright = osmobts_copyright,
|
||||||
|
.go_parent_cb = bts_vty_go_parent,
|
||||||
|
.is_config_node = bts_vty_is_config_node,
|
||||||
|
};
|
||||||
|
|
||||||
extern struct gsm_network bts_gsmnet;
|
extern struct gsm_network bts_gsmnet;
|
||||||
|
|
||||||
struct gsm_network *gsmnet_from_vty(struct vty *v)
|
struct gsm_network *gsmnet_from_vty(struct vty *v)
|
||||||
@@ -126,9 +144,37 @@ static struct cmd_node bts_node = {
|
|||||||
1,
|
1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct cmd_node trx_node = {
|
||||||
|
TRX_NODE,
|
||||||
|
"%s(trx)#",
|
||||||
|
1,
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFUN(cfg_bts_trx, cfg_bts_trx_cmd,
|
||||||
|
"trx <0-0>",
|
||||||
|
"Select a TRX to configure\n" "TRX number\n")
|
||||||
|
{
|
||||||
|
int trx_nr = atoi(argv[0]);
|
||||||
|
struct gsm_bts *bts = vty->index;
|
||||||
|
struct gsm_bts_trx *trx;
|
||||||
|
|
||||||
|
trx = gsm_bts_trx_num(bts, trx_nr);
|
||||||
|
if (!trx) {
|
||||||
|
vty_out(vty, "Unknown TRX %u%s", trx_nr, VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
vty->index = trx;
|
||||||
|
vty->index_sub = &trx->description;
|
||||||
|
vty->node = TRX_NODE;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
|
static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
|
||||||
{
|
{
|
||||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||||
|
struct gsm_bts_trx *trx;
|
||||||
|
|
||||||
vty_out(vty, "bts %u%s", bts->nr, VTY_NEWLINE);
|
vty_out(vty, "bts %u%s", bts->nr, VTY_NEWLINE);
|
||||||
if (bts->description)
|
if (bts->description)
|
||||||
@@ -138,9 +184,18 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
|
|||||||
bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE);
|
bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE);
|
||||||
vty_out(vty, " oml remote-ip %s%s", btsb->bsc_oml_host, VTY_NEWLINE);
|
vty_out(vty, " oml remote-ip %s%s", btsb->bsc_oml_host, VTY_NEWLINE);
|
||||||
vty_out(vty, " rtp bind-ip %s%s", btsb->rtp_bind_host, VTY_NEWLINE);
|
vty_out(vty, " rtp bind-ip %s%s", btsb->rtp_bind_host, VTY_NEWLINE);
|
||||||
|
vty_out(vty, " rtp jitter-buffer %u%s", btsb->rtp_jitter_buf_ms,
|
||||||
|
VTY_NEWLINE);
|
||||||
|
|
||||||
|
bts_model_config_write_bts(vty, bts);
|
||||||
|
|
||||||
|
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||||
|
vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE);
|
||||||
|
bts_model_config_write_trx(vty, trx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int config_write_bts(struct vty *vty)
|
static int config_write_bts(struct vty *vty)
|
||||||
{
|
{
|
||||||
struct gsm_network *net = gsmnet_from_vty(vty);
|
struct gsm_network *net = gsmnet_from_vty(vty);
|
||||||
struct gsm_bts *bts;
|
struct gsm_bts *bts;
|
||||||
@@ -151,6 +206,11 @@ int config_write_bts(struct vty *vty)
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int config_write_dummy(struct vty *vty)
|
||||||
|
{
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/* per-BTS configuration */
|
/* per-BTS configuration */
|
||||||
DEFUN(cfg_bts,
|
DEFUN(cfg_bts,
|
||||||
cfg_bts_cmd,
|
cfg_bts_cmd,
|
||||||
@@ -180,7 +240,9 @@ DEFUN(cfg_bts,
|
|||||||
DEFUN(cfg_bts_unit_id,
|
DEFUN(cfg_bts_unit_id,
|
||||||
cfg_bts_unit_id_cmd,
|
cfg_bts_unit_id_cmd,
|
||||||
"ipa unit-id <0-65534> <0-255>",
|
"ipa unit-id <0-65534> <0-255>",
|
||||||
"Set the ip.access BTS Unit ID of this BTS\n")
|
"ip.access RSL commands\n"
|
||||||
|
"Set the Unit ID of this BTS\n"
|
||||||
|
"Site ID\n" "Unit ID\n")
|
||||||
{
|
{
|
||||||
struct gsm_bts *bts = vty->index;
|
struct gsm_bts *bts = vty->index;
|
||||||
int site_id = atoi(argv[0]);
|
int site_id = atoi(argv[0]);
|
||||||
@@ -195,7 +257,15 @@ DEFUN(cfg_bts_unit_id,
|
|||||||
DEFUN(cfg_bts_band,
|
DEFUN(cfg_bts_band,
|
||||||
cfg_bts_band_cmd,
|
cfg_bts_band_cmd,
|
||||||
"band (450|GSM450|480|GSM480|750|GSM750|810|GSM810|850|GSM850|900|GSM900|1800|DCS1800|1900|PCS1900)",
|
"band (450|GSM450|480|GSM480|750|GSM750|810|GSM810|850|GSM850|900|GSM900|1800|DCS1800|1900|PCS1900)",
|
||||||
"Set the frequency band of this BTS\n" "Frequency band\n")
|
"Set the frequency band of this BTS\n"
|
||||||
|
"Alias for GSM450\n450Mhz\n"
|
||||||
|
"Alias for GSM480\n480Mhz\n"
|
||||||
|
"Alias for GSM750\n750Mhz\n"
|
||||||
|
"Alias for GSM810\n810Mhz\n"
|
||||||
|
"Alias for GSM850\n850Mhz\n"
|
||||||
|
"Alias for GSM900\n900Mhz\n"
|
||||||
|
"Alias for DCS1800\n1800Mhz\n"
|
||||||
|
"Alias for PCS1900\n1900Mhz\n")
|
||||||
{
|
{
|
||||||
struct gsm_bts *bts = vty->index;
|
struct gsm_bts *bts = vty->index;
|
||||||
int band = gsm_band_parse(argv[0]);
|
int band = gsm_band_parse(argv[0]);
|
||||||
@@ -227,10 +297,12 @@ DEFUN(cfg_bts_oml_ip,
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define RTP_STR "RTP parameters\n"
|
||||||
|
|
||||||
DEFUN(cfg_bts_rtp_bind_ip,
|
DEFUN(cfg_bts_rtp_bind_ip,
|
||||||
cfg_bts_rtp_bind_ip_cmd,
|
cfg_bts_rtp_bind_ip_cmd,
|
||||||
"rtp bind-ip A.B.C.D",
|
"rtp bind-ip A.B.C.D",
|
||||||
"RTP Parameters\n" "RTP local bind IP Address\n" "RTP local bind IP Address\n")
|
RTP_STR "RTP local bind IP Address\n" "RTP local bind IP Address\n")
|
||||||
{
|
{
|
||||||
struct gsm_bts *bts = vty->index;
|
struct gsm_bts *bts = vty->index;
|
||||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||||
@@ -242,6 +314,20 @@ DEFUN(cfg_bts_rtp_bind_ip,
|
|||||||
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_bts_rtp_jitbuf,
|
||||||
|
cfg_bts_rtp_jitbuf_cmd,
|
||||||
|
"rtp jitter-buffer <0-10000>",
|
||||||
|
RTP_STR "RTP jitter buffer\n" "jitter buffer in ms\n")
|
||||||
|
{
|
||||||
|
struct gsm_bts *bts = vty->index;
|
||||||
|
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||||
|
|
||||||
|
btsb->rtp_jitter_buf_ms = atoi(argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/* ======================================================================
|
/* ======================================================================
|
||||||
* SHOW
|
* SHOW
|
||||||
* ======================================================================*/
|
* ======================================================================*/
|
||||||
@@ -316,6 +402,70 @@ DEFUN(show_bts, show_bts_cmd, "show bts <0-255>",
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct gsm_lchan *resolve_lchan(struct gsm_network *net,
|
||||||
|
const char **argv, int idx)
|
||||||
|
{
|
||||||
|
int bts_nr = atoi(argv[idx+0]);
|
||||||
|
int trx_nr = atoi(argv[idx+1]);
|
||||||
|
int ts_nr = atoi(argv[idx+2]);
|
||||||
|
int lchan_nr = atoi(argv[idx+3]);
|
||||||
|
struct gsm_bts *bts;
|
||||||
|
struct gsm_bts_trx *trx;
|
||||||
|
struct gsm_bts_trx_ts *ts;
|
||||||
|
|
||||||
|
bts = gsm_bts_num(net, bts_nr);
|
||||||
|
if (!bts)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
trx = gsm_bts_trx_num(bts, trx_nr);
|
||||||
|
if (!trx)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (ts_nr >= ARRAY_SIZE(trx->ts))
|
||||||
|
return NULL;
|
||||||
|
ts = &trx->ts[ts_nr];
|
||||||
|
|
||||||
|
if (lchan_nr >= ARRAY_SIZE(ts->lchan))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return &ts->lchan[lchan_nr];
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BTS_T_T_L_STR \
|
||||||
|
"BTS related commands\n" \
|
||||||
|
"BTS number\n" \
|
||||||
|
"TRX related commands\n" \
|
||||||
|
"TRX number\n" \
|
||||||
|
"timeslot related commands\n" \
|
||||||
|
"timeslot number\n" \
|
||||||
|
"logical channel commands\n" \
|
||||||
|
"logical channel number\n"
|
||||||
|
|
||||||
|
DEFUN(bts_t_t_l_jitter_buf,
|
||||||
|
bts_t_t_l_jitter_buf_cmd,
|
||||||
|
"bts <0-0> trx <0-0> ts <0-7> lchan <0-1> rtp jitter-buffer <0-10000>",
|
||||||
|
BTS_T_T_L_STR "RTP settings\n"
|
||||||
|
"Jitter buffer\n" "Size of jitter buffer in (ms)\n")
|
||||||
|
{
|
||||||
|
struct gsm_network *net = gsmnet_from_vty(vty);
|
||||||
|
struct gsm_lchan *lchan;
|
||||||
|
int jitbuf_ms = atoi(argv[4]);
|
||||||
|
|
||||||
|
lchan = resolve_lchan(net, argv, 0);
|
||||||
|
if (!lchan) {
|
||||||
|
vty_out(vty, "%% can't find BTS%s", VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
if (!lchan->abis_ip.rtp_socket) {
|
||||||
|
vty_out(vty, "%% this channel has no active RTP stream%s",
|
||||||
|
VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
osmo_rtp_socket_set_param(lchan->abis_ip.rtp_socket,
|
||||||
|
OSMO_RTP_P_JITBUF, jitbuf_ms);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
int bts_vty_init(const struct log_info *cat)
|
int bts_vty_init(const struct log_info *cat)
|
||||||
{
|
{
|
||||||
@@ -329,7 +479,17 @@ int bts_vty_init(const struct log_info *cat)
|
|||||||
install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
|
install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
|
||||||
install_element(BTS_NODE, &cfg_bts_oml_ip_cmd);
|
install_element(BTS_NODE, &cfg_bts_oml_ip_cmd);
|
||||||
install_element(BTS_NODE, &cfg_bts_rtp_bind_ip_cmd);
|
install_element(BTS_NODE, &cfg_bts_rtp_bind_ip_cmd);
|
||||||
|
install_element(BTS_NODE, &cfg_bts_rtp_jitbuf_cmd);
|
||||||
install_element(BTS_NODE, &cfg_bts_band_cmd);
|
install_element(BTS_NODE, &cfg_bts_band_cmd);
|
||||||
install_element(BTS_NODE, &cfg_description_cmd);
|
install_element(BTS_NODE, &cfg_description_cmd);
|
||||||
install_element(BTS_NODE, &cfg_no_description_cmd);
|
install_element(BTS_NODE, &cfg_no_description_cmd);
|
||||||
|
|
||||||
|
/* add and link to TRX config node */
|
||||||
|
install_element(BTS_NODE, &cfg_bts_trx_cmd);
|
||||||
|
install_node(&trx_node, config_write_dummy);
|
||||||
|
install_default(TRX_NODE);
|
||||||
|
|
||||||
|
install_element(ENABLE_NODE, &bts_t_t_l_jitter_buf_cmd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
|||||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
|
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
|
||||||
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) -lortp
|
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) -lortp
|
||||||
|
|
||||||
bin_PROGRAMS = sysmobts sysmobts-remote l1fwd-proxy
|
EXTRA_DIST = misc/sysmobts_mgr.h misc/sysmobts_misc.h misc/sysmobts_par.h misc/sysmobts_eeprom.h
|
||||||
|
|
||||||
COMMON_SOURCES = main.c femtobts.c l1_if.c oml.c bts_model.c sysmobts_vty.c tch.c
|
bin_PROGRAMS = sysmobts sysmobts-remote l1fwd-proxy sysmobts-mgr
|
||||||
|
|
||||||
|
COMMON_SOURCES = main.c femtobts.c l1_if.c oml.c sysmobts_vty.c tch.c hw_misc.c calib_file.c
|
||||||
|
|
||||||
sysmobts_SOURCES = $(COMMON_SOURCES) l1_transp_hw.c
|
sysmobts_SOURCES = $(COMMON_SOURCES) l1_transp_hw.c
|
||||||
sysmobts_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
|
sysmobts_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
|
||||||
@@ -14,3 +16,5 @@ sysmobts_remote_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
|
|||||||
|
|
||||||
l1fwd_proxy_SOURCES = l1_fwd_main.c l1_transp_hw.c
|
l1fwd_proxy_SOURCES = l1_fwd_main.c l1_transp_hw.c
|
||||||
l1fwd_proxy_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
|
l1fwd_proxy_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
|
||||||
|
|
||||||
|
sysmobts_mgr_SOURCES = misc/sysmobts_mgr.c misc/sysmobts_misc.c misc/sysmobts_par.c
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
274
src/osmo-bts-sysmo/calib_file.c
Normal file
274
src/osmo-bts-sysmo/calib_file.c
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
/* sysmocom femtobts L1 calibration file routines*/
|
||||||
|
|
||||||
|
/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
|
||||||
|
#include <osmo-bts/logging.h>
|
||||||
|
|
||||||
|
#include <sysmocom/femtobts/superfemto.h>
|
||||||
|
#include <sysmocom/femtobts/gsml1const.h>
|
||||||
|
|
||||||
|
#include "l1_if.h"
|
||||||
|
|
||||||
|
struct calib_file_desc {
|
||||||
|
const char *fname;
|
||||||
|
GsmL1_FreqBand_t band;
|
||||||
|
int uplink;
|
||||||
|
int rx;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct calib_file_desc calib_files[] = {
|
||||||
|
{
|
||||||
|
.fname = "calib_rxu_850.cfg",
|
||||||
|
.band = GsmL1_FreqBand_850,
|
||||||
|
.uplink = 1,
|
||||||
|
.rx = 1,
|
||||||
|
}, {
|
||||||
|
.fname = "calib_rxu_900.cfg",
|
||||||
|
.band = GsmL1_FreqBand_900,
|
||||||
|
.uplink = 1,
|
||||||
|
.rx = 1,
|
||||||
|
}, {
|
||||||
|
.fname = "calib_rxu_1800.cfg",
|
||||||
|
.band = GsmL1_FreqBand_1800,
|
||||||
|
.uplink = 1,
|
||||||
|
.rx = 1,
|
||||||
|
}, {
|
||||||
|
.fname = "calib_rxu_1900.cfg",
|
||||||
|
.band = GsmL1_FreqBand_1900,
|
||||||
|
.uplink = 1,
|
||||||
|
.rx = 1,
|
||||||
|
}, {
|
||||||
|
.fname = "calib_rxd_850.cfg",
|
||||||
|
.band = GsmL1_FreqBand_850,
|
||||||
|
.uplink = 0,
|
||||||
|
.rx = 1,
|
||||||
|
}, {
|
||||||
|
.fname = "calib_rxd_900.cfg",
|
||||||
|
.band = GsmL1_FreqBand_900,
|
||||||
|
.uplink = 0,
|
||||||
|
.rx = 1,
|
||||||
|
}, {
|
||||||
|
.fname = "calib_rxd_1800.cfg",
|
||||||
|
.band = GsmL1_FreqBand_1800,
|
||||||
|
.uplink = 0,
|
||||||
|
.rx = 1,
|
||||||
|
}, {
|
||||||
|
.fname = "calib_rxd_1900.cfg",
|
||||||
|
.band = GsmL1_FreqBand_1900,
|
||||||
|
.uplink = 0,
|
||||||
|
.rx = 1,
|
||||||
|
}, {
|
||||||
|
.fname = "calib_tx_850.cfg",
|
||||||
|
.band = GsmL1_FreqBand_850,
|
||||||
|
.uplink = 0,
|
||||||
|
.rx = 0,
|
||||||
|
}, {
|
||||||
|
.fname = "calib_tx_900.cfg",
|
||||||
|
.band = GsmL1_FreqBand_900,
|
||||||
|
.uplink = 0,
|
||||||
|
.rx = 0,
|
||||||
|
}, {
|
||||||
|
.fname = "calib_tx_1800.cfg",
|
||||||
|
.band = GsmL1_FreqBand_1800,
|
||||||
|
.uplink = 0,
|
||||||
|
.rx = 0,
|
||||||
|
}, {
|
||||||
|
.fname = "calib_tx_1900.cfg",
|
||||||
|
.band = GsmL1_FreqBand_1900,
|
||||||
|
.uplink = 0,
|
||||||
|
.rx = 0,
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned int arrsize_by_band[] = {
|
||||||
|
[GsmL1_FreqBand_850] = 124,
|
||||||
|
[GsmL1_FreqBand_900] = 194,
|
||||||
|
[GsmL1_FreqBand_1800] = 374,
|
||||||
|
[GsmL1_FreqBand_1900] = 299
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static float read_float(FILE *in)
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
fscanf(in, "%f\n", &f);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_int(FILE *in)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
fscanf(in, "%d\n", &i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int calib_file_read(const char *path, const struct calib_file_desc *desc,
|
||||||
|
SuperFemto_Prim_t *prim)
|
||||||
|
{
|
||||||
|
FILE *in;
|
||||||
|
char fname[PATH_MAX];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fname[0] = '\0';
|
||||||
|
snprintf(fname, sizeof(fname)-1, "%s/%s", path, desc->fname);
|
||||||
|
fname[sizeof(fname)-1] = '\0';
|
||||||
|
|
||||||
|
in = fopen(fname, "r");
|
||||||
|
if (!in)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,4,0)
|
||||||
|
if (desc->rx) {
|
||||||
|
SuperFemto_SetRxCalibTblReq_t *rx = &prim->u.setRxCalibTblReq;
|
||||||
|
memset(rx, 0, sizeof(*rx));
|
||||||
|
|
||||||
|
prim->id = SuperFemto_PrimId_SetRxCalibTblReq;
|
||||||
|
|
||||||
|
rx->freqBand = desc->band;
|
||||||
|
rx->bUplink = desc->uplink;
|
||||||
|
|
||||||
|
rx->fExtRxGain = read_float(in);
|
||||||
|
rx->fRxMixGainCorr = read_float(in);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(rx->fRxLnaGainCorr); i++)
|
||||||
|
rx->fRxLnaGainCorr[i] = read_float(in);
|
||||||
|
|
||||||
|
for (i = 0; i < arrsize_by_band[desc->band]; i++)
|
||||||
|
rx->fRxRollOffCorr[i] = read_float(in);
|
||||||
|
|
||||||
|
if (desc->uplink) {
|
||||||
|
rx->u8IqImbalMode = read_int(in);
|
||||||
|
printf("%s: u8IqImbalMode=%d\n", desc->fname, rx->u8IqImbalMode);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(rx->u16IqImbalCorr); i++)
|
||||||
|
rx->u16IqImbalCorr[i] = read_int(in);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SuperFemto_SetTxCalibTblReq_t *tx = &prim->u.setTxCalibTblReq;
|
||||||
|
memset(tx, 0, sizeof(*tx));
|
||||||
|
|
||||||
|
prim->id = SuperFemto_PrimId_SetTxCalibTblReq;
|
||||||
|
|
||||||
|
tx->freqBand = desc->band;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(tx->fTxGainGmsk); i++)
|
||||||
|
tx->fTxGainGmsk[i] = read_float(in);
|
||||||
|
|
||||||
|
tx->fTx8PskCorr = read_float(in);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(tx->fTxExtAttCorr); i++)
|
||||||
|
tx->fTxExtAttCorr[i] = read_float(in);
|
||||||
|
|
||||||
|
for (i = 0; i < arrsize_by_band[desc->band]; i++)
|
||||||
|
tx->fTxRollOffCorr[i] = read_float(in);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#warning Format of calibration tables before API version 2.4.0 not supported
|
||||||
|
#endif
|
||||||
|
fclose(in);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iteratively download the calibration data into the L1 */
|
||||||
|
|
||||||
|
struct calib_send_state {
|
||||||
|
struct femtol1_hdl *fl1h;
|
||||||
|
const char *path;
|
||||||
|
int last_file_idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int calib_send_compl_cb(struct msgb *l1_msg, void *data);
|
||||||
|
|
||||||
|
/* send the calibration table for a single specified file */
|
||||||
|
static int calib_file_send(struct femtol1_hdl *fl1h,
|
||||||
|
const struct calib_file_desc *desc, void *state)
|
||||||
|
{
|
||||||
|
struct msgb *msg;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
msg = sysp_msgb_alloc();
|
||||||
|
|
||||||
|
rc = calib_file_read(fl1h->calib_path, desc, msgb_sysprim(msg));
|
||||||
|
if (rc < 0) {
|
||||||
|
msgb_free(msg);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return l1if_req_compl(fl1h, msg, 1, calib_send_compl_cb, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* completion callback after every SetCalibTbl is confirmed */
|
||||||
|
static int calib_send_compl_cb(struct msgb *l1_msg, void *data)
|
||||||
|
{
|
||||||
|
struct calib_send_state *st = data;
|
||||||
|
|
||||||
|
LOGP(DL1C, LOGL_DEBUG, "L1 calibration table %s loaded\n",
|
||||||
|
calib_files[st->last_file_idx].fname);
|
||||||
|
|
||||||
|
st->last_file_idx++;
|
||||||
|
|
||||||
|
if (st->last_file_idx < ARRAY_SIZE(calib_files))
|
||||||
|
return calib_file_send(st->fl1h,
|
||||||
|
&calib_files[st->last_file_idx], st);
|
||||||
|
|
||||||
|
LOGP(DL1C, LOGL_INFO, "L1 calibration table loading complete!\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int calib_load(struct femtol1_hdl *fl1h)
|
||||||
|
{
|
||||||
|
static struct calib_send_state st;
|
||||||
|
|
||||||
|
memset(&st, 0, sizeof(st));
|
||||||
|
st.fl1h = fl1h;
|
||||||
|
|
||||||
|
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
return calib_file_send(fl1h, &calib_files[0], &st);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
SuperFemto_Prim_t p;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(calib_files); i++) {
|
||||||
|
memset(&p, 0, sizeof(p));
|
||||||
|
calib_read_file(argv[1], &calib_files[i], &p);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sysmocom/femtobts/femtobts.h>
|
#include <sysmocom/femtobts/superfemto.h>
|
||||||
#include <sysmocom/femtobts/gsml1const.h>
|
#include <sysmocom/femtobts/gsml1const.h>
|
||||||
#include <sysmocom/femtobts/gsml1dbg.h>
|
#include <sysmocom/femtobts/gsml1dbg.h>
|
||||||
|
|
||||||
@@ -91,48 +91,64 @@ const GsmL1_PrimId_t femtobts_l1prim_req2conf[GsmL1_PrimId_NUM] = {
|
|||||||
[GsmL1_PrimId_MphMeasureReq] = GsmL1_PrimId_MphMeasureCnf,
|
[GsmL1_PrimId_MphMeasureReq] = GsmL1_PrimId_MphMeasureCnf,
|
||||||
};
|
};
|
||||||
|
|
||||||
const enum l1prim_type femtobts_sysprim_type[FemtoBts_PrimId_NUM] = {
|
const enum l1prim_type femtobts_sysprim_type[SuperFemto_PrimId_NUM] = {
|
||||||
[FemtoBts_PrimId_SystemInfoReq] = L1P_T_REQ,
|
[SuperFemto_PrimId_SystemInfoReq] = L1P_T_REQ,
|
||||||
[FemtoBts_PrimId_SystemInfoCnf] = L1P_T_CONF,
|
[SuperFemto_PrimId_SystemInfoCnf] = L1P_T_CONF,
|
||||||
[FemtoBts_PrimId_SystemFailureInd] = L1P_T_IND,
|
[SuperFemto_PrimId_SystemFailureInd] = L1P_T_IND,
|
||||||
[FemtoBts_PrimId_ActivateRfReq] = L1P_T_REQ,
|
[SuperFemto_PrimId_ActivateRfReq] = L1P_T_REQ,
|
||||||
[FemtoBts_PrimId_ActivateRfCnf] = L1P_T_CONF,
|
[SuperFemto_PrimId_ActivateRfCnf] = L1P_T_CONF,
|
||||||
[FemtoBts_PrimId_DeactivateRfReq] = L1P_T_REQ,
|
[SuperFemto_PrimId_DeactivateRfReq] = L1P_T_REQ,
|
||||||
[FemtoBts_PrimId_DeactivateRfCnf] = L1P_T_CONF,
|
[SuperFemto_PrimId_DeactivateRfCnf] = L1P_T_CONF,
|
||||||
[FemtoBts_PrimId_SetTraceFlagsReq] = L1P_T_REQ,
|
[SuperFemto_PrimId_SetTraceFlagsReq] = L1P_T_REQ,
|
||||||
[FemtoBts_PrimId_RfClockInfoReq] = L1P_T_REQ,
|
[SuperFemto_PrimId_RfClockInfoReq] = L1P_T_REQ,
|
||||||
[FemtoBts_PrimId_RfClockInfoCnf] = L1P_T_CONF,
|
[SuperFemto_PrimId_RfClockInfoCnf] = L1P_T_CONF,
|
||||||
[FemtoBts_PrimId_RfClockSetupReq] = L1P_T_REQ,
|
[SuperFemto_PrimId_RfClockSetupReq] = L1P_T_REQ,
|
||||||
[FemtoBts_PrimId_RfClockSetupCnf] = L1P_T_CONF,
|
[SuperFemto_PrimId_RfClockSetupCnf] = L1P_T_CONF,
|
||||||
[FemtoBts_PrimId_Layer1ResetReq] = L1P_T_REQ,
|
[SuperFemto_PrimId_Layer1ResetReq] = L1P_T_REQ,
|
||||||
[FemtoBts_PrimId_Layer1ResetCnf] = L1P_T_CONF,
|
[SuperFemto_PrimId_Layer1ResetCnf] = L1P_T_CONF,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct value_string femtobts_sysprim_names[FemtoBts_PrimId_NUM+1] = {
|
const struct value_string femtobts_sysprim_names[SuperFemto_PrimId_NUM+1] = {
|
||||||
{ FemtoBts_PrimId_SystemInfoReq, "SYSTEM-INFO.req" },
|
{ SuperFemto_PrimId_SystemInfoReq, "SYSTEM-INFO.req" },
|
||||||
{ FemtoBts_PrimId_SystemInfoCnf, "SYSTEM-INFO.conf" },
|
{ SuperFemto_PrimId_SystemInfoCnf, "SYSTEM-INFO.conf" },
|
||||||
{ FemtoBts_PrimId_SystemFailureInd, "SYSTEM-FAILURE.ind" },
|
{ SuperFemto_PrimId_SystemFailureInd, "SYSTEM-FAILURE.ind" },
|
||||||
{ FemtoBts_PrimId_ActivateRfReq, "ACTIVATE-RF.req" },
|
{ SuperFemto_PrimId_ActivateRfReq, "ACTIVATE-RF.req" },
|
||||||
{ FemtoBts_PrimId_ActivateRfCnf, "ACTIVATE-RF.conf" },
|
{ SuperFemto_PrimId_ActivateRfCnf, "ACTIVATE-RF.conf" },
|
||||||
{ FemtoBts_PrimId_DeactivateRfReq, "DEACTIVATE-RF.req" },
|
{ SuperFemto_PrimId_DeactivateRfReq, "DEACTIVATE-RF.req" },
|
||||||
{ FemtoBts_PrimId_DeactivateRfCnf, "DEACTIVATE-RF.conf" },
|
{ SuperFemto_PrimId_DeactivateRfCnf, "DEACTIVATE-RF.conf" },
|
||||||
{ FemtoBts_PrimId_SetTraceFlagsReq, "SET-TRACE-FLAGS.req" },
|
{ SuperFemto_PrimId_SetTraceFlagsReq, "SET-TRACE-FLAGS.req" },
|
||||||
{ FemtoBts_PrimId_RfClockInfoReq, "RF-CLOCK-INFO.req" },
|
{ SuperFemto_PrimId_RfClockInfoReq, "RF-CLOCK-INFO.req" },
|
||||||
{ FemtoBts_PrimId_RfClockInfoCnf, "RF-CLOCK-INFO.conf" },
|
{ SuperFemto_PrimId_RfClockInfoCnf, "RF-CLOCK-INFO.conf" },
|
||||||
{ FemtoBts_PrimId_RfClockSetupReq, "RF-CLOCK-SETUP.req" },
|
{ SuperFemto_PrimId_RfClockSetupReq, "RF-CLOCK-SETUP.req" },
|
||||||
{ FemtoBts_PrimId_RfClockSetupCnf, "RF-CLOCK-SETUP.conf" },
|
{ SuperFemto_PrimId_RfClockSetupCnf, "RF-CLOCK-SETUP.conf" },
|
||||||
{ FemtoBts_PrimId_Layer1ResetReq, "LAYER1-RESET.req" },
|
{ SuperFemto_PrimId_Layer1ResetReq, "LAYER1-RESET.req" },
|
||||||
{ FemtoBts_PrimId_Layer1ResetCnf, "LAYER1-RESET.conf" },
|
{ SuperFemto_PrimId_Layer1ResetCnf, "LAYER1-RESET.conf" },
|
||||||
|
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
|
||||||
|
{ SuperFemto_PrimId_GetTxCalibTblReq, "GET-TX-CALIB.req" },
|
||||||
|
{ SuperFemto_PrimId_GetTxCalibTblCnf, "GET-TX-CALIB.cnf" },
|
||||||
|
{ SuperFemto_PrimId_SetTxCalibTblReq, "SET-TX-CALIB.req" },
|
||||||
|
{ SuperFemto_PrimId_SetTxCalibTblCnf, "SET-TX-CALIB.cnf" },
|
||||||
|
{ SuperFemto_PrimId_GetRxCalibTblReq, "GET-RX-CALIB.req" },
|
||||||
|
{ SuperFemto_PrimId_GetRxCalibTblCnf, "GET-RX-CALIB.cnf" },
|
||||||
|
{ SuperFemto_PrimId_SetRxCalibTblReq, "SET-RX-CALIB.req" },
|
||||||
|
{ SuperFemto_PrimId_SetRxCalibTblCnf, "SET-RX-CALIB.cnf" },
|
||||||
|
#endif
|
||||||
{ 0, NULL }
|
{ 0, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
const FemtoBts_PrimId_t femtobts_sysprim_req2conf[FemtoBts_PrimId_NUM] = {
|
const SuperFemto_PrimId_t femtobts_sysprim_req2conf[SuperFemto_PrimId_NUM] = {
|
||||||
[FemtoBts_PrimId_SystemInfoReq] = FemtoBts_PrimId_SystemInfoCnf,
|
[SuperFemto_PrimId_SystemInfoReq] = SuperFemto_PrimId_SystemInfoCnf,
|
||||||
[FemtoBts_PrimId_ActivateRfReq] = FemtoBts_PrimId_ActivateRfCnf,
|
[SuperFemto_PrimId_ActivateRfReq] = SuperFemto_PrimId_ActivateRfCnf,
|
||||||
[FemtoBts_PrimId_DeactivateRfReq] = FemtoBts_PrimId_DeactivateRfCnf,
|
[SuperFemto_PrimId_DeactivateRfReq] = SuperFemto_PrimId_DeactivateRfCnf,
|
||||||
[FemtoBts_PrimId_RfClockInfoReq] = FemtoBts_PrimId_RfClockInfoCnf,
|
[SuperFemto_PrimId_RfClockInfoReq] = SuperFemto_PrimId_RfClockInfoCnf,
|
||||||
[FemtoBts_PrimId_RfClockSetupReq] = FemtoBts_PrimId_RfClockSetupCnf,
|
[SuperFemto_PrimId_RfClockSetupReq] = SuperFemto_PrimId_RfClockSetupCnf,
|
||||||
[FemtoBts_PrimId_Layer1ResetReq] = FemtoBts_PrimId_Layer1ResetCnf,
|
[SuperFemto_PrimId_Layer1ResetReq] = SuperFemto_PrimId_Layer1ResetCnf,
|
||||||
|
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
|
||||||
|
[SuperFemto_PrimId_GetTxCalibTblReq] = SuperFemto_PrimId_GetTxCalibTblCnf,
|
||||||
|
[SuperFemto_PrimId_SetTxCalibTblReq] = SuperFemto_PrimId_SetTxCalibTblCnf,
|
||||||
|
[SuperFemto_PrimId_GetRxCalibTblReq] = SuperFemto_PrimId_GetRxCalibTblCnf,
|
||||||
|
[SuperFemto_PrimId_SetRxCalibTblReq] = SuperFemto_PrimId_SetRxCalibTblCnf,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct value_string femtobts_l1sapi_names[GsmL1_Sapi_NUM+1] = {
|
const struct value_string femtobts_l1sapi_names[GsmL1_Sapi_NUM+1] = {
|
||||||
@@ -220,10 +236,42 @@ const struct value_string femtobts_tracef_names[29] = {
|
|||||||
{ 0, NULL }
|
{ 0, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const struct value_string femtobts_tracef_docs[29] = {
|
||||||
|
{ DBG_DEBUG, "Debug Region" },
|
||||||
|
{ DBG_L1WARNING, "L1 Warning Region" },
|
||||||
|
{ DBG_ERROR, "Error Region" },
|
||||||
|
{ DBG_L1RXMSG, "L1_RX_MSG Region" },
|
||||||
|
{ DBG_L1RXMSGBYTE, "L1_RX_MSG_BYTE Region" },
|
||||||
|
{ DBG_L1TXMSG, "L1_TX_MSG Region" },
|
||||||
|
{ DBG_L1TXMSGBYTE, "L1_TX_MSG_BYTE Region" },
|
||||||
|
{ DBG_MPHCNF, "MphConfirmation Region" },
|
||||||
|
{ DBG_MPHIND, "MphIndication Region" },
|
||||||
|
{ DBG_MPHREQ, "MphRequest Region" },
|
||||||
|
{ DBG_PHIND, "PhIndication Region" },
|
||||||
|
{ DBG_PHREQ, "PhRequest Region" },
|
||||||
|
{ DBG_PHYRF, "PhyRF Region" },
|
||||||
|
{ DBG_PHYRFMSGBYTE, "PhyRF Message Region" },
|
||||||
|
{ DBG_MODE, "Mode Region" },
|
||||||
|
{ DBG_TDMAINFO, "TDMA Info Region" },
|
||||||
|
{ DBG_BADCRC, "Bad CRC Region" },
|
||||||
|
{ DBG_PHINDBYTE, "PH_IND_BYTE" },
|
||||||
|
{ DBG_PHREQBYTE, "PH_REQ_BYTE" },
|
||||||
|
{ DBG_DEVICEMSG, "Device Message Region" },
|
||||||
|
{ DBG_RACHINFO, "RACH Info" },
|
||||||
|
{ DBG_LOGCHINFO, "LOG_CH_INFO" },
|
||||||
|
{ DBG_MEMORY, "Memory Region" },
|
||||||
|
{ DBG_PROFILING, "Profiling Region" },
|
||||||
|
{ DBG_TESTCOMMENT, "Test Comments" },
|
||||||
|
{ DBG_TEST, "Test Region" },
|
||||||
|
{ DBG_STATUS, "Status Region" },
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
const struct value_string femtobts_tch_pl_names[] = {
|
const struct value_string femtobts_tch_pl_names[] = {
|
||||||
{ GsmL1_TchPlType_NA, "N/A" },
|
{ GsmL1_TchPlType_NA, "N/A" },
|
||||||
{ GsmL1_TchPlType_Fr, "FR" },
|
{ GsmL1_TchPlType_Fr, "FR" },
|
||||||
{ GsmL1_TchPlType_Hr, "HR" },
|
{ GsmL1_TchPlType_Hr, "HR" },
|
||||||
|
{ GsmL1_TchPlType_Efr, "EFR" },
|
||||||
{ GsmL1_TchPlType_Amr, "AMR(IF2)" },
|
{ GsmL1_TchPlType_Amr, "AMR(IF2)" },
|
||||||
{ GsmL1_TchPlType_Amr_SidBad, "AMR(SID BAD)" },
|
{ GsmL1_TchPlType_Amr_SidBad, "AMR(SID BAD)" },
|
||||||
{ GsmL1_TchPlType_Amr_Onset, "AMR(ONSET)" },
|
{ GsmL1_TchPlType_Amr_Onset, "AMR(ONSET)" },
|
||||||
@@ -236,3 +284,62 @@ const struct value_string femtobts_tch_pl_names[] = {
|
|||||||
{ GsmL1_TchPlType_Amr_RatscchData, "AMR(RATSCCH DATA)" },
|
{ GsmL1_TchPlType_Amr_RatscchData, "AMR(RATSCCH DATA)" },
|
||||||
{ 0, NULL }
|
{ 0, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const struct value_string femtobts_clksrc_names[] = {
|
||||||
|
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
|
||||||
|
{ SuperFemto_ClkSrcId_None, "None" },
|
||||||
|
{ SuperFemto_ClkSrcId_Ocxo, "ocxo" },
|
||||||
|
{ SuperFemto_ClkSrcId_Tcxo, "tcxo" },
|
||||||
|
{ SuperFemto_ClkSrcId_External, "ext" },
|
||||||
|
{ SuperFemto_ClkSrcId_GpsPps, "gps" },
|
||||||
|
{ SuperFemto_ClkSrcId_Trx, "trx" },
|
||||||
|
{ SuperFemto_ClkSrcId_Rx, "rx" },
|
||||||
|
{ SuperFemto_ClkSrcId_Edge, "edge" },
|
||||||
|
{ SuperFemto_ClkSrcId_NetList, "nwl" },
|
||||||
|
#else
|
||||||
|
{ SF_CLKSRC_NONE, "None" },
|
||||||
|
{ SF_CLKSRC_OCXO, "ocxo" },
|
||||||
|
{ SF_CLKSRC_TCXO, "tcxo" },
|
||||||
|
{ SF_CLKSRC_EXT, "ext" },
|
||||||
|
{ SF_CLKSRC_GPS, "gps" },
|
||||||
|
{ SF_CLKSRC_TRX, "trx" },
|
||||||
|
{ SF_CLKSRC_RX, "rx" },
|
||||||
|
#endif
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct value_string femtobts_dir_names[] = {
|
||||||
|
{ GsmL1_Dir_TxDownlink, "TxDL" },
|
||||||
|
{ GsmL1_Dir_TxUplink, "TxUL" },
|
||||||
|
{ GsmL1_Dir_RxUplink, "RxUL" },
|
||||||
|
{ GsmL1_Dir_RxDownlink, "RxDL" },
|
||||||
|
{ GsmL1_Dir_TxDownlink|GsmL1_Dir_RxUplink, "BOTH" },
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct value_string femtobts_chcomb_names[] = {
|
||||||
|
{ GsmL1_LogChComb_0, "dummy" },
|
||||||
|
{ GsmL1_LogChComb_I, "tch_f" },
|
||||||
|
{ GsmL1_LogChComb_II, "tch_h" },
|
||||||
|
{ GsmL1_LogChComb_IV, "ccch" },
|
||||||
|
{ GsmL1_LogChComb_V, "ccch_sdcch4" },
|
||||||
|
{ GsmL1_LogChComb_VII, "sdcch8" },
|
||||||
|
{ GsmL1_LogChComb_XIII, "pdtch" },
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t pdch_msu_size[_NUM_PDCH_CS] = {
|
||||||
|
[PDCH_CS_1] = 23,
|
||||||
|
[PDCH_CS_2] = 34,
|
||||||
|
[PDCH_CS_3] = 40,
|
||||||
|
[PDCH_CS_4] = 54,
|
||||||
|
[PDCH_MCS_1] = 27,
|
||||||
|
[PDCH_MCS_2] = 33,
|
||||||
|
[PDCH_MCS_3] = 42,
|
||||||
|
[PDCH_MCS_4] = 49,
|
||||||
|
[PDCH_MCS_5] = 60,
|
||||||
|
[PDCH_MCS_6] = 78,
|
||||||
|
[PDCH_MCS_7] = 118,
|
||||||
|
[PDCH_MCS_8] = 142,
|
||||||
|
[PDCH_MCS_9] = 154
|
||||||
|
};
|
||||||
|
|||||||
@@ -4,29 +4,78 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <osmocom/core/utils.h>
|
#include <osmocom/core/utils.h>
|
||||||
|
|
||||||
#include <sysmocom/femtobts/femtobts.h>
|
#include <sysmocom/femtobts/superfemto.h>
|
||||||
#include <sysmocom/femtobts/gsml1const.h>
|
#include <sysmocom/femtobts/gsml1const.h>
|
||||||
|
|
||||||
|
#ifdef L1_HAS_RTP_MODE
|
||||||
|
/* This is temporarily disabled, as AMR has some bugs in RTP mode */
|
||||||
|
//#define USE_L1_RTP_MODE /* Tell L1 to use RTP mode */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Depending on the firmware version either GsmL1_Prim_t or SuperFemto_Prim_t
|
||||||
|
* is the bigger struct. For earlier firmware versions the GsmL1_Prim_t was the
|
||||||
|
* bigger struct.
|
||||||
|
*/
|
||||||
|
#define SYSMOBTS_PRIM_SIZE \
|
||||||
|
(OSMO_MAX(sizeof(SuperFemto_Prim_t), sizeof(GsmL1_Prim_t)) + 128)
|
||||||
|
|
||||||
enum l1prim_type {
|
enum l1prim_type {
|
||||||
L1P_T_REQ,
|
L1P_T_REQ,
|
||||||
L1P_T_CONF,
|
L1P_T_CONF,
|
||||||
L1P_T_IND,
|
L1P_T_IND,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,1,0)
|
||||||
|
enum uperfemto_clk_src {
|
||||||
|
SF_CLKSRC_NONE = 0,
|
||||||
|
SF_CLKSRC_OCXO = 1,
|
||||||
|
SF_CLKSRC_TCXO = 2,
|
||||||
|
SF_CLKSRC_EXT = 3,
|
||||||
|
SF_CLKSRC_GPS = 4,
|
||||||
|
SF_CLKSRC_TRX = 5,
|
||||||
|
SF_CLKSRC_RX = 6,
|
||||||
|
SF_CLKSRC_NL = 7,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
const enum l1prim_type femtobts_l1prim_type[GsmL1_PrimId_NUM];
|
const enum l1prim_type femtobts_l1prim_type[GsmL1_PrimId_NUM];
|
||||||
const struct value_string femtobts_l1prim_names[GsmL1_PrimId_NUM+1];
|
const struct value_string femtobts_l1prim_names[GsmL1_PrimId_NUM+1];
|
||||||
const GsmL1_PrimId_t femtobts_l1prim_req2conf[GsmL1_PrimId_NUM];
|
const GsmL1_PrimId_t femtobts_l1prim_req2conf[GsmL1_PrimId_NUM];
|
||||||
|
|
||||||
const enum l1prim_type femtobts_sysprim_type[FemtoBts_PrimId_NUM];
|
const enum l1prim_type femtobts_sysprim_type[SuperFemto_PrimId_NUM];
|
||||||
const struct value_string femtobts_sysprim_names[FemtoBts_PrimId_NUM+1];
|
const struct value_string femtobts_sysprim_names[SuperFemto_PrimId_NUM+1];
|
||||||
const FemtoBts_PrimId_t femtobts_sysprim_req2conf[FemtoBts_PrimId_NUM];
|
const SuperFemto_PrimId_t femtobts_sysprim_req2conf[SuperFemto_PrimId_NUM];
|
||||||
|
|
||||||
const struct value_string femtobts_l1sapi_names[GsmL1_Sapi_NUM+1];
|
const struct value_string femtobts_l1sapi_names[GsmL1_Sapi_NUM+1];
|
||||||
const struct value_string femtobts_l1status_names[GSML1_STATUS_NUM+1];
|
const struct value_string femtobts_l1status_names[GSML1_STATUS_NUM+1];
|
||||||
|
|
||||||
const struct value_string femtobts_tracef_names[29];
|
const struct value_string femtobts_tracef_names[29];
|
||||||
|
const struct value_string femtobts_tracef_docs[29];
|
||||||
|
|
||||||
const struct value_string femtobts_tch_pl_names[];
|
const struct value_string femtobts_tch_pl_names[15];
|
||||||
|
|
||||||
|
const struct value_string femtobts_clksrc_names[10];
|
||||||
|
|
||||||
|
const struct value_string femtobts_dir_names[6];
|
||||||
|
|
||||||
|
enum pdch_cs {
|
||||||
|
PDCH_CS_1,
|
||||||
|
PDCH_CS_2,
|
||||||
|
PDCH_CS_3,
|
||||||
|
PDCH_CS_4,
|
||||||
|
PDCH_MCS_1,
|
||||||
|
PDCH_MCS_2,
|
||||||
|
PDCH_MCS_3,
|
||||||
|
PDCH_MCS_4,
|
||||||
|
PDCH_MCS_5,
|
||||||
|
PDCH_MCS_6,
|
||||||
|
PDCH_MCS_7,
|
||||||
|
PDCH_MCS_8,
|
||||||
|
PDCH_MCS_9,
|
||||||
|
_NUM_PDCH_CS
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t pdch_msu_size[_NUM_PDCH_CS];
|
||||||
|
|
||||||
#endif /* FEMTOBTS_H */
|
#endif /* FEMTOBTS_H */
|
||||||
|
|||||||
113
src/osmo-bts-sysmo/hw_misc.c
Normal file
113
src/osmo-bts-sysmo/hw_misc.c
Normal 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
|
||||||
12
src/osmo-bts-sysmo/hw_misc.h
Normal file
12
src/osmo-bts-sysmo/hw_misc.h
Normal 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
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
#define L1FWD_L1_PORT 9999
|
#define L1FWD_L1_PORT 9999
|
||||||
#define L1FWD_SYS_PORT 9998
|
#define L1FWD_SYS_PORT 9998
|
||||||
|
#define L1FWD_TCH_PORT 9997
|
||||||
|
#define L1FWD_PDTCH_PORT 9996
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
#include <osmo-bts/logging.h>
|
#include <osmo-bts/logging.h>
|
||||||
#include <osmo-bts/gsm_data.h>
|
#include <osmo-bts/gsm_data.h>
|
||||||
|
|
||||||
#include <sysmocom/femtobts/femtobts.h>
|
#include <sysmocom/femtobts/superfemto.h>
|
||||||
#include <sysmocom/femtobts/gsml1prim.h>
|
#include <sysmocom/femtobts/gsml1prim.h>
|
||||||
#include <sysmocom/femtobts/gsml1const.h>
|
#include <sysmocom/femtobts/gsml1const.h>
|
||||||
#include <sysmocom/femtobts/gsml1types.h>
|
#include <sysmocom/femtobts/gsml1types.h>
|
||||||
@@ -55,11 +55,15 @@
|
|||||||
static const uint16_t fwd_udp_ports[_NUM_MQ_WRITE] = {
|
static const uint16_t fwd_udp_ports[_NUM_MQ_WRITE] = {
|
||||||
[MQ_SYS_READ] = L1FWD_SYS_PORT,
|
[MQ_SYS_READ] = L1FWD_SYS_PORT,
|
||||||
[MQ_L1_READ] = L1FWD_L1_PORT,
|
[MQ_L1_READ] = L1FWD_L1_PORT,
|
||||||
|
#ifndef HW_SYSMOBTS_V1
|
||||||
|
[MQ_TCH_READ] = L1FWD_TCH_PORT,
|
||||||
|
[MQ_PDTCH_READ] = L1FWD_PDTCH_PORT,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct l1fwd_hdl {
|
struct l1fwd_hdl {
|
||||||
struct sockaddr_storage remote_sa;
|
struct sockaddr_storage remote_sa[_NUM_MQ_WRITE];
|
||||||
socklen_t remote_sa_len;
|
socklen_t remote_sa_len[_NUM_MQ_WRITE];
|
||||||
|
|
||||||
struct osmo_wqueue udp_wq[_NUM_MQ_WRITE];
|
struct osmo_wqueue udp_wq[_NUM_MQ_WRITE];
|
||||||
|
|
||||||
@@ -68,12 +72,12 @@ struct l1fwd_hdl {
|
|||||||
|
|
||||||
|
|
||||||
/* callback when there's a new L1 primitive coming in from the HW */
|
/* callback when there's a new L1 primitive coming in from the HW */
|
||||||
int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg)
|
int l1if_handle_l1prim(int wq, struct femtol1_hdl *fl1h, struct msgb *msg)
|
||||||
{
|
{
|
||||||
struct l1fwd_hdl *l1fh = fl1h->priv;
|
struct l1fwd_hdl *l1fh = fl1h->priv;
|
||||||
|
|
||||||
/* Enqueue message to UDP socket */
|
/* Enqueue message to UDP socket */
|
||||||
return osmo_wqueue_enqueue(&l1fh->udp_wq[MQ_L1_WRITE], msg);
|
return osmo_wqueue_enqueue(&l1fh->udp_wq[wq], msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* callback when there's a new SYS primitive coming in from the HW */
|
/* callback when there's a new SYS primitive coming in from the HW */
|
||||||
@@ -89,7 +93,7 @@ int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg)
|
|||||||
/* data has arrived on the udp socket */
|
/* data has arrived on the udp socket */
|
||||||
static int udp_read_cb(struct osmo_fd *ofd)
|
static int udp_read_cb(struct osmo_fd *ofd)
|
||||||
{
|
{
|
||||||
struct msgb *msg = msgb_alloc_headroom(2048, 128, "udp_rx");
|
struct msgb *msg = msgb_alloc_headroom(SYSMOBTS_PRIM_SIZE, 128, "udp_rx");
|
||||||
struct l1fwd_hdl *l1fh = ofd->data;
|
struct l1fwd_hdl *l1fh = ofd->data;
|
||||||
struct femtol1_hdl *fl1h = l1fh->fl1h;
|
struct femtol1_hdl *fl1h = l1fh->fl1h;
|
||||||
int rc;
|
int rc;
|
||||||
@@ -99,9 +103,9 @@ static int udp_read_cb(struct osmo_fd *ofd)
|
|||||||
|
|
||||||
msg->l1h = msg->data;
|
msg->l1h = msg->data;
|
||||||
|
|
||||||
l1fh->remote_sa_len = sizeof(l1fh->remote_sa);
|
l1fh->remote_sa_len[ofd->priv_nr] = sizeof(l1fh->remote_sa[ofd->priv_nr]);
|
||||||
rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0,
|
rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0,
|
||||||
(struct sockaddr *) &l1fh->remote_sa, &l1fh->remote_sa_len);
|
(struct sockaddr *) &l1fh->remote_sa[ofd->priv_nr], &l1fh->remote_sa_len[ofd->priv_nr]);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
perror("read from udp");
|
perror("read from udp");
|
||||||
msgb_free(msg);
|
msgb_free(msg);
|
||||||
@@ -113,14 +117,11 @@ static int udp_read_cb(struct osmo_fd *ofd)
|
|||||||
}
|
}
|
||||||
msgb_put(msg, rc);
|
msgb_put(msg, rc);
|
||||||
|
|
||||||
DEBUGP(DL1C, "UDP: Received %u bytes for %s queue\n", rc,
|
DEBUGP(DL1C, "UDP: Received %u bytes for queue %d\n", rc,
|
||||||
ofd->priv_nr == MQ_SYS_WRITE ? "SYS" : "L1");
|
ofd->priv_nr);
|
||||||
|
|
||||||
/* put the message into the right queue */
|
/* put the message into the right queue */
|
||||||
if (ofd->priv_nr == MQ_SYS_WRITE)
|
rc = osmo_wqueue_enqueue(&fl1h->write_q[ofd->priv_nr], msg);
|
||||||
rc = osmo_wqueue_enqueue(&fl1h->write_q[MQ_SYS_WRITE], msg);
|
|
||||||
else
|
|
||||||
rc = osmo_wqueue_enqueue(&fl1h->write_q[MQ_L1_WRITE], msg);
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -131,11 +132,11 @@ static int udp_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
|||||||
int rc;
|
int rc;
|
||||||
struct l1fwd_hdl *l1fh = ofd->data;
|
struct l1fwd_hdl *l1fh = ofd->data;
|
||||||
|
|
||||||
DEBUGP(DL1C, "UDP: Writing %u bytes for %s queue\n", msgb_l1len(msg),
|
DEBUGP(DL1C, "UDP: Writing %u bytes for queue %d\n", msgb_l1len(msg),
|
||||||
ofd->priv_nr == MQ_SYS_WRITE ? "SYS" : "L1");
|
ofd->priv_nr);
|
||||||
|
|
||||||
rc = sendto(ofd->fd, msg->l1h, msgb_l1len(msg), 0,
|
rc = sendto(ofd->fd, msg->l1h, msgb_l1len(msg), 0,
|
||||||
(const struct sockaddr *)&l1fh->remote_sa, l1fh->remote_sa_len);
|
(const struct sockaddr *)&l1fh->remote_sa[ofd->priv_nr], l1fh->remote_sa_len[ofd->priv_nr]);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
LOGP(DL1C, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
|
LOGP(DL1C, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
@@ -155,8 +156,8 @@ int main(int argc, char **argv)
|
|||||||
struct femtol1_hdl *fl1h;
|
struct femtol1_hdl *fl1h;
|
||||||
int rc, i;
|
int rc, i;
|
||||||
|
|
||||||
printf("sizeof(GsmL1_Prim_t) = %lu\n", sizeof(GsmL1_Prim_t));
|
printf("sizeof(GsmL1_Prim_t) = %zu\n", sizeof(GsmL1_Prim_t));
|
||||||
printf("sizeof(FemtoBts_Prim_t) = %lu\n", sizeof(FemtoBts_Prim_t));
|
printf("sizeof(SuperFemto_Prim_t) = %zu\n", sizeof(SuperFemto_Prim_t));
|
||||||
|
|
||||||
bts_log_init(NULL);
|
bts_log_init(NULL);
|
||||||
|
|
||||||
@@ -165,9 +166,11 @@ int main(int argc, char **argv)
|
|||||||
INIT_LLIST_HEAD(&fl1h->wlc_list);
|
INIT_LLIST_HEAD(&fl1h->wlc_list);
|
||||||
|
|
||||||
/* open the actual hardware transport */
|
/* open the actual hardware transport */
|
||||||
rc = l1if_transport_open(fl1h);
|
for (i = 0; i < ARRAY_SIZE(fl1h->write_q); i++) {
|
||||||
if (rc < 0)
|
rc = l1if_transport_open(i, fl1h);
|
||||||
exit(1);
|
if (rc < 0)
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
/* create our fwd handle */
|
/* create our fwd handle */
|
||||||
l1fh = talloc_zero(NULL, struct l1fwd_hdl);
|
l1fh = talloc_zero(NULL, struct l1fwd_hdl);
|
||||||
@@ -176,7 +179,7 @@ int main(int argc, char **argv)
|
|||||||
fl1h->priv = l1fh;
|
fl1h->priv = l1fh;
|
||||||
|
|
||||||
/* Open UDP */
|
/* Open UDP */
|
||||||
for (i = 0; i < 2; i++) {
|
for (i = 0; i < ARRAY_SIZE(l1fh->udp_wq); i++) {
|
||||||
struct osmo_wqueue *wq = &l1fh->udp_wq[i];
|
struct osmo_wqueue *wq = &l1fh->udp_wq[i];
|
||||||
|
|
||||||
osmo_wqueue_init(wq, 10);
|
osmo_wqueue_init(wq, 10);
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
#include <osmocom/core/select.h>
|
#include <osmocom/core/select.h>
|
||||||
#include <osmocom/core/timer.h>
|
#include <osmocom/core/timer.h>
|
||||||
#include <osmocom/core/write_queue.h>
|
#include <osmocom/core/write_queue.h>
|
||||||
|
#include <osmocom/core/gsmtap.h>
|
||||||
|
#include <osmocom/core/gsmtap_util.h>
|
||||||
#include <osmocom/gsm/gsm_utils.h>
|
#include <osmocom/gsm/gsm_utils.h>
|
||||||
#include <osmocom/gsm/lapdm.h>
|
#include <osmocom/gsm/lapdm.h>
|
||||||
|
|
||||||
@@ -43,8 +45,9 @@
|
|||||||
#include <osmo-bts/gsm_data.h>
|
#include <osmo-bts/gsm_data.h>
|
||||||
#include <osmo-bts/paging.h>
|
#include <osmo-bts/paging.h>
|
||||||
#include <osmo-bts/measurement.h>
|
#include <osmo-bts/measurement.h>
|
||||||
|
#include <osmo-bts/pcu_if.h>
|
||||||
|
|
||||||
#include <sysmocom/femtobts/femtobts.h>
|
#include <sysmocom/femtobts/superfemto.h>
|
||||||
#include <sysmocom/femtobts/gsml1prim.h>
|
#include <sysmocom/femtobts/gsml1prim.h>
|
||||||
#include <sysmocom/femtobts/gsml1const.h>
|
#include <sysmocom/femtobts/gsml1const.h>
|
||||||
#include <sysmocom/femtobts/gsml1types.h>
|
#include <sysmocom/femtobts/gsml1types.h>
|
||||||
@@ -52,11 +55,104 @@
|
|||||||
#include "femtobts.h"
|
#include "femtobts.h"
|
||||||
#include "l1_if.h"
|
#include "l1_if.h"
|
||||||
#include "l1_transp.h"
|
#include "l1_transp.h"
|
||||||
|
#include "hw_misc.h"
|
||||||
|
|
||||||
|
extern int pcu_direct;
|
||||||
|
|
||||||
/* FIXME: make threshold configurable */
|
/* FIXME: make threshold configurable */
|
||||||
#define MIN_QUAL_RACH 5.0f /* at least 5 dB C/I */
|
#define MIN_QUAL_RACH 5.0f /* at least 5 dB C/I */
|
||||||
#define MIN_QUAL_NORM -0.5f /* at least -1 dB C/I */
|
#define MIN_QUAL_NORM -0.5f /* at least -1 dB C/I */
|
||||||
|
|
||||||
|
/* mapping from femtbts L1 SAPI to GSMTAP channel type */
|
||||||
|
static const uint8_t l1sapi2gsmtap_cht[GsmL1_Sapi_NUM] = {
|
||||||
|
[GsmL1_Sapi_Idle] = 255,
|
||||||
|
[GsmL1_Sapi_Fcch] = 255,
|
||||||
|
[GsmL1_Sapi_Sch] = 255,
|
||||||
|
[GsmL1_Sapi_Sacch] = GSMTAP_CHANNEL_SDCCH | GSMTAP_CHANNEL_ACCH,
|
||||||
|
[GsmL1_Sapi_Sdcch] = GSMTAP_CHANNEL_SDCCH,
|
||||||
|
[GsmL1_Sapi_Bcch] = GSMTAP_CHANNEL_BCCH,
|
||||||
|
[GsmL1_Sapi_Pch] = GSMTAP_CHANNEL_PCH,
|
||||||
|
[GsmL1_Sapi_Agch] = GSMTAP_CHANNEL_AGCH,
|
||||||
|
[GsmL1_Sapi_Cbch] = GSMTAP_CHANNEL_CBCH51,
|
||||||
|
[GsmL1_Sapi_Rach] = GSMTAP_CHANNEL_RACH,
|
||||||
|
[GsmL1_Sapi_TchF] = 255,
|
||||||
|
[GsmL1_Sapi_FacchF] = GSMTAP_CHANNEL_TCH_F,
|
||||||
|
[GsmL1_Sapi_TchH] = 255,
|
||||||
|
[GsmL1_Sapi_FacchH] = GSMTAP_CHANNEL_TCH_H,
|
||||||
|
[GsmL1_Sapi_Nch] = GSMTAP_CHANNEL_CCCH,
|
||||||
|
[GsmL1_Sapi_Pdtch] = GSMTAP_CHANNEL_PACCH,
|
||||||
|
[GsmL1_Sapi_Pacch] = GSMTAP_CHANNEL_PACCH,
|
||||||
|
[GsmL1_Sapi_Pbcch] = 255,
|
||||||
|
[GsmL1_Sapi_Pagch] = 255,
|
||||||
|
[GsmL1_Sapi_Ppch] = 255,
|
||||||
|
[GsmL1_Sapi_Pnch] = 255,
|
||||||
|
[GsmL1_Sapi_Ptcch] = GSMTAP_CHANNEL_PTCCH,
|
||||||
|
[GsmL1_Sapi_Prach] = 255,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void tx_to_gsmtap(struct femtol1_hdl *fl1h, struct msgb *msg)
|
||||||
|
{
|
||||||
|
struct gsm_bts_trx *trx = fl1h->priv;
|
||||||
|
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
|
||||||
|
GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
|
||||||
|
|
||||||
|
if (fl1h->gsmtap) {
|
||||||
|
uint8_t ss, chan_type;
|
||||||
|
if (data_req->subCh == 0x1f)
|
||||||
|
ss = 0;
|
||||||
|
else
|
||||||
|
ss = data_req->subCh;
|
||||||
|
|
||||||
|
if (!(fl1h->gsmtap_sapi_mask & (1 << data_req->sapi)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
chan_type = l1sapi2gsmtap_cht[data_req->sapi];
|
||||||
|
if (chan_type == 255)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gsmtap_send(fl1h->gsmtap, trx->arfcn, data_req->u8Tn,
|
||||||
|
chan_type, ss, data_req->u32Fn, 0, 0,
|
||||||
|
data_req->msgUnitParam.u8Buffer,
|
||||||
|
data_req->msgUnitParam.u8Size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ul_to_gsmtap(struct femtol1_hdl *fl1h, struct msgb *msg)
|
||||||
|
{
|
||||||
|
struct gsm_bts_trx *trx = fl1h->priv;
|
||||||
|
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
|
||||||
|
GsmL1_PhDataInd_t *data_ind = &l1p->u.phDataInd;
|
||||||
|
int skip = 0;
|
||||||
|
|
||||||
|
if (fl1h->gsmtap) {
|
||||||
|
uint8_t ss, chan_type;
|
||||||
|
if (data_ind->subCh == 0x1f)
|
||||||
|
ss = 0;
|
||||||
|
else
|
||||||
|
ss = data_ind->subCh;
|
||||||
|
|
||||||
|
if (!(fl1h->gsmtap_sapi_mask & (1 << data_ind->sapi)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
chan_type = l1sapi2gsmtap_cht[data_ind->sapi];
|
||||||
|
if (chan_type == 255)
|
||||||
|
return;
|
||||||
|
if (chan_type == GSMTAP_CHANNEL_PACCH
|
||||||
|
|| chan_type == GSMTAP_CHANNEL_PDCH) {
|
||||||
|
if (data_ind->msgUnitParam.u8Buffer[0]
|
||||||
|
!= GsmL1_PdtchPlType_Full)
|
||||||
|
return;
|
||||||
|
skip = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
gsmtap_send(fl1h->gsmtap, trx->arfcn | GSMTAP_ARFCN_F_UPLINK,
|
||||||
|
data_ind->u8Tn, chan_type, ss, data_ind->u32Fn,
|
||||||
|
0, 0, data_ind->msgUnitParam.u8Buffer + skip,
|
||||||
|
data_ind->msgUnitParam.u8Size - skip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct wait_l1_conf {
|
struct wait_l1_conf {
|
||||||
struct llist_head list; /* internal linked list */
|
struct llist_head list; /* internal linked list */
|
||||||
struct osmo_timer_list timer; /* timer for L1 timeout */
|
struct osmo_timer_list timer; /* timer for L1 timeout */
|
||||||
@@ -116,7 +212,7 @@ int l1if_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
|
|||||||
wqueue = &fl1h->write_q[MQ_L1_WRITE];
|
wqueue = &fl1h->write_q[MQ_L1_WRITE];
|
||||||
timeout_secs = 30;
|
timeout_secs = 30;
|
||||||
} else {
|
} else {
|
||||||
FemtoBts_Prim_t *sysp = msgb_sysprim(msg);
|
SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
|
||||||
|
|
||||||
LOGP(DL1C, LOGL_INFO, "Tx SYS prim %s\n",
|
LOGP(DL1C, LOGL_INFO, "Tx SYS prim %s\n",
|
||||||
get_value_string(femtobts_sysprim_names, sysp->id));
|
get_value_string(femtobts_sysprim_names, sysp->id));
|
||||||
@@ -156,13 +252,13 @@ struct msgb *l1p_msgb_alloc(void)
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* allocate a msgb containing a FemtoBts_Prim_t */
|
/* allocate a msgb containing a SuperFemto_Prim_t */
|
||||||
struct msgb *sysp_msgb_alloc(void)
|
struct msgb *sysp_msgb_alloc(void)
|
||||||
{
|
{
|
||||||
struct msgb *msg = msgb_alloc(sizeof(FemtoBts_Prim_t), "sys_prim");
|
struct msgb *msg = msgb_alloc(sizeof(SuperFemto_Prim_t), "sys_prim");
|
||||||
|
|
||||||
if (msg)
|
if (msg)
|
||||||
msg->l1h = msgb_put(msg, sizeof(FemtoBts_Prim_t));
|
msg->l1h = msgb_put(msg, sizeof(SuperFemto_Prim_t));
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
@@ -217,8 +313,41 @@ get_lapdm_chan_by_hl2(struct gsm_bts_trx *trx, uint32_t hLayer2)
|
|||||||
return &lchan->lapdm_ch;
|
return &lchan->lapdm_ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* check if the message is a GSM48_MT_RR_CIPH_M_CMD, and if yes, enable
|
||||||
|
* uni-directional de-cryption on the uplink. We need this ugly layering
|
||||||
|
* violation as we have no way of passing down L3 metadata (RSL CIPHERING CMD)
|
||||||
|
* to this point in L1 */
|
||||||
|
static int check_for_ciph_cmd(struct femtol1_hdl *fl1h,
|
||||||
|
struct msgb *msg, struct gsm_lchan *lchan)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* only do this if we are in the right state */
|
||||||
|
switch (lchan->ciph_state) {
|
||||||
|
case LCHAN_CIPH_NONE:
|
||||||
|
case LCHAN_CIPH_RX_REQ:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* First byte (Address Field) of LAPDm header) */
|
||||||
|
if (msg->data[0] != 0x03)
|
||||||
|
return 0;
|
||||||
|
/* First byte (protocol discriminator) of RR */
|
||||||
|
if ((msg->data[3] & 0xF) != GSM48_PDISC_RR)
|
||||||
|
return 0;
|
||||||
|
/* 2nd byte (msg type) of RR */
|
||||||
|
if ((msg->data[4] & 0x3F) != GSM48_MT_RR_CIPH_M_CMD)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
lchan->ciph_state = LCHAN_CIPH_RX_REQ;
|
||||||
|
l1if_set_ciphering(fl1h, lchan, 0);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = {
|
static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = {
|
||||||
0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
|
0x01, 0x03, 0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
|
||||||
0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
|
0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
|
||||||
0x2B, 0x2B, 0x2B
|
0x2B, 0x2B, 0x2B
|
||||||
};
|
};
|
||||||
@@ -232,7 +361,6 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
|
|||||||
struct msgb *resp_msg;
|
struct msgb *resp_msg;
|
||||||
GsmL1_PhDataReq_t *data_req;
|
GsmL1_PhDataReq_t *data_req;
|
||||||
GsmL1_MsgUnitParam_t *msu_param;
|
GsmL1_MsgUnitParam_t *msu_param;
|
||||||
struct lapdm_channel *lc;
|
|
||||||
struct lapdm_entity *le;
|
struct lapdm_entity *le;
|
||||||
struct gsm_lchan *lchan;
|
struct gsm_lchan *lchan;
|
||||||
struct gsm_time g_time;
|
struct gsm_time g_time;
|
||||||
@@ -259,7 +387,7 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
|
|||||||
if (!lchan)
|
if (!lchan)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (lchan->abis_ip.rtp_socket) {
|
if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
|
||||||
osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
|
osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
|
||||||
/* FIXME: we _assume_ that we never miss TDMA
|
/* FIXME: we _assume_ that we never miss TDMA
|
||||||
* frames and that we always get to this point
|
* frames and that we always get to this point
|
||||||
@@ -287,6 +415,13 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
|
|||||||
/* actually transmit it */
|
/* actually transmit it */
|
||||||
goto tx;
|
goto tx;
|
||||||
break;
|
break;
|
||||||
|
case GsmL1_Sapi_Pdtch:
|
||||||
|
case GsmL1_Sapi_Pacch:
|
||||||
|
return pcu_tx_rts_req(&trx->ts[rts_ind->u8Tn], 0,
|
||||||
|
rts_ind->u32Fn, rts_ind->u16Arfcn, rts_ind->u8BlockNbr);
|
||||||
|
case GsmL1_Sapi_Ptcch:
|
||||||
|
return pcu_tx_rts_req(&trx->ts[rts_ind->u8Tn], 1,
|
||||||
|
rts_ind->u32Fn, rts_ind->u16Arfcn, rts_ind->u8BlockNbr);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -340,13 +475,15 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
|
|||||||
break;
|
break;
|
||||||
case GsmL1_Sapi_Sdcch:
|
case GsmL1_Sapi_Sdcch:
|
||||||
/* resolve the L2 entity using rts_ind->hLayer2 */
|
/* resolve the L2 entity using rts_ind->hLayer2 */
|
||||||
lc = get_lapdm_chan_by_hl2(trx, rts_ind->hLayer2);
|
lchan = l1if_hLayer2_to_lchan(trx, rts_ind->hLayer2);
|
||||||
le = &lc->lapdm_dcch;
|
le = &lchan->lapdm_ch.lapdm_dcch;
|
||||||
rc = lapdm_phsap_dequeue_prim(le, &pp);
|
rc = lapdm_phsap_dequeue_prim(le, &pp);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
|
memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
|
||||||
else {
|
else {
|
||||||
memcpy(msu_param->u8Buffer, pp.oph.msg->data, GSM_MACBLOCK_LEN);
|
memcpy(msu_param->u8Buffer, pp.oph.msg->data, GSM_MACBLOCK_LEN);
|
||||||
|
/* check if it is a RR CIPH MODE CMD. if yes, enable RX ciphering */
|
||||||
|
check_for_ciph_cmd(fl1, pp.oph.msg, lchan);
|
||||||
msgb_free(pp.oph.msg);
|
msgb_free(pp.oph.msg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -374,22 +511,29 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
|
|||||||
case GsmL1_Sapi_FacchF:
|
case GsmL1_Sapi_FacchF:
|
||||||
case GsmL1_Sapi_FacchH:
|
case GsmL1_Sapi_FacchH:
|
||||||
/* resolve the L2 entity using rts_ind->hLayer2 */
|
/* resolve the L2 entity using rts_ind->hLayer2 */
|
||||||
lc = get_lapdm_chan_by_hl2(trx, rts_ind->hLayer2);
|
lchan = l1if_hLayer2_to_lchan(trx, rts_ind->hLayer2);
|
||||||
le = &lc->lapdm_dcch;
|
le = &lchan->lapdm_ch.lapdm_dcch;
|
||||||
rc = lapdm_phsap_dequeue_prim(le, &pp);
|
rc = lapdm_phsap_dequeue_prim(le, &pp);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto empty_frame;
|
goto empty_frame;
|
||||||
else {
|
else {
|
||||||
memcpy(msu_param->u8Buffer, pp.oph.msg->data, GSM_MACBLOCK_LEN);
|
memcpy(msu_param->u8Buffer, pp.oph.msg->data, GSM_MACBLOCK_LEN);
|
||||||
|
/* check if it is a RR CIPH MODE CMD. if yes, enable RX ciphering */
|
||||||
|
check_for_ciph_cmd(fl1, pp.oph.msg, lchan);
|
||||||
msgb_free(pp.oph.msg);
|
msgb_free(pp.oph.msg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* we should never receive a request here */
|
case GsmL1_Sapi_Prach:
|
||||||
|
goto empty_frame;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
|
memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tx:
|
tx:
|
||||||
|
|
||||||
|
tx_to_gsmtap(fl1, resp_msg);
|
||||||
|
|
||||||
/* transmit */
|
/* transmit */
|
||||||
osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], resp_msg);
|
osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], resp_msg);
|
||||||
|
|
||||||
@@ -405,6 +549,15 @@ empty_frame:
|
|||||||
static int handle_mph_time_ind(struct femtol1_hdl *fl1,
|
static int handle_mph_time_ind(struct femtol1_hdl *fl1,
|
||||||
GsmL1_MphTimeInd_t *time_ind)
|
GsmL1_MphTimeInd_t *time_ind)
|
||||||
{
|
{
|
||||||
|
struct gsm_bts_trx *trx = fl1->priv;
|
||||||
|
struct gsm_bts *bts = trx->bts;
|
||||||
|
struct gsm_bts_role_bts *btsb = bts->role;
|
||||||
|
|
||||||
|
int frames_expired = time_ind->u32Fn - fl1->gsm_time.fn;
|
||||||
|
|
||||||
|
/* update time on PCU interface */
|
||||||
|
pcu_tx_time_ind(time_ind->u32Fn);
|
||||||
|
|
||||||
/* Update our data structures with the current GSM time */
|
/* Update our data structures with the current GSM time */
|
||||||
gsm_fn2gsmtime(&fl1->gsm_time, time_ind->u32Fn);
|
gsm_fn2gsmtime(&fl1->gsm_time, time_ind->u32Fn);
|
||||||
|
|
||||||
@@ -415,6 +568,19 @@ static int handle_mph_time_ind(struct femtol1_hdl *fl1,
|
|||||||
/* increment the primitive count for the alive timer */
|
/* increment the primitive count for the alive timer */
|
||||||
fl1->alive_prim_cnt++;
|
fl1->alive_prim_cnt++;
|
||||||
|
|
||||||
|
/* increment number of RACH slots that have passed by since the
|
||||||
|
* last time indication */
|
||||||
|
if (trx == bts->c0) {
|
||||||
|
unsigned int num_rach_per_frame;
|
||||||
|
/* 27 / 51 taken from TS 05.01 Figure 3 */
|
||||||
|
if (bts->c0->ts[0].pchan == GSM_PCHAN_CCCH_SDCCH4)
|
||||||
|
num_rach_per_frame = 27;
|
||||||
|
else
|
||||||
|
num_rach_per_frame = 51;
|
||||||
|
|
||||||
|
btsb->load.rach.total += frames_expired * num_rach_per_frame;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,6 +616,11 @@ static int process_meas_res(struct gsm_lchan *lchan, GsmL1_MeasParam_t *m)
|
|||||||
{
|
{
|
||||||
struct bts_ul_meas ulm;
|
struct bts_ul_meas ulm;
|
||||||
|
|
||||||
|
/* in the GPRS case we are not interested in measurement
|
||||||
|
* processing. The PCU will take care of it */
|
||||||
|
if (lchan->type == GSM_LCHAN_PDTCH)
|
||||||
|
return 0;
|
||||||
|
|
||||||
ulm.ta_offs_qbits = m->i16BurstTiming;
|
ulm.ta_offs_qbits = m->i16BurstTiming;
|
||||||
ulm.ber10k = (unsigned int) (m->fBer * 100);
|
ulm.ber10k = (unsigned int) (m->fBer * 100);
|
||||||
ulm.inv_rssi = (uint8_t) (m->fRssi * -1);
|
ulm.inv_rssi = (uint8_t) (m->fRssi * -1);
|
||||||
@@ -460,11 +631,14 @@ static int process_meas_res(struct gsm_lchan *lchan, GsmL1_MeasParam_t *m)
|
|||||||
static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_ind,
|
static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_ind,
|
||||||
struct msgb *l1p_msg)
|
struct msgb *l1p_msg)
|
||||||
{
|
{
|
||||||
|
struct gsm_bts_trx *trx = fl1->priv;
|
||||||
struct osmo_phsap_prim pp;
|
struct osmo_phsap_prim pp;
|
||||||
struct gsm_lchan *lchan;
|
struct gsm_lchan *lchan;
|
||||||
struct lapdm_entity *le;
|
struct lapdm_entity *le;
|
||||||
struct msgb *msg;
|
struct msgb *msg;
|
||||||
int rc;
|
int rc = 0;
|
||||||
|
|
||||||
|
ul_to_gsmtap(fl1, l1p_msg);
|
||||||
|
|
||||||
lchan = l1if_hLayer2_to_lchan(fl1->priv, data_ind->hLayer2);
|
lchan = l1if_hLayer2_to_lchan(fl1->priv, data_ind->hLayer2);
|
||||||
if (!lchan) {
|
if (!lchan) {
|
||||||
@@ -496,6 +670,19 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
|
|||||||
case GsmL1_Sapi_Sdcch:
|
case GsmL1_Sapi_Sdcch:
|
||||||
case GsmL1_Sapi_FacchF:
|
case GsmL1_Sapi_FacchF:
|
||||||
case GsmL1_Sapi_FacchH:
|
case GsmL1_Sapi_FacchH:
|
||||||
|
/* if this is the first valid message after enabling Rx
|
||||||
|
* decryption, we have to enable Tx encryption */
|
||||||
|
if (lchan->ciph_state == LCHAN_CIPH_RX_CONF) {
|
||||||
|
/* HACK: check if it's an I frame, in order to
|
||||||
|
* ignore some still buffered/queued UI frames received
|
||||||
|
* before decryption was enabled */
|
||||||
|
if (data_ind->msgUnitParam.u8Buffer[0] == 0x01 &&
|
||||||
|
(data_ind->msgUnitParam.u8Buffer[1] & 0x01) == 0) {
|
||||||
|
l1if_set_ciphering(fl1, lchan, 1);
|
||||||
|
lchan->ciph_state = LCHAN_CIPH_TXRX_REQ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* SDCCH, SACCH and FACCH all go to LAPDm */
|
/* SDCCH, SACCH and FACCH all go to LAPDm */
|
||||||
le = le_by_l1_sapi(&lchan->lapdm_ch, data_ind->sapi);
|
le = le_by_l1_sapi(&lchan->lapdm_ch, data_ind->sapi);
|
||||||
/* allocate and fill LAPDm primitive */
|
/* allocate and fill LAPDm primitive */
|
||||||
@@ -520,6 +707,27 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
|
|||||||
/* TCH speech frame handling */
|
/* TCH speech frame handling */
|
||||||
rc = l1if_tch_rx(lchan, l1p_msg);
|
rc = l1if_tch_rx(lchan, l1p_msg);
|
||||||
break;
|
break;
|
||||||
|
case GsmL1_Sapi_Pdtch:
|
||||||
|
case GsmL1_Sapi_Pacch:
|
||||||
|
/* drop incomplete UL block */
|
||||||
|
if (data_ind->msgUnitParam.u8Buffer[0]
|
||||||
|
!= GsmL1_PdtchPlType_Full)
|
||||||
|
break;
|
||||||
|
/* PDTCH / PACCH frame handling */
|
||||||
|
rc = pcu_tx_data_ind(&trx->ts[data_ind->u8Tn], 0,
|
||||||
|
data_ind->u32Fn, data_ind->u16Arfcn,
|
||||||
|
data_ind->u8BlockNbr,
|
||||||
|
data_ind->msgUnitParam.u8Buffer + 1,
|
||||||
|
data_ind->msgUnitParam.u8Size - 1);
|
||||||
|
break;
|
||||||
|
case GsmL1_Sapi_Ptcch:
|
||||||
|
/* PTCCH frame handling */
|
||||||
|
rc = pcu_tx_data_ind(&trx->ts[data_ind->u8Tn], 1,
|
||||||
|
data_ind->u32Fn, data_ind->u16Arfcn,
|
||||||
|
data_ind->u8BlockNbr,
|
||||||
|
data_ind->msgUnitParam.u8Buffer,
|
||||||
|
data_ind->msgUnitParam.u8Size);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOGP(DL1C, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n",
|
LOGP(DL1C, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n",
|
||||||
get_value_string(femtobts_l1sapi_names, data_ind->sapi));
|
get_value_string(femtobts_l1sapi_names, data_ind->sapi));
|
||||||
@@ -532,12 +740,25 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
|
|||||||
|
|
||||||
static int handle_ph_ra_ind(struct femtol1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind)
|
static int handle_ph_ra_ind(struct femtol1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind)
|
||||||
{
|
{
|
||||||
|
struct gsm_bts_trx *trx = fl1->priv;
|
||||||
|
struct gsm_bts *bts = trx->bts;
|
||||||
|
struct gsm_bts_role_bts *btsb = bts->role;
|
||||||
struct osmo_phsap_prim pp;
|
struct osmo_phsap_prim pp;
|
||||||
struct lapdm_channel *lc;
|
struct lapdm_channel *lc;
|
||||||
|
uint8_t acc_delay;
|
||||||
|
|
||||||
|
/* increment number of busy RACH slots, if required */
|
||||||
|
if (trx == bts->c0 &&
|
||||||
|
ra_ind->measParam.fRssi >= btsb->load.rach.busy_thresh)
|
||||||
|
btsb->load.rach.busy++;
|
||||||
|
|
||||||
if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
|
if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/* increment number of RACH slots with valid RACH burst */
|
||||||
|
if (trx == bts->c0)
|
||||||
|
btsb->load.rach.access++;
|
||||||
|
|
||||||
DEBUGP(DL1C, "Rx PH-RA.ind");
|
DEBUGP(DL1C, "Rx PH-RA.ind");
|
||||||
dump_meas_res(&ra_ind->measParam);
|
dump_meas_res(&ra_ind->measParam);
|
||||||
|
|
||||||
@@ -547,17 +768,31 @@ static int handle_ph_ra_ind(struct femtol1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind)
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* check for under/overflow / sign */
|
||||||
|
if (ra_ind->measParam.i16BurstTiming < 0)
|
||||||
|
acc_delay = 0;
|
||||||
|
else
|
||||||
|
acc_delay = ra_ind->measParam.i16BurstTiming >> 2;
|
||||||
|
if (acc_delay > btsb->max_ta) {
|
||||||
|
LOGP(DL1C, LOGL_INFO, "ignoring RACH request %u > max_ta(%u)\n",
|
||||||
|
acc_delay, btsb->max_ta);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for packet access */
|
||||||
|
if (trx == bts->c0
|
||||||
|
&& (ra_ind->msgUnitParam.u8Buffer[0] & 0xf0) == 0x70) {
|
||||||
|
LOGP(DL1C, LOGL_INFO, "RACH for packet access\n");
|
||||||
|
return pcu_tx_rach_ind(bts, ra_ind->measParam.i16BurstTiming,
|
||||||
|
ra_ind->msgUnitParam.u8Buffer[0], ra_ind->u32Fn);
|
||||||
|
}
|
||||||
|
|
||||||
osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_RACH,
|
osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_RACH,
|
||||||
PRIM_OP_INDICATION, NULL);
|
PRIM_OP_INDICATION, NULL);
|
||||||
|
|
||||||
pp.u.rach_ind.ra = ra_ind->msgUnitParam.u8Buffer[0];
|
pp.u.rach_ind.ra = ra_ind->msgUnitParam.u8Buffer[0];
|
||||||
pp.u.rach_ind.fn = ra_ind->u32Fn;
|
pp.u.rach_ind.fn = ra_ind->u32Fn;
|
||||||
/* FIXME: check for under/overflow / sign */
|
pp.u.rach_ind.acc_delay = acc_delay;
|
||||||
if (ra_ind->measParam.i16BurstTiming <= 0 ||
|
|
||||||
ra_ind->measParam.i16BurstTiming > 63 * 4)
|
|
||||||
pp.u.rach_ind.acc_delay = 0;
|
|
||||||
else
|
|
||||||
pp.u.rach_ind.acc_delay = ra_ind->measParam.i16BurstTiming >> 2;
|
|
||||||
|
|
||||||
return lapdm_phsap_up(&pp.oph, &lc->lapdm_dcch);
|
return lapdm_phsap_up(&pp.oph, &lc->lapdm_dcch);
|
||||||
}
|
}
|
||||||
@@ -596,7 +831,7 @@ static int l1if_handle_ind(struct femtol1_hdl *fl1, struct msgb *msg)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg)
|
int l1if_handle_l1prim(int wq, struct femtol1_hdl *fl1h, struct msgb *msg)
|
||||||
{
|
{
|
||||||
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
|
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
|
||||||
struct wait_l1_conf *wlc;
|
struct wait_l1_conf *wlc;
|
||||||
@@ -607,8 +842,8 @@ int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg)
|
|||||||
/* silent, don't clog the log file */
|
/* silent, don't clog the log file */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOGP(DL1P, LOGL_DEBUG, "Rx L1 prim %s\n",
|
LOGP(DL1P, LOGL_DEBUG, "Rx L1 prim %s on queue %d\n",
|
||||||
get_value_string(femtobts_l1prim_names, l1p->id));
|
get_value_string(femtobts_l1prim_names, l1p->id), wq);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if this is a resposne to a sync-waiting request */
|
/* check if this is a resposne to a sync-waiting request */
|
||||||
@@ -617,7 +852,10 @@ int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg)
|
|||||||
* sending the same primitive */
|
* sending the same primitive */
|
||||||
if (wlc->is_sys_prim == 0 && l1p->id == wlc->conf_prim_id) {
|
if (wlc->is_sys_prim == 0 && l1p->id == wlc->conf_prim_id) {
|
||||||
llist_del(&wlc->list);
|
llist_del(&wlc->list);
|
||||||
rc = wlc->cb(msg, wlc->cb_data);
|
if (wlc->cb)
|
||||||
|
rc = wlc->cb(msg, wlc->cb_data);
|
||||||
|
else
|
||||||
|
rc = 0;
|
||||||
release_wlc(wlc);
|
release_wlc(wlc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -629,7 +867,7 @@ int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg)
|
|||||||
|
|
||||||
int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg)
|
int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg)
|
||||||
{
|
{
|
||||||
FemtoBts_Prim_t *sysp = msgb_sysprim(msg);
|
SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
|
||||||
struct wait_l1_conf *wlc;
|
struct wait_l1_conf *wlc;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@@ -642,7 +880,10 @@ int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg)
|
|||||||
* sending the same primitive */
|
* sending the same primitive */
|
||||||
if (wlc->is_sys_prim && sysp->id == wlc->conf_prim_id) {
|
if (wlc->is_sys_prim && sysp->id == wlc->conf_prim_id) {
|
||||||
llist_del(&wlc->list);
|
llist_del(&wlc->list);
|
||||||
rc = wlc->cb(msg, wlc->cb_data);
|
if (wlc->cb)
|
||||||
|
rc = wlc->cb(msg, wlc->cb_data);
|
||||||
|
else
|
||||||
|
rc = 0;
|
||||||
release_wlc(wlc);
|
release_wlc(wlc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -669,14 +910,14 @@ int sysinfo_has_changed(struct gsm_bts *bts, int si)
|
|||||||
|
|
||||||
static int activate_rf_compl_cb(struct msgb *resp, void *data)
|
static int activate_rf_compl_cb(struct msgb *resp, void *data)
|
||||||
{
|
{
|
||||||
FemtoBts_Prim_t *sysp = msgb_sysprim(resp);
|
SuperFemto_Prim_t *sysp = msgb_sysprim(resp);
|
||||||
struct femtol1_hdl *fl1h = data;
|
struct femtol1_hdl *fl1h = data;
|
||||||
struct gsm_bts_trx *trx = fl1h->priv;
|
struct gsm_bts_trx *trx = fl1h->priv;
|
||||||
GsmL1_Status_t status;
|
GsmL1_Status_t status;
|
||||||
int on = 0;
|
int on = 0;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
if (sysp->id == FemtoBts_PrimId_ActivateRfCnf)
|
if (sysp->id == SuperFemto_PrimId_ActivateRfCnf)
|
||||||
on = 1;
|
on = 1;
|
||||||
|
|
||||||
if (on)
|
if (on)
|
||||||
@@ -693,7 +934,9 @@ static int activate_rf_compl_cb(struct msgb *resp, void *data)
|
|||||||
LOGP(DL1C, LOGL_FATAL, "RF-ACT.conf with status %s\n",
|
LOGP(DL1C, LOGL_FATAL, "RF-ACT.conf with status %s\n",
|
||||||
get_value_string(femtobts_l1status_names, status));
|
get_value_string(femtobts_l1status_names, status));
|
||||||
bts_shutdown(trx->bts, "RF-ACT failure");
|
bts_shutdown(trx->bts, "RF-ACT failure");
|
||||||
}
|
} else
|
||||||
|
sysmobts_led_set(LED_RF_ACTIVE, 1);
|
||||||
|
|
||||||
/* signal availability */
|
/* signal availability */
|
||||||
oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OK);
|
oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OK);
|
||||||
oml_mo_tx_sw_act_rep(&trx->mo);
|
oml_mo_tx_sw_act_rep(&trx->mo);
|
||||||
@@ -703,6 +946,7 @@ static int activate_rf_compl_cb(struct msgb *resp, void *data)
|
|||||||
for (i = 0; i < ARRAY_SIZE(trx->ts); i++)
|
for (i = 0; i < ARRAY_SIZE(trx->ts); i++)
|
||||||
oml_mo_state_chg(&trx->ts[i].mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY);
|
oml_mo_state_chg(&trx->ts[i].mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY);
|
||||||
} else {
|
} else {
|
||||||
|
sysmobts_led_set(LED_RF_ACTIVE, 0);
|
||||||
oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
|
oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
|
||||||
oml_mo_state_chg(&trx->bb_transc.mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
|
oml_mo_state_chg(&trx->bb_transc.mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
|
||||||
}
|
}
|
||||||
@@ -716,23 +960,99 @@ static int activate_rf_compl_cb(struct msgb *resp, void *data)
|
|||||||
int l1if_activate_rf(struct femtol1_hdl *hdl, int on)
|
int l1if_activate_rf(struct femtol1_hdl *hdl, int on)
|
||||||
{
|
{
|
||||||
struct msgb *msg = sysp_msgb_alloc();
|
struct msgb *msg = sysp_msgb_alloc();
|
||||||
FemtoBts_Prim_t *sysp = msgb_sysprim(msg);
|
SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
|
||||||
|
|
||||||
if (on) {
|
if (on) {
|
||||||
sysp->id = FemtoBts_PrimId_ActivateRfReq;
|
sysp->id = SuperFemto_PrimId_ActivateRfReq;
|
||||||
sysp->u.activateRfReq.u12ClkVc = 0xFFFF;
|
#ifdef HW_SYSMOBTS_V1
|
||||||
|
sysp->u.activateRfReq.u12ClkVc = hdl->clk_cal;
|
||||||
|
#else
|
||||||
|
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(0,2,0)
|
||||||
|
sysp->u.activateRfReq.timing.u8TimSrc = 1; /* Master */
|
||||||
|
#endif /* 0.2.0 */
|
||||||
|
sysp->u.activateRfReq.msgq.u8UseTchMsgq = 0;
|
||||||
|
sysp->u.activateRfReq.msgq.u8UsePdtchMsgq = pcu_direct;
|
||||||
|
/* Use clock from OCXO or whatever source is configured */
|
||||||
|
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,1,0)
|
||||||
|
sysp->u.activateRfReq.rfTrx.u8ClkSrc = hdl->clk_src;
|
||||||
|
#else
|
||||||
|
sysp->u.activateRfReq.rfTrx.clkSrc = hdl->clk_src;
|
||||||
|
#endif /* 2.1.0 */
|
||||||
|
sysp->u.activateRfReq.rfTrx.iClkCor = hdl->clk_cal;
|
||||||
|
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
|
||||||
|
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,1,0)
|
||||||
|
sysp->u.activateRfReq.rfRx.u8ClkSrc = hdl->clk_src;
|
||||||
|
#else
|
||||||
|
sysp->u.activateRfReq.rfRx.clkSrc = hdl->clk_src;
|
||||||
|
#endif /* 2.1.0 */
|
||||||
|
sysp->u.activateRfReq.rfRx.iClkCor = hdl->clk_cal;
|
||||||
|
#endif /* API 2.4.0 */
|
||||||
|
#endif /* !HW_SYSMOBTS_V1 */
|
||||||
} else {
|
} else {
|
||||||
sysp->id = FemtoBts_PrimId_DeactivateRfReq;
|
sysp->id = SuperFemto_PrimId_DeactivateRfReq;
|
||||||
}
|
}
|
||||||
|
|
||||||
return l1if_req_compl(hdl, msg, 1, activate_rf_compl_cb, hdl);
|
return l1if_req_compl(hdl, msg, 1, activate_rf_compl_cb, hdl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* call-back on arrival of DSP+FPGA version + band capability */
|
||||||
|
static int info_compl_cb(struct msgb *resp, void *data)
|
||||||
|
{
|
||||||
|
SuperFemto_Prim_t *sysp = msgb_sysprim(resp);
|
||||||
|
SuperFemto_SystemInfoCnf_t *sic = &sysp->u.systemInfoCnf;
|
||||||
|
struct femtol1_hdl *fl1h = data;
|
||||||
|
struct gsm_bts_trx *trx = fl1h->priv;
|
||||||
|
|
||||||
|
fl1h->hw_info.dsp_version[0] = sic->dspVersion.major;
|
||||||
|
fl1h->hw_info.dsp_version[1] = sic->dspVersion.minor;
|
||||||
|
fl1h->hw_info.dsp_version[2] = sic->dspVersion.build;
|
||||||
|
|
||||||
|
fl1h->hw_info.fpga_version[0] = sic->fpgaVersion.major;
|
||||||
|
fl1h->hw_info.fpga_version[1] = sic->fpgaVersion.minor;
|
||||||
|
fl1h->hw_info.fpga_version[2] = sic->fpgaVersion.build;
|
||||||
|
|
||||||
|
LOGP(DL1C, LOGL_INFO, "DSP v%u.%u.%u, FPGA v%u.%u.%u\n",
|
||||||
|
sic->dspVersion.major, sic->dspVersion.minor,
|
||||||
|
sic->dspVersion.build, sic->fpgaVersion.major,
|
||||||
|
sic->fpgaVersion.minor, sic->fpgaVersion.build);
|
||||||
|
|
||||||
|
#ifdef HW_SYSMOBTS_V1
|
||||||
|
if (sic->rfBand.gsm850)
|
||||||
|
fl1h->hw_info.band_support |= GSM_BAND_850;
|
||||||
|
if (sic->rfBand.gsm900)
|
||||||
|
fl1h->hw_info.band_support |= GSM_BAND_900;
|
||||||
|
if (sic->rfBand.dcs1800)
|
||||||
|
fl1h->hw_info.band_support |= GSM_BAND_1800;
|
||||||
|
if (sic->rfBand.pcs1900)
|
||||||
|
fl1h->hw_info.band_support |= GSM_BAND_1900;
|
||||||
|
#else
|
||||||
|
fl1h->hw_info.band_support |= GSM_BAND_850 | GSM_BAND_900 | GSM_BAND_1800 | GSM_BAND_1900;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!(fl1h->hw_info.band_support & trx->bts->band))
|
||||||
|
LOGP(DL1C, LOGL_FATAL, "BTS band %s not supported by hw\n",
|
||||||
|
gsm_band_name(trx->bts->band));
|
||||||
|
|
||||||
|
/* FIXME: clock related */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* request DSP+FPGA code versions + band capability */
|
||||||
|
static int l1if_get_info(struct femtol1_hdl *hdl)
|
||||||
|
{
|
||||||
|
struct msgb *msg = sysp_msgb_alloc();
|
||||||
|
SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
|
||||||
|
|
||||||
|
sysp->id = SuperFemto_PrimId_SystemInfoReq;
|
||||||
|
|
||||||
|
return l1if_req_compl(hdl, msg, 1, info_compl_cb, hdl);
|
||||||
|
}
|
||||||
|
|
||||||
static int reset_compl_cb(struct msgb *resp, void *data)
|
static int reset_compl_cb(struct msgb *resp, void *data)
|
||||||
{
|
{
|
||||||
struct femtol1_hdl *fl1h = data;
|
struct femtol1_hdl *fl1h = data;
|
||||||
struct gsm_bts_trx *trx = fl1h->priv;
|
struct gsm_bts_trx *trx = fl1h->priv;
|
||||||
FemtoBts_Prim_t *sysp = msgb_sysprim(resp);
|
SuperFemto_Prim_t *sysp = msgb_sysprim(resp);
|
||||||
GsmL1_Status_t status = sysp->u.layer1ResetCnf.status;
|
GsmL1_Status_t status = sysp->u.layer1ResetCnf.status;
|
||||||
|
|
||||||
LOGP(DL1C, LOGL_NOTICE, "Rx L1-RESET.conf (status=%s)\n",
|
LOGP(DL1C, LOGL_NOTICE, "Rx L1-RESET.conf (status=%s)\n",
|
||||||
@@ -751,6 +1071,17 @@ static int reset_compl_cb(struct msgb *resp, void *data)
|
|||||||
* set them to zero (or whatever dsp_trace_f has been initialized to */
|
* set them to zero (or whatever dsp_trace_f has been initialized to */
|
||||||
l1if_set_trace_flags(fl1h, fl1h->dsp_trace_f);
|
l1if_set_trace_flags(fl1h, fl1h->dsp_trace_f);
|
||||||
|
|
||||||
|
/* obtain version information on DSP/FPGA and band capabilities */
|
||||||
|
l1if_get_info(fl1h);
|
||||||
|
|
||||||
|
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
|
||||||
|
/* load calibration tables (if we know their path) */
|
||||||
|
if (fl1h->calib_path)
|
||||||
|
calib_load(fl1h);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
LOGP(DL1C, LOGL_NOTICE, "Operating without calibration tables!\n");
|
||||||
|
|
||||||
/* otherwise, request activation of RF board */
|
/* otherwise, request activation of RF board */
|
||||||
l1if_activate_rf(fl1h, 1);
|
l1if_activate_rf(fl1h, 1);
|
||||||
|
|
||||||
@@ -760,8 +1091,8 @@ static int reset_compl_cb(struct msgb *resp, void *data)
|
|||||||
int l1if_reset(struct femtol1_hdl *hdl)
|
int l1if_reset(struct femtol1_hdl *hdl)
|
||||||
{
|
{
|
||||||
struct msgb *msg = sysp_msgb_alloc();
|
struct msgb *msg = sysp_msgb_alloc();
|
||||||
FemtoBts_Prim_t *sysp = msgb_sysprim(msg);
|
SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
|
||||||
sysp->id = FemtoBts_PrimId_Layer1ResetReq;
|
sysp->id = SuperFemto_PrimId_Layer1ResetReq;
|
||||||
|
|
||||||
return l1if_req_compl(hdl, msg, 1, reset_compl_cb, hdl);
|
return l1if_req_compl(hdl, msg, 1, reset_compl_cb, hdl);
|
||||||
}
|
}
|
||||||
@@ -770,12 +1101,12 @@ int l1if_reset(struct femtol1_hdl *hdl)
|
|||||||
int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags)
|
int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags)
|
||||||
{
|
{
|
||||||
struct msgb *msg = sysp_msgb_alloc();
|
struct msgb *msg = sysp_msgb_alloc();
|
||||||
FemtoBts_Prim_t *sysp = msgb_sysprim(msg);
|
SuperFemto_Prim_t *sysp = msgb_sysprim(msg);
|
||||||
|
|
||||||
LOGP(DL1C, LOGL_INFO, "Tx SET-TRACE-FLAGS.req (0x%08x)\n",
|
LOGP(DL1C, LOGL_INFO, "Tx SET-TRACE-FLAGS.req (0x%08x)\n",
|
||||||
flags);
|
flags);
|
||||||
|
|
||||||
sysp->id = FemtoBts_PrimId_SetTraceFlagsReq;
|
sysp->id = SuperFemto_PrimId_SetTraceFlagsReq;
|
||||||
sysp->u.setTraceFlagsReq.u32Tf = flags;
|
sysp->u.setTraceFlagsReq.u32Tf = flags;
|
||||||
|
|
||||||
hdl->dsp_trace_f = flags;
|
hdl->dsp_trace_f = flags;
|
||||||
@@ -784,28 +1115,93 @@ int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags)
|
|||||||
return osmo_wqueue_enqueue(&hdl->write_q[MQ_SYS_WRITE], msg);
|
return osmo_wqueue_enqueue(&hdl->write_q[MQ_SYS_WRITE], msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* send packet data request to L1 */
|
||||||
|
int l1if_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
|
||||||
|
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
|
||||||
|
{
|
||||||
|
struct gsm_bts_trx *trx = ts->trx;
|
||||||
|
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||||
|
struct msgb *msg;
|
||||||
|
GsmL1_Prim_t *l1p;
|
||||||
|
GsmL1_PhDataReq_t *data_req;
|
||||||
|
GsmL1_MsgUnitParam_t *msu_param;
|
||||||
|
struct gsm_time g_time;
|
||||||
|
|
||||||
|
gsm_fn2gsmtime(&g_time, fn);
|
||||||
|
|
||||||
|
DEBUGP(DL1P, "TX packet data %02u/%02u/%02u is_ptcch=%d trx=%d ts=%d "
|
||||||
|
"block_nr=%d, arfcn=%d, len=%d\n", g_time.t1, g_time.t2,
|
||||||
|
g_time.t3, is_ptcch, ts->trx->nr, ts->nr, block_nr, arfcn, len);
|
||||||
|
|
||||||
|
msg = l1p_msgb_alloc();
|
||||||
|
l1p = msgb_l1prim(msg);
|
||||||
|
l1p->id = GsmL1_PrimId_PhDataReq;
|
||||||
|
data_req = &l1p->u.phDataReq;
|
||||||
|
data_req->hLayer1 = fl1h->hLayer1;
|
||||||
|
data_req->sapi = (is_ptcch) ? GsmL1_Sapi_Ptcch : GsmL1_Sapi_Pdtch;
|
||||||
|
data_req->subCh = GsmL1_SubCh_NA;
|
||||||
|
data_req->u8BlockNbr = block_nr;
|
||||||
|
data_req->u8Tn = ts->nr;
|
||||||
|
data_req->u32Fn = fn;
|
||||||
|
msu_param = &data_req->msgUnitParam;
|
||||||
|
msu_param->u8Size = len;
|
||||||
|
memcpy(msu_param->u8Buffer, data, len);
|
||||||
|
|
||||||
|
tx_to_gsmtap(fl1h, msg);
|
||||||
|
|
||||||
|
/* transmit */
|
||||||
|
osmo_wqueue_enqueue(&fl1h->write_q[MQ_L1_WRITE], msg);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct femtol1_hdl *l1if_open(void *priv)
|
struct femtol1_hdl *l1if_open(void *priv)
|
||||||
{
|
{
|
||||||
struct femtol1_hdl *fl1h;
|
struct femtol1_hdl *fl1h;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
LOGP(DL1C, LOGL_INFO, "sysmoBTS L1IF compiled against API headers "
|
||||||
|
"v%u.%u.%u\n", SUPERFEMTO_API_VERSION >> 16,
|
||||||
|
(SUPERFEMTO_API_VERSION >> 8) & 0xff,
|
||||||
|
SUPERFEMTO_API_VERSION & 0xff);
|
||||||
|
|
||||||
fl1h = talloc_zero(priv, struct femtol1_hdl);
|
fl1h = talloc_zero(priv, struct femtol1_hdl);
|
||||||
if (!fl1h)
|
if (!fl1h)
|
||||||
return NULL;
|
return NULL;
|
||||||
INIT_LLIST_HEAD(&fl1h->wlc_list);
|
INIT_LLIST_HEAD(&fl1h->wlc_list);
|
||||||
|
|
||||||
fl1h->priv = priv;
|
fl1h->priv = priv;
|
||||||
|
fl1h->clk_cal = 0;
|
||||||
|
/* default clock source: OCXO */
|
||||||
|
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
|
||||||
|
fl1h->clk_src = SuperFemto_ClkSrcId_Ocxo;
|
||||||
|
#else
|
||||||
|
fl1h->clk_src = SF_CLKSRC_OCXO;
|
||||||
|
#endif
|
||||||
|
|
||||||
rc = l1if_transport_open(fl1h);
|
rc = l1if_transport_open(MQ_SYS_WRITE, fl1h);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
talloc_free(fl1h);
|
talloc_free(fl1h);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rc = l1if_transport_open(MQ_L1_WRITE, fl1h);
|
||||||
|
if (rc < 0) {
|
||||||
|
l1if_transport_close(MQ_SYS_WRITE, fl1h);
|
||||||
|
talloc_free(fl1h);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fl1h->gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 1);
|
||||||
|
if (fl1h->gsmtap)
|
||||||
|
gsmtap_source_add_sink(fl1h->gsmtap);
|
||||||
|
|
||||||
return fl1h;
|
return fl1h;
|
||||||
}
|
}
|
||||||
|
|
||||||
int l1if_close(struct femtol1_hdl *fl1h)
|
int l1if_close(struct femtol1_hdl *fl1h)
|
||||||
{
|
{
|
||||||
return l1if_transport_close(fl1h);
|
l1if_transport_close(MQ_L1_WRITE, fl1h);
|
||||||
|
l1if_transport_close(MQ_SYS_WRITE, fl1h);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,17 +3,27 @@
|
|||||||
|
|
||||||
#include <osmocom/core/select.h>
|
#include <osmocom/core/select.h>
|
||||||
#include <osmocom/core/write_queue.h>
|
#include <osmocom/core/write_queue.h>
|
||||||
|
#include <osmocom/core/gsmtap_util.h>
|
||||||
|
#include <osmocom/core/timer.h>
|
||||||
#include <osmocom/gsm/gsm_utils.h>
|
#include <osmocom/gsm/gsm_utils.h>
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
MQ_SYS_READ,
|
MQ_SYS_READ,
|
||||||
MQ_L1_READ,
|
MQ_L1_READ,
|
||||||
|
#ifndef HW_SYSMOBTS_V1
|
||||||
|
MQ_TCH_READ,
|
||||||
|
MQ_PDTCH_READ,
|
||||||
|
#endif
|
||||||
_NUM_MQ_READ
|
_NUM_MQ_READ
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
MQ_SYS_WRITE,
|
MQ_SYS_WRITE,
|
||||||
MQ_L1_WRITE,
|
MQ_L1_WRITE,
|
||||||
|
#ifndef HW_SYSMOBTS_V1
|
||||||
|
MQ_TCH_WRITE,
|
||||||
|
MQ_PDTCH_WRITE,
|
||||||
|
#endif
|
||||||
_NUM_MQ_WRITE
|
_NUM_MQ_WRITE
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -21,8 +31,14 @@ struct femtol1_hdl {
|
|||||||
struct gsm_time gsm_time;
|
struct gsm_time gsm_time;
|
||||||
uint32_t hLayer1; /* handle to the L1 instance in the DSP */
|
uint32_t hLayer1; /* handle to the L1 instance in the DSP */
|
||||||
uint32_t dsp_trace_f;
|
uint32_t dsp_trace_f;
|
||||||
|
int clk_cal;
|
||||||
|
uint8_t clk_src;
|
||||||
|
char *calib_path;
|
||||||
struct llist_head wlc_list;
|
struct llist_head wlc_list;
|
||||||
|
|
||||||
|
struct gsmtap_inst *gsmtap;
|
||||||
|
uint32_t gsmtap_sapi_mask;
|
||||||
|
|
||||||
void *priv; /* user reference */
|
void *priv; /* user reference */
|
||||||
|
|
||||||
struct osmo_timer_list alive_timer;
|
struct osmo_timer_list alive_timer;
|
||||||
@@ -30,10 +46,16 @@ struct femtol1_hdl {
|
|||||||
|
|
||||||
struct osmo_fd read_ofd[_NUM_MQ_READ]; /* osmo file descriptors */
|
struct osmo_fd read_ofd[_NUM_MQ_READ]; /* osmo file descriptors */
|
||||||
struct osmo_wqueue write_q[_NUM_MQ_WRITE];
|
struct osmo_wqueue write_q[_NUM_MQ_WRITE];
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint8_t dsp_version[3];
|
||||||
|
uint8_t fpga_version[3];
|
||||||
|
uint32_t band_support; /* bitmask of GSM_BAND_* */
|
||||||
|
} hw_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define msgb_l1prim(msg) ((GsmL1_Prim_t *)(msg)->l1h)
|
#define msgb_l1prim(msg) ((GsmL1_Prim_t *)(msg)->l1h)
|
||||||
#define msgb_sysprim(msg) ((FemtoBts_Prim_t *)(msg)->l1h)
|
#define msgb_sysprim(msg) ((SuperFemto_Prim_t *)(msg)->l1h)
|
||||||
|
|
||||||
typedef int l1if_compl_cb(struct msgb *l1_msg, void *data);
|
typedef int l1if_compl_cb(struct msgb *l1_msg, void *data);
|
||||||
|
|
||||||
@@ -46,6 +68,7 @@ int l1if_close(struct femtol1_hdl *hdl);
|
|||||||
int l1if_reset(struct femtol1_hdl *hdl);
|
int l1if_reset(struct femtol1_hdl *hdl);
|
||||||
int l1if_activate_rf(struct femtol1_hdl *hdl, int on);
|
int l1if_activate_rf(struct femtol1_hdl *hdl, int on);
|
||||||
int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags);
|
int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags);
|
||||||
|
int l1if_set_txpower(struct femtol1_hdl *fl1h, float tx_power);
|
||||||
|
|
||||||
struct msgb *l1p_msgb_alloc(void);
|
struct msgb *l1p_msgb_alloc(void);
|
||||||
struct msgb *sysp_msgb_alloc(void);
|
struct msgb *sysp_msgb_alloc(void);
|
||||||
@@ -56,5 +79,6 @@ struct gsm_lchan *l1if_hLayer2_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer
|
|||||||
/* tch.c */
|
/* tch.c */
|
||||||
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
|
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
|
||||||
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
|
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
|
||||||
|
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
|
||||||
|
|
||||||
#endif /* _FEMTO_L1_H */
|
#endif /* _FEMTO_L1_H */
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
#include <osmocom/core/msgb.h>
|
#include <osmocom/core/msgb.h>
|
||||||
|
|
||||||
/* functions a transport calls on arrival of primitive from BTS */
|
/* functions a transport calls on arrival of primitive from BTS */
|
||||||
int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg);
|
int l1if_handle_l1prim(int wq, struct femtol1_hdl *fl1h, struct msgb *msg);
|
||||||
int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg);
|
int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg);
|
||||||
|
|
||||||
/* functions exported by a transport */
|
/* functions exported by a transport */
|
||||||
int l1if_transport_open(struct femtol1_hdl *fl1h);
|
int l1if_transport_open(int q, struct femtol1_hdl *fl1h);
|
||||||
int l1if_transport_close(struct femtol1_hdl *fl1h);
|
int l1if_transport_close(int q, struct femtol1_hdl *fl1h);
|
||||||
|
|
||||||
#endif /* _FEMTOL1_TRANSP_H */
|
#endif /* _FEMTOL1_TRANSP_H */
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
#include <osmo-bts/logging.h>
|
#include <osmo-bts/logging.h>
|
||||||
#include <osmo-bts/gsm_data.h>
|
#include <osmo-bts/gsm_data.h>
|
||||||
|
|
||||||
#include <sysmocom/femtobts/femtobts.h>
|
#include <sysmocom/femtobts/superfemto.h>
|
||||||
#include <sysmocom/femtobts/gsml1prim.h>
|
#include <sysmocom/femtobts/gsml1prim.h>
|
||||||
#include <sysmocom/femtobts/gsml1const.h>
|
#include <sysmocom/femtobts/gsml1const.h>
|
||||||
#include <sysmocom/femtobts/gsml1types.h>
|
#include <sysmocom/femtobts/gsml1types.h>
|
||||||
@@ -56,11 +56,15 @@
|
|||||||
static const uint16_t fwd_udp_ports[] = {
|
static const uint16_t fwd_udp_ports[] = {
|
||||||
[MQ_SYS_WRITE] = L1FWD_SYS_PORT,
|
[MQ_SYS_WRITE] = L1FWD_SYS_PORT,
|
||||||
[MQ_L1_WRITE] = L1FWD_L1_PORT,
|
[MQ_L1_WRITE] = L1FWD_L1_PORT,
|
||||||
|
#ifndef HW_SYSMOBTS_V1
|
||||||
|
[MQ_TCH_WRITE] = L1FWD_TCH_PORT,
|
||||||
|
[MQ_PDTCH_WRITE]= L1FWD_PDTCH_PORT,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static int fwd_read_cb(struct osmo_fd *ofd)
|
static int fwd_read_cb(struct osmo_fd *ofd)
|
||||||
{
|
{
|
||||||
struct msgb *msg = msgb_alloc_headroom(2048, 127, "udp_rx");
|
struct msgb *msg = msgb_alloc_headroom(SYSMOBTS_PRIM_SIZE, 128, "udp_rx");
|
||||||
struct femtol1_hdl *fl1h = ofd->data;
|
struct femtol1_hdl *fl1h = ofd->data;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@@ -83,7 +87,7 @@ static int fwd_read_cb(struct osmo_fd *ofd)
|
|||||||
if (ofd->priv_nr == MQ_SYS_WRITE)
|
if (ofd->priv_nr == MQ_SYS_WRITE)
|
||||||
rc = l1if_handle_sysprim(fl1h, msg);
|
rc = l1if_handle_sysprim(fl1h, msg);
|
||||||
else
|
else
|
||||||
rc = l1if_handle_l1prim(fl1h, msg);
|
rc = l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -94,55 +98,55 @@ static int prim_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
|||||||
return write(ofd->fd, msg->head, msg->len);
|
return write(ofd->fd, msg->head, msg->len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int l1if_transport_open(struct femtol1_hdl *fl1h)
|
int l1if_transport_open(int q, struct femtol1_hdl *fl1h)
|
||||||
{
|
{
|
||||||
int rc, i;
|
int rc;
|
||||||
char *bts_host = getenv("L1FWD_BTS_HOST");
|
char *bts_host = getenv("L1FWD_BTS_HOST");
|
||||||
|
|
||||||
printf("sizeof(GsmL1_Prim_t) = %lu\n", sizeof(GsmL1_Prim_t));
|
switch (q) {
|
||||||
printf("sizeof(FemtoBts_Prim_t) = %lu\n", sizeof(FemtoBts_Prim_t));
|
case MQ_L1_WRITE:
|
||||||
|
LOGP(DL1C, LOGL_INFO, "sizeof(GsmL1_Prim_t) = %zu\n",
|
||||||
|
sizeof(GsmL1_Prim_t));
|
||||||
|
break;
|
||||||
|
case MQ_SYS_WRITE:
|
||||||
|
LOGP(DL1C, LOGL_INFO, "sizeof(SuperFemto_Prim_t) = %zu\n",
|
||||||
|
sizeof(SuperFemto_Prim_t));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!bts_host) {
|
if (!bts_host) {
|
||||||
fprintf(stderr, "You have to set the L1FWD_BTS_HOST environment variable\n");
|
fprintf(stderr, "You have to set the L1FWD_BTS_HOST environment variable\n");
|
||||||
exit(2);
|
exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(fl1h->write_q); i++) {
|
struct osmo_wqueue *wq = &fl1h->write_q[q];
|
||||||
struct osmo_wqueue *wq = &fl1h->write_q[i];
|
struct osmo_fd *ofd = &wq->bfd;
|
||||||
struct osmo_fd *ofd = &wq->bfd;
|
|
||||||
|
|
||||||
osmo_wqueue_init(wq, 10);
|
osmo_wqueue_init(wq, 10);
|
||||||
wq->write_cb = prim_write_cb;
|
wq->write_cb = prim_write_cb;
|
||||||
wq->read_cb = fwd_read_cb;
|
wq->read_cb = fwd_read_cb;
|
||||||
|
|
||||||
ofd->data = fl1h;
|
ofd->data = fl1h;
|
||||||
ofd->priv_nr = i;
|
ofd->priv_nr = q;
|
||||||
ofd->when |= BSC_FD_READ;
|
ofd->when |= BSC_FD_READ;
|
||||||
|
|
||||||
rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
|
rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
|
||||||
bts_host, fwd_udp_ports[i],
|
bts_host, fwd_udp_ports[q],
|
||||||
OSMO_SOCK_F_CONNECT);
|
OSMO_SOCK_F_CONNECT);
|
||||||
if (rc < 0) {
|
if (rc < 0)
|
||||||
talloc_free(fl1h);
|
return rc;
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int l1if_transport_close(struct femtol1_hdl *fl1h)
|
int l1if_transport_close(int q, struct femtol1_hdl *fl1h)
|
||||||
{
|
{
|
||||||
int i;
|
struct osmo_wqueue *wq = &fl1h->write_q[q];
|
||||||
|
struct osmo_fd *ofd = &wq->bfd;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(fl1h->write_q); i++) {
|
osmo_wqueue_clear(wq);
|
||||||
struct osmo_wqueue *wq = &fl1h->write_q[i];
|
osmo_fd_unregister(ofd);
|
||||||
struct osmo_fd *ofd = &wq->bfd;
|
close(ofd->fd);
|
||||||
|
|
||||||
osmo_wqueue_clear(wq);
|
|
||||||
osmo_fd_unregister(ofd);
|
|
||||||
close(ofd->fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -36,7 +37,7 @@
|
|||||||
#include <osmo-bts/logging.h>
|
#include <osmo-bts/logging.h>
|
||||||
#include <osmo-bts/gsm_data.h>
|
#include <osmo-bts/gsm_data.h>
|
||||||
|
|
||||||
#include <sysmocom/femtobts/femtobts.h>
|
#include <sysmocom/femtobts/superfemto.h>
|
||||||
#include <sysmocom/femtobts/gsml1prim.h>
|
#include <sysmocom/femtobts/gsml1prim.h>
|
||||||
#include <sysmocom/femtobts/gsml1const.h>
|
#include <sysmocom/femtobts/gsml1const.h>
|
||||||
#include <sysmocom/femtobts/gsml1types.h>
|
#include <sysmocom/femtobts/gsml1types.h>
|
||||||
@@ -46,31 +47,57 @@
|
|||||||
#include "l1_transp.h"
|
#include "l1_transp.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef HW_SYSMOBTS_V1
|
||||||
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/femtobts_dsp2arm"
|
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/femtobts_dsp2arm"
|
||||||
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/femtobts_arm2dsp"
|
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/femtobts_arm2dsp"
|
||||||
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_dsp2arm"
|
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_dsp2arm"
|
||||||
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_arm2dsp"
|
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_arm2dsp"
|
||||||
|
#else
|
||||||
|
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/superfemto_dsp2arm"
|
||||||
|
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/superfemto_arm2dsp"
|
||||||
|
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_sig_dsp2arm"
|
||||||
|
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_sig_arm2dsp"
|
||||||
|
|
||||||
|
#define DEV_TCH_DSP2ARM_NAME "/dev/msgq/gsml1_tch_dsp2arm"
|
||||||
|
#define DEV_TCH_ARM2DSP_NAME "/dev/msgq/gsml1_tch_arm2dsp"
|
||||||
|
#define DEV_PDTCH_DSP2ARM_NAME "/dev/msgq/gsml1_pdtch_dsp2arm"
|
||||||
|
#define DEV_PDTCH_ARM2DSP_NAME "/dev/msgq/gsml1_pdtch_arm2dsp"
|
||||||
|
#endif
|
||||||
|
|
||||||
static const char *rd_devnames[] = {
|
static const char *rd_devnames[] = {
|
||||||
[MQ_SYS_READ] = DEV_SYS_DSP2ARM_NAME,
|
[MQ_SYS_READ] = DEV_SYS_DSP2ARM_NAME,
|
||||||
[MQ_L1_READ] = DEV_L1_DSP2ARM_NAME,
|
[MQ_L1_READ] = DEV_L1_DSP2ARM_NAME,
|
||||||
|
#ifndef HW_SYSMOBTS_V1
|
||||||
|
[MQ_TCH_READ] = DEV_TCH_DSP2ARM_NAME,
|
||||||
|
[MQ_PDTCH_READ] = DEV_PDTCH_DSP2ARM_NAME,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *wr_devnames[] = {
|
static const char *wr_devnames[] = {
|
||||||
[MQ_SYS_WRITE] = DEV_SYS_ARM2DSP_NAME,
|
[MQ_SYS_WRITE] = DEV_SYS_ARM2DSP_NAME,
|
||||||
[MQ_L1_WRITE] = DEV_L1_ARM2DSP_NAME,
|
[MQ_L1_WRITE] = DEV_L1_ARM2DSP_NAME,
|
||||||
|
#ifndef HW_SYSMOBTS_V1
|
||||||
|
[MQ_TCH_WRITE] = DEV_TCH_ARM2DSP_NAME,
|
||||||
|
[MQ_PDTCH_WRITE]= DEV_PDTCH_ARM2DSP_NAME,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure that all structs we read fit into the SYSMOBTS_PRIM_SIZE
|
||||||
|
*/
|
||||||
|
osmo_static_assert(sizeof(GsmL1_Prim_t) + 128 <= SYSMOBTS_PRIM_SIZE, prim)
|
||||||
|
osmo_static_assert(sizeof(SuperFemto_Prim_t) + 128 <= SYSMOBTS_PRIM_SIZE, prim)
|
||||||
|
|
||||||
/* callback when there's something to read from the l1 msg_queue */
|
/* callback when there's something to read from the l1 msg_queue */
|
||||||
static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||||
{
|
{
|
||||||
//struct msgb *msg = l1p_msgb_alloc();
|
//struct msgb *msg = l1p_msgb_alloc();
|
||||||
struct msgb *msg = msgb_alloc_headroom(2048, 128, "1l_fd");
|
struct msgb *msg = msgb_alloc_headroom(SYSMOBTS_PRIM_SIZE, 128, "1l_fd");
|
||||||
struct femtol1_hdl *fl1h = ofd->data;
|
struct femtol1_hdl *fl1h = ofd->data;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
msg->l1h = msg->data;
|
msg->l1h = msg->data;
|
||||||
rc = read(ofd->fd, msg->l1h, sizeof(GsmL1_Prim_t));
|
rc = read(ofd->fd, msg->l1h, msgb_tailroom(msg));
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
if (rc != -1)
|
if (rc != -1)
|
||||||
LOGP(DL1C, LOGL_ERROR, "error reading from L1 msg_queue: %s\n",
|
LOGP(DL1C, LOGL_ERROR, "error reading from L1 msg_queue: %s\n",
|
||||||
@@ -80,10 +107,28 @@ static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
|||||||
}
|
}
|
||||||
msgb_put(msg, rc);
|
msgb_put(msg, rc);
|
||||||
|
|
||||||
if (ofd->priv_nr == MQ_L1_WRITE)
|
switch (ofd->priv_nr) {
|
||||||
return l1if_handle_l1prim(fl1h, msg);
|
case MQ_SYS_WRITE:
|
||||||
else
|
if (rc != sizeof(SuperFemto_Prim_t))
|
||||||
|
LOGP(DL1C, LOGL_NOTICE, "%u != "
|
||||||
|
"sizeof(SuperFemto_Prim_t)\n", rc);
|
||||||
return l1if_handle_sysprim(fl1h, msg);
|
return l1if_handle_sysprim(fl1h, msg);
|
||||||
|
case MQ_L1_WRITE:
|
||||||
|
#ifndef HW_SYSMOBTS_V1
|
||||||
|
case MQ_TCH_WRITE:
|
||||||
|
case MQ_PDTCH_WRITE:
|
||||||
|
#endif
|
||||||
|
if (rc != sizeof(GsmL1_Prim_t))
|
||||||
|
LOGP(DL1C, LOGL_NOTICE, "%u != "
|
||||||
|
"sizeof(GsmL1_Prim_t)\n", rc);
|
||||||
|
return l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
|
||||||
|
default:
|
||||||
|
/* The compiler can't know that priv_nr is an enum. Assist. */
|
||||||
|
LOGP(DL1C, LOGL_FATAL, "writing on a wrong queue: %d\n",
|
||||||
|
ofd->priv_nr);
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* callback when we can write to one of the l1 msg_queue devices */
|
/* callback when we can write to one of the l1 msg_queue devices */
|
||||||
@@ -105,88 +150,73 @@ static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int l1if_transport_open(struct femtol1_hdl *hdl)
|
int l1if_transport_open(int q, struct femtol1_hdl *hdl)
|
||||||
{
|
{
|
||||||
int rc, i;
|
int rc;
|
||||||
|
|
||||||
/* Step 1: Open all msg_queue file descriptors */
|
/* Step 1: Open all msg_queue file descriptors */
|
||||||
for (i = 0; i < ARRAY_SIZE(hdl->read_ofd); i++) {
|
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
|
||||||
struct osmo_fd *ofd = &hdl->read_ofd[i];
|
struct osmo_wqueue *wq = &hdl->write_q[q];
|
||||||
|
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
|
||||||
|
|
||||||
rc = open(rd_devnames[i], O_RDONLY);
|
rc = open(rd_devnames[q], O_RDONLY);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue: %s\n",
|
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue: %s\n",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
goto out_free;
|
return rc;
|
||||||
}
|
|
||||||
ofd->fd = rc;
|
|
||||||
ofd->priv_nr = i;
|
|
||||||
ofd->data = hdl;
|
|
||||||
ofd->cb = l1if_fd_cb;
|
|
||||||
ofd->when = BSC_FD_READ;
|
|
||||||
rc = osmo_fd_register(ofd);
|
|
||||||
if (rc < 0) {
|
|
||||||
close(ofd->fd);
|
|
||||||
ofd->fd = -1;
|
|
||||||
goto out_free;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (i = 0; i < ARRAY_SIZE(hdl->write_q); i++) {
|
read_ofd->fd = rc;
|
||||||
struct osmo_wqueue *wq = &hdl->write_q[i];
|
read_ofd->priv_nr = q;
|
||||||
struct osmo_fd *ofd = &hdl->write_q[i].bfd;
|
read_ofd->data = hdl;
|
||||||
|
read_ofd->cb = l1if_fd_cb;
|
||||||
rc = open(wr_devnames[i], O_WRONLY);
|
read_ofd->when = BSC_FD_READ;
|
||||||
if (rc < 0) {
|
rc = osmo_fd_register(read_ofd);
|
||||||
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue: %s\n",
|
if (rc < 0) {
|
||||||
strerror(errno));
|
close(read_ofd->fd);
|
||||||
goto out_read;
|
read_ofd->fd = -1;
|
||||||
}
|
return rc;
|
||||||
|
|
||||||
osmo_wqueue_init(wq, 10);
|
|
||||||
wq->write_cb = l1fd_write_cb;
|
|
||||||
|
|
||||||
ofd->fd = rc;
|
|
||||||
ofd->priv_nr = i;
|
|
||||||
ofd->data = hdl;
|
|
||||||
ofd->when = BSC_FD_WRITE;
|
|
||||||
rc = osmo_fd_register(ofd);
|
|
||||||
if (rc < 0) {
|
|
||||||
close(ofd->fd);
|
|
||||||
ofd->fd = -1;
|
|
||||||
goto out_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rc = open(wr_devnames[q], O_WRONLY);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto out_read;
|
||||||
|
}
|
||||||
|
osmo_wqueue_init(wq, 10);
|
||||||
|
wq->write_cb = l1fd_write_cb;
|
||||||
|
write_ofd->fd = rc;
|
||||||
|
write_ofd->priv_nr = q;
|
||||||
|
write_ofd->data = hdl;
|
||||||
|
write_ofd->when = BSC_FD_WRITE;
|
||||||
|
rc = osmo_fd_register(write_ofd);
|
||||||
|
if (rc < 0) {
|
||||||
|
close(write_ofd->fd);
|
||||||
|
write_ofd->fd = -1;
|
||||||
|
goto out_read;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_read:
|
out_read:
|
||||||
for (i = 0; i < ARRAY_SIZE(hdl->read_ofd); i++) {
|
close(hdl->read_ofd[q].fd);
|
||||||
close(hdl->read_ofd[i].fd);
|
osmo_fd_unregister(&hdl->read_ofd[q]);
|
||||||
osmo_fd_unregister(&hdl->read_ofd[i]);
|
|
||||||
}
|
|
||||||
out_free:
|
|
||||||
talloc_free(hdl);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int l1if_transport_close(struct femtol1_hdl *hdl)
|
int l1if_transport_close(int q, struct femtol1_hdl *hdl)
|
||||||
{
|
{
|
||||||
int i;
|
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
|
||||||
|
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(hdl->read_ofd); i++) {
|
osmo_fd_unregister(read_ofd);
|
||||||
struct osmo_fd *ofd = &hdl->read_ofd[i];
|
close(read_ofd->fd);
|
||||||
|
read_ofd->fd = -1;
|
||||||
|
|
||||||
osmo_fd_unregister(ofd);
|
osmo_fd_unregister(write_ofd);
|
||||||
close(ofd->fd);
|
close(write_ofd->fd);
|
||||||
ofd->fd = -1;
|
write_ofd->fd = -1;
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(hdl->write_q); i++) {
|
|
||||||
struct osmo_fd *ofd = &hdl->write_q[i].bfd;
|
|
||||||
|
|
||||||
osmo_fd_unregister(ofd);
|
|
||||||
close(ofd->fd);
|
|
||||||
ofd->fd = -1;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -24,7 +24,10 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <sys/signal.h>
|
#include <sys/signal.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
@@ -39,18 +42,20 @@
|
|||||||
#include <osmo-bts/abis.h>
|
#include <osmo-bts/abis.h>
|
||||||
#include <osmo-bts/bts.h>
|
#include <osmo-bts/bts.h>
|
||||||
#include <osmo-bts/vty.h>
|
#include <osmo-bts/vty.h>
|
||||||
|
#include <osmo-bts/bts_model.h>
|
||||||
|
#include <osmo-bts/pcu_if.h>
|
||||||
|
|
||||||
|
#define SYSMOBTS_RF_LOCK_PATH "/var/lock/bts_rf_lock"
|
||||||
|
|
||||||
#include "l1_if.h"
|
#include "l1_if.h"
|
||||||
|
|
||||||
/* FIXME: read from real hardware */
|
/* FIXME: read from real hardware */
|
||||||
const uint8_t abis_mac[6] = { 0,1,2,3,4,5 };
|
const uint8_t abis_mac[6] = { 0,1,2,3,4,5 };
|
||||||
/* FIXME: generate from git */
|
int pcu_direct = 0;
|
||||||
const char *software_version = "0815";
|
|
||||||
|
|
||||||
static const char *config_file = "osmo-bts.cfg";
|
static const char *config_file = "osmo-bts.cfg";
|
||||||
extern const char *osmobts_copyright;
|
|
||||||
static int daemonize = 0;
|
static int daemonize = 0;
|
||||||
static unsigned int dsp_trace = 0;
|
static unsigned int dsp_trace = 0x71c00020;
|
||||||
|
|
||||||
int bts_model_init(struct gsm_bts *bts)
|
int bts_model_init(struct gsm_bts *bts)
|
||||||
{
|
{
|
||||||
@@ -68,7 +73,7 @@ int bts_model_init(struct gsm_bts *bts)
|
|||||||
|
|
||||||
l1if_reset(fl1h);
|
l1if_reset(fl1h);
|
||||||
|
|
||||||
femtol1_vty_init(bts);
|
bts_model_vty_init(bts);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -102,10 +107,22 @@ static void print_help()
|
|||||||
" -T --timestamp Prefix every log line with a timestamp\n"
|
" -T --timestamp Prefix every log line with a timestamp\n"
|
||||||
" -V --version Print version information and exit\n"
|
" -V --version Print version information and exit\n"
|
||||||
" -e --log-level Set a global log-level\n"
|
" -e --log-level Set a global log-level\n"
|
||||||
" -p --dsp-trace Set DSP trace flags\n"
|
" -p --dsp-trace Set DSP trace flags\n"
|
||||||
|
" -w --hw-version Print the targeted HW Version\n"
|
||||||
|
" -M --pcu-direct Force PCU to access message queue for "
|
||||||
|
"PDCH dchannel directly\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void print_hwversion()
|
||||||
|
{
|
||||||
|
#ifdef HW_SYSMOBTS_V1
|
||||||
|
printf("sysmobts was compiled for hw version 1.\n");
|
||||||
|
#else
|
||||||
|
printf("sysmobts was compiled for hw version 2.\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* FIXME: finally get some option parsing code into libosmocore */
|
/* FIXME: finally get some option parsing code into libosmocore */
|
||||||
static void handle_options(int argc, char **argv)
|
static void handle_options(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@@ -122,10 +139,12 @@ static void handle_options(int argc, char **argv)
|
|||||||
{ "version", 0, 0, 'V' },
|
{ "version", 0, 0, 'V' },
|
||||||
{ "log-level", 1, 0, 'e' },
|
{ "log-level", 1, 0, 'e' },
|
||||||
{ "dsp-trace", 1, 0, 'p' },
|
{ "dsp-trace", 1, 0, 'p' },
|
||||||
|
{ "hw-version", 0, 0, 'w' },
|
||||||
|
{ "pcu-direct", 0, 0, 'M' },
|
||||||
{ 0, 0, 0, 0 }
|
{ 0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "hc:d:Dc:sTVe:p:",
|
c = getopt_long(argc, argv, "hc:d:Dc:sTVe:p:w:M",
|
||||||
long_options, &option_idx);
|
long_options, &option_idx);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
@@ -150,6 +169,9 @@ static void handle_options(int argc, char **argv)
|
|||||||
case 'T':
|
case 'T':
|
||||||
log_set_print_timestamp(osmo_stderr_target, 1);
|
log_set_print_timestamp(osmo_stderr_target, 1);
|
||||||
break;
|
break;
|
||||||
|
case 'M':
|
||||||
|
pcu_direct = 1;
|
||||||
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
print_version(1);
|
print_version(1);
|
||||||
exit(0);
|
exit(0);
|
||||||
@@ -160,6 +182,10 @@ static void handle_options(int argc, char **argv)
|
|||||||
case 'p':
|
case 'p':
|
||||||
dsp_trace = strtoul(optarg, NULL, 16);
|
dsp_trace = strtoul(optarg, NULL, 16);
|
||||||
break;
|
break;
|
||||||
|
case 'w':
|
||||||
|
print_hwversion();
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -187,8 +213,28 @@ static void signal_handler(int signal)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int write_pid_file(char *procname)
|
||||||
|
{
|
||||||
|
FILE *outf;
|
||||||
|
char tmp[PATH_MAX+1];
|
||||||
|
|
||||||
|
snprintf(tmp, sizeof(tmp)-1, "/var/run/%s.pid", procname);
|
||||||
|
tmp[PATH_MAX-1] = '\0';
|
||||||
|
|
||||||
|
outf = fopen(tmp, "w");
|
||||||
|
if (!outf)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
fprintf(outf, "%d\n", getpid());
|
||||||
|
|
||||||
|
fclose(outf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
struct stat st;
|
||||||
struct gsm_bts_role_bts *btsb;
|
struct gsm_bts_role_bts *btsb;
|
||||||
struct ipabis_link *link;
|
struct ipabis_link *link;
|
||||||
void *tall_msgb_ctx;
|
void *tall_msgb_ctx;
|
||||||
@@ -203,14 +249,15 @@ int main(int argc, char **argv)
|
|||||||
vty_init(&bts_vty_info);
|
vty_init(&bts_vty_info);
|
||||||
bts_vty_init(&bts_log_info);
|
bts_vty_init(&bts_log_info);
|
||||||
|
|
||||||
|
handle_options(argc, argv);
|
||||||
|
|
||||||
bts = gsm_bts_alloc(tall_bts_ctx);
|
bts = gsm_bts_alloc(tall_bts_ctx);
|
||||||
if (bts_init(bts) < 0) {
|
if (bts_init(bts) < 0) {
|
||||||
fprintf(stderr, "unable to to open bts\n");
|
fprintf(stderr, "unable to to open bts\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
btsb = bts_role_bts(bts);
|
btsb = bts_role_bts(bts);
|
||||||
|
btsb->support.ciphers = (1 << 0) | (1 << 1) | (1 << 2);
|
||||||
handle_options(argc, argv);
|
|
||||||
|
|
||||||
rc = vty_read_config_file(config_file, NULL);
|
rc = vty_read_config_file(config_file, NULL);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
@@ -219,12 +266,23 @@ int main(int argc, char **argv)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stat(SYSMOBTS_RF_LOCK_PATH, &st) == 0) {
|
||||||
|
LOGP(DL1C, LOGL_NOTICE, "Not starting BTS due to RF_LOCK file present\n");
|
||||||
|
exit(23);
|
||||||
|
}
|
||||||
|
write_pid_file("osmo-bts");
|
||||||
|
|
||||||
rc = telnet_init(tall_bts_ctx, NULL, 4241);
|
rc = telnet_init(tall_bts_ctx, NULL, 4241);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
fprintf(stderr, "Error initializing telnet\n");
|
fprintf(stderr, "Error initializing telnet\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pcu_sock_init()) {
|
||||||
|
fprintf(stderr, "PCU L1 socket failed\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
signal(SIGINT, &signal_handler);
|
signal(SIGINT, &signal_handler);
|
||||||
//signal(SIGABRT, &signal_handler);
|
//signal(SIGABRT, &signal_handler);
|
||||||
signal(SIGUSR1, &signal_handler);
|
signal(SIGUSR1, &signal_handler);
|
||||||
|
|||||||
19
src/osmo-bts-sysmo/misc/sysmobts_eeprom.h
Normal file
19
src/osmo-bts-sysmo/misc/sysmobts_eeprom.h
Normal 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
|
||||||
163
src/osmo-bts-sysmo/misc/sysmobts_mgr.c
Normal file
163
src/osmo-bts-sysmo/misc/sysmobts_mgr.c
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/osmo-bts-sysmo/misc/sysmobts_mgr.h
Normal file
9
src/osmo-bts-sysmo/misc/sysmobts_mgr.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#ifndef _SYSMOBTS_MGR_H
|
||||||
|
#define _SYSMOBTS_MGR_H
|
||||||
|
|
||||||
|
enum {
|
||||||
|
DTEMP,
|
||||||
|
DFW,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
268
src/osmo-bts-sysmo/misc/sysmobts_misc.c
Normal file
268
src/osmo-bts-sysmo/misc/sysmobts_misc.c
Normal 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;
|
||||||
|
}
|
||||||
31
src/osmo-bts-sysmo/misc/sysmobts_misc.h
Normal file
31
src/osmo-bts-sysmo/misc/sysmobts_misc.h
Normal 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
|
||||||
226
src/osmo-bts-sysmo/misc/sysmobts_par.c
Normal file
226
src/osmo-bts-sysmo/misc/sysmobts_par.c
Normal 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;
|
||||||
|
}
|
||||||
26
src/osmo-bts-sysmo/misc/sysmobts_par.h
Normal file
26
src/osmo-bts-sysmo/misc/sysmobts_par.h
Normal 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
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#include <osmocom/core/talloc.h>
|
#include <osmocom/core/talloc.h>
|
||||||
#include <osmocom/core/utils.h>
|
#include <osmocom/core/utils.h>
|
||||||
@@ -29,6 +30,10 @@
|
|||||||
#include <osmo-bts/gsm_data.h>
|
#include <osmo-bts/gsm_data.h>
|
||||||
#include <osmo-bts/logging.h>
|
#include <osmo-bts/logging.h>
|
||||||
#include <osmo-bts/oml.h>
|
#include <osmo-bts/oml.h>
|
||||||
|
#include <osmo-bts/rsl.h>
|
||||||
|
#include <osmo-bts/amr.h>
|
||||||
|
#include <osmo-bts/bts.h>
|
||||||
|
#include <osmo-bts/bts_model.h>
|
||||||
|
|
||||||
#include "l1_if.h"
|
#include "l1_if.h"
|
||||||
#include "femtobts.h"
|
#include "femtobts.h"
|
||||||
@@ -226,7 +231,7 @@ static int trx_init_compl_cb(struct msgb *l1_msg, void *data)
|
|||||||
return opstart_compl_cb(l1_msg, &trx->mo);
|
return opstart_compl_cb(l1_msg, &trx->mo);
|
||||||
}
|
}
|
||||||
|
|
||||||
int gsm_abis_mo_check_attr(const struct gsm_abis_mo *mo, uint8_t *attr_ids,
|
int gsm_abis_mo_check_attr(const struct gsm_abis_mo *mo, const uint8_t *attr_ids,
|
||||||
unsigned int num_attr_ids)
|
unsigned int num_attr_ids)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
@@ -250,7 +255,6 @@ static int trx_init(struct gsm_bts_trx *trx)
|
|||||||
struct msgb *msg;
|
struct msgb *msg;
|
||||||
GsmL1_MphInitReq_t *mi_req;
|
GsmL1_MphInitReq_t *mi_req;
|
||||||
GsmL1_DeviceParam_t *dev_par;
|
GsmL1_DeviceParam_t *dev_par;
|
||||||
enum gsm_band osmo_band;
|
|
||||||
int femto_band;
|
int femto_band;
|
||||||
|
|
||||||
if (!gsm_abis_mo_check_attr(&trx->mo, trx_rqd_attr,
|
if (!gsm_abis_mo_check_attr(&trx->mo, trx_rqd_attr,
|
||||||
@@ -261,11 +265,10 @@ static int trx_init(struct gsm_bts_trx *trx)
|
|||||||
//return oml_mo_opstart_nack(&trx->mo, NM_NACK_CANT_PERFORM);
|
//return oml_mo_opstart_nack(&trx->mo, NM_NACK_CANT_PERFORM);
|
||||||
}
|
}
|
||||||
|
|
||||||
osmo_band = gsm_arfcn2band(trx->arfcn);
|
femto_band = band_osmo2femto(trx->bts->band);
|
||||||
femto_band = band_osmo2femto(osmo_band);
|
|
||||||
if (femto_band < 0) {
|
if (femto_band < 0) {
|
||||||
LOGP(DL1C, LOGL_ERROR, "Unsupported GSM band %s\n",
|
LOGP(DL1C, LOGL_ERROR, "Unsupported GSM band %s\n",
|
||||||
gsm_band_name(osmo_band));
|
gsm_band_name(trx->bts->band));
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = l1p_msgb_alloc();
|
msg = l1p_msgb_alloc();
|
||||||
@@ -286,6 +289,30 @@ static int trx_init(struct gsm_bts_trx *trx)
|
|||||||
return l1if_req_compl(fl1h, msg, 0, trx_init_compl_cb, fl1h);
|
return l1if_req_compl(fl1h, msg, 0, trx_init_compl_cb, fl1h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx)
|
||||||
|
{
|
||||||
|
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||||
|
|
||||||
|
return fl1h->hLayer1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trx_close_compl_cb(struct msgb *l1_msg, void *data)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trx_close(struct gsm_bts_trx *trx)
|
||||||
|
{
|
||||||
|
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||||
|
struct msgb *msg;
|
||||||
|
|
||||||
|
msg = l1p_msgb_alloc();
|
||||||
|
prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphCloseReq, fl1h);
|
||||||
|
LOGP(DL1C, LOGL_NOTICE, "Close TRX %u\n", trx->nr);
|
||||||
|
|
||||||
|
return l1if_req_compl(fl1h, msg, 0, trx_close_compl_cb, fl1h);
|
||||||
|
}
|
||||||
|
|
||||||
static int ts_connect(struct gsm_bts_trx_ts *ts)
|
static int ts_connect(struct gsm_bts_trx_ts *ts)
|
||||||
{
|
{
|
||||||
struct msgb *msg = l1p_msgb_alloc();
|
struct msgb *msg = l1p_msgb_alloc();
|
||||||
@@ -353,20 +380,39 @@ static const struct sapi_dir ccch_sapis[] = {
|
|||||||
#define DIR_BOTH (GsmL1_Dir_TxDownlink|GsmL1_Dir_RxUplink)
|
#define DIR_BOTH (GsmL1_Dir_TxDownlink|GsmL1_Dir_RxUplink)
|
||||||
|
|
||||||
static const struct sapi_dir tchf_sapis[] = {
|
static const struct sapi_dir tchf_sapis[] = {
|
||||||
{ GsmL1_Sapi_TchF, DIR_BOTH },
|
{ GsmL1_Sapi_TchF, GsmL1_Dir_TxDownlink },
|
||||||
{ GsmL1_Sapi_FacchF, DIR_BOTH },
|
{ GsmL1_Sapi_TchF, GsmL1_Dir_RxUplink },
|
||||||
{ GsmL1_Sapi_Sacch, DIR_BOTH },
|
{ GsmL1_Sapi_FacchF, GsmL1_Dir_TxDownlink },
|
||||||
|
{ GsmL1_Sapi_FacchF, GsmL1_Dir_RxUplink },
|
||||||
|
{ GsmL1_Sapi_Sacch, GsmL1_Dir_TxDownlink },
|
||||||
|
{ GsmL1_Sapi_Sacch, GsmL1_Dir_RxUplink },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sapi_dir tchh_sapis[] = {
|
static const struct sapi_dir tchh_sapis[] = {
|
||||||
{ GsmL1_Sapi_TchH, DIR_BOTH },
|
{ GsmL1_Sapi_TchH, GsmL1_Dir_TxDownlink },
|
||||||
{ GsmL1_Sapi_FacchH, DIR_BOTH },
|
{ GsmL1_Sapi_TchH, GsmL1_Dir_RxUplink },
|
||||||
{ GsmL1_Sapi_Sacch, DIR_BOTH },
|
{ GsmL1_Sapi_FacchH, GsmL1_Dir_TxDownlink },
|
||||||
|
{ GsmL1_Sapi_FacchH, GsmL1_Dir_RxUplink },
|
||||||
|
{ GsmL1_Sapi_Sacch, GsmL1_Dir_TxDownlink },
|
||||||
|
{ GsmL1_Sapi_Sacch, GsmL1_Dir_RxUplink },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sapi_dir sdcch_sapis[] = {
|
static const struct sapi_dir sdcch_sapis[] = {
|
||||||
{ GsmL1_Sapi_Sdcch, DIR_BOTH },
|
{ GsmL1_Sapi_Sdcch, GsmL1_Dir_TxDownlink },
|
||||||
{ GsmL1_Sapi_Sacch, DIR_BOTH },
|
{ GsmL1_Sapi_Sdcch, GsmL1_Dir_RxUplink },
|
||||||
|
{ GsmL1_Sapi_Sacch, GsmL1_Dir_TxDownlink },
|
||||||
|
{ GsmL1_Sapi_Sacch, GsmL1_Dir_RxUplink },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sapi_dir pdtch_sapis[] = {
|
||||||
|
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_TxDownlink },
|
||||||
|
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_RxUplink },
|
||||||
|
{ GsmL1_Sapi_Ptcch, GsmL1_Dir_TxDownlink },
|
||||||
|
{ GsmL1_Sapi_Prach, GsmL1_Dir_RxUplink },
|
||||||
|
#if 0
|
||||||
|
{ GsmL1_Sapi_Ptcch, GsmL1_Dir_RxUplink },
|
||||||
|
{ GsmL1_Sapi_Pacch, GsmL1_Dir_TxDownlink },
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lchan_sapis {
|
struct lchan_sapis {
|
||||||
@@ -391,31 +437,48 @@ static const struct lchan_sapis sapis_for_lchan[_GSM_LCHAN_MAX] = {
|
|||||||
.sapis = ccch_sapis,
|
.sapis = ccch_sapis,
|
||||||
.num_sapis = ARRAY_SIZE(ccch_sapis),
|
.num_sapis = ARRAY_SIZE(ccch_sapis),
|
||||||
},
|
},
|
||||||
|
[GSM_LCHAN_PDTCH] = {
|
||||||
|
.sapis = pdtch_sapis,
|
||||||
|
.num_sapis = ARRAY_SIZE(pdtch_sapis),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int lchan_act_compl_cb(struct msgb *l1_msg, void *data)
|
static int lchan_act_compl_cb(struct msgb *l1_msg, void *data)
|
||||||
{
|
{
|
||||||
|
struct gsm_time *time;
|
||||||
struct gsm_lchan *lchan = data;
|
struct gsm_lchan *lchan = data;
|
||||||
GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
|
GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
|
||||||
GsmL1_MphActivateCnf_t *ic = &l1p->u.mphActivateCnf;
|
GsmL1_MphActivateCnf_t *ic = &l1p->u.mphActivateCnf;
|
||||||
|
|
||||||
LOGP(DL1C, LOGL_INFO, "%s MPH-ACTIVATE.conf\n", gsm_lchan_name(lchan));
|
LOGP(DL1C, LOGL_INFO, "%s MPH-ACTIVATE.conf (%s ",
|
||||||
|
gsm_lchan_name(lchan),
|
||||||
|
get_value_string(femtobts_l1sapi_names, ic->sapi));
|
||||||
|
LOGPC(DL1C, LOGL_INFO, "%s)\n",
|
||||||
|
get_value_string(femtobts_dir_names, ic->dir));
|
||||||
|
|
||||||
if (ic->status == GsmL1_Status_Success) {
|
if (ic->status == GsmL1_Status_Success) {
|
||||||
DEBUGP(DL1C, "Successful activation of L1 SAPI %s on TS %u\n",
|
DEBUGP(DL1C, "Successful activation of L1 SAPI %s on TS %u\n",
|
||||||
get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn);
|
get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn);
|
||||||
lchan->state = LCHAN_S_ACTIVE;
|
lchan_set_state(lchan, LCHAN_S_ACTIVE);
|
||||||
} else {
|
} else {
|
||||||
LOGP(DL1C, LOGL_ERROR, "Error activating L1 SAPI %s on TS %u: %s\n",
|
LOGP(DL1C, LOGL_ERROR, "Error activating L1 SAPI %s on TS %u: %s\n",
|
||||||
get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn,
|
get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn,
|
||||||
get_value_string(femtobts_l1status_names, ic->status));
|
get_value_string(femtobts_l1status_names, ic->status));
|
||||||
lchan->state = LCHAN_S_NONE;
|
lchan_set_state(lchan, LCHAN_S_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ic->sapi) {
|
switch (ic->sapi) {
|
||||||
case GsmL1_Sapi_Sdcch:
|
case GsmL1_Sapi_Sdcch:
|
||||||
case GsmL1_Sapi_TchF:
|
case GsmL1_Sapi_TchF:
|
||||||
/* FIXME: Send RSL CHAN ACT */
|
case GsmL1_Sapi_TchH:
|
||||||
|
time = bts_model_get_time(lchan->ts->trx->bts);
|
||||||
|
if (lchan->state == LCHAN_S_ACTIVE) {
|
||||||
|
/* Hack: we simply only use one direction to
|
||||||
|
* avoid sending two ACKs for one activate */
|
||||||
|
if (ic->dir == GsmL1_Dir_TxDownlink)
|
||||||
|
rsl_tx_chan_act_ack(lchan, time);
|
||||||
|
} else
|
||||||
|
rsl_tx_chan_act_nack(lchan, RSL_ERR_EQUIPMENT_FAIL);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -466,28 +529,62 @@ static void alive_timer_cb(void *data)
|
|||||||
osmo_timer_schedule(&fl1h->alive_timer, 5, 0);
|
osmo_timer_schedule(&fl1h->alive_timer, 5, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void clear_amr_params(GsmL1_LogChParam_t *lch_par)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
/* common for the SIGN, V1 and EFR: */
|
||||||
|
lch_par->tch.amrCmiPhase = GsmL1_AmrCmiPhase_NA;
|
||||||
|
lch_par->tch.amrInitCodecMode = GsmL1_AmrCodecMode_Unset;
|
||||||
|
for (j = 0; j < ARRAY_SIZE(lch_par->tch.amrActiveCodecSet); j++)
|
||||||
|
lch_par->tch.amrActiveCodecSet[j] = GsmL1_AmrCodec_Unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_payload_format(GsmL1_LogChParam_t *lch_par)
|
||||||
|
{
|
||||||
|
#ifdef L1_HAS_RTP_MODE
|
||||||
|
#ifdef USE_L1_RTP_MODE
|
||||||
|
lch_par->tch.tchPlFmt = GsmL1_TchPlFmt_Rtp;
|
||||||
|
#else
|
||||||
|
lch_par->tch.tchPlFmt = GsmL1_TchPlFmt_If2;
|
||||||
|
#endif /* USE_L1_RTP_MODE */
|
||||||
|
#endif /* L1_HAS_RTP_MODE */
|
||||||
|
}
|
||||||
|
|
||||||
static void lchan2lch_par(GsmL1_LogChParam_t *lch_par, struct gsm_lchan *lchan)
|
static void lchan2lch_par(GsmL1_LogChParam_t *lch_par, struct gsm_lchan *lchan)
|
||||||
{
|
{
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
LOGPC(DL1C, LOGL_INFO, "%s: %s tch_mode=0x%02x\n",
|
LOGP(DL1C, LOGL_INFO, "%s: %s tch_mode=0x%02x\n",
|
||||||
gsm_lchan_name(lchan), __FUNCTION__, lchan->tch_mode);
|
gsm_lchan_name(lchan), __FUNCTION__, lchan->tch_mode);
|
||||||
|
|
||||||
switch (lchan->tch_mode) {
|
switch (lchan->tch_mode) {
|
||||||
case GSM48_CMODE_SIGN:
|
case GSM48_CMODE_SIGN:
|
||||||
|
/* we have to set some TCH payload type even if we don't
|
||||||
|
* know yet what codec we will use later on */
|
||||||
|
if (lchan->type == GSM_LCHAN_TCH_F)
|
||||||
|
lch_par->tch.tchPlType = GsmL1_TchPlType_Fr;
|
||||||
|
else
|
||||||
|
lch_par->tch.tchPlType = GsmL1_TchPlType_Hr;
|
||||||
|
clear_amr_params(lch_par);
|
||||||
|
break;
|
||||||
case GSM48_CMODE_SPEECH_V1:
|
case GSM48_CMODE_SPEECH_V1:
|
||||||
|
if (lchan->type == GSM_LCHAN_TCH_F)
|
||||||
|
lch_par->tch.tchPlType = GsmL1_TchPlType_Fr;
|
||||||
|
else
|
||||||
|
lch_par->tch.tchPlType = GsmL1_TchPlType_Hr;
|
||||||
|
set_payload_format(lch_par);
|
||||||
|
clear_amr_params(lch_par);
|
||||||
|
break;
|
||||||
case GSM48_CMODE_SPEECH_EFR:
|
case GSM48_CMODE_SPEECH_EFR:
|
||||||
lch_par->tch.amrCmiPhase = GsmL1_AmrCmiPhase_NA;
|
lch_par->tch.tchPlType = GsmL1_TchPlType_Efr;
|
||||||
lch_par->tch.amrInitCodecMode = GsmL1_AmrCodecMode_Unset;
|
set_payload_format(lch_par);
|
||||||
for (j = 0; j < ARRAY_SIZE(lch_par->tch.amrActiveCodecSet); j++)
|
clear_amr_params(lch_par);
|
||||||
lch_par->tch.amrActiveCodecSet[j] = GsmL1_AmrCodec_Unset;
|
|
||||||
break;
|
break;
|
||||||
case GSM48_CMODE_SPEECH_AMR:
|
case GSM48_CMODE_SPEECH_AMR:
|
||||||
|
lch_par->tch.tchPlType = GsmL1_TchPlType_Amr;
|
||||||
|
set_payload_format(lch_par);
|
||||||
lch_par->tch.amrCmiPhase = GsmL1_AmrCmiPhase_Odd; /* FIXME? */
|
lch_par->tch.amrCmiPhase = GsmL1_AmrCmiPhase_Odd; /* FIXME? */
|
||||||
if (lchan->mr_conf.icmi)
|
lch_par->tch.amrInitCodecMode = amr_get_initial_mode(lchan);
|
||||||
lch_par->tch.amrInitCodecMode = lchan->mr_conf.smod;
|
|
||||||
/* else: FIXME (implicit rule by TS 05.09 ?!?) */
|
|
||||||
|
|
||||||
/* initialize to clean state */
|
/* initialize to clean state */
|
||||||
for (j = 0; j < ARRAY_SIZE(lch_par->tch.amrActiveCodecSet); j++)
|
for (j = 0; j < ARRAY_SIZE(lch_par->tch.amrActiveCodecSet); j++)
|
||||||
@@ -531,6 +628,13 @@ static void lchan2lch_par(GsmL1_LogChParam_t *lch_par, struct gsm_lchan *lchan)
|
|||||||
if (lchan->mr_conf.m12_2)
|
if (lchan->mr_conf.m12_2)
|
||||||
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_12_2;
|
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_12_2;
|
||||||
break;
|
break;
|
||||||
|
case GSM48_CMODE_DATA_14k5:
|
||||||
|
case GSM48_CMODE_DATA_12k0:
|
||||||
|
case GSM48_CMODE_DATA_6k0:
|
||||||
|
case GSM48_CMODE_DATA_3k6:
|
||||||
|
LOGP(DL1C, LOGL_ERROR, "%s: CSD not supported!\n",
|
||||||
|
gsm_lchan_name(lchan));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,23 +675,45 @@ int lchan_activate(struct gsm_lchan *lchan)
|
|||||||
case GsmL1_Sapi_Sacch:
|
case GsmL1_Sapi_Sacch:
|
||||||
/* Only if we use manual MS power control */
|
/* Only if we use manual MS power control */
|
||||||
//act_req->logChPrm.sacch.u8MsPowerLevel = FIXME;
|
//act_req->logChPrm.sacch.u8MsPowerLevel = FIXME;
|
||||||
|
/* enable bad frame indication from >= -100dBm on SACCH */
|
||||||
|
act_req->fBFILevel = -100.0;
|
||||||
break;
|
break;
|
||||||
case GsmL1_Sapi_TchH:
|
case GsmL1_Sapi_TchH:
|
||||||
case GsmL1_Sapi_TchF:
|
case GsmL1_Sapi_TchF:
|
||||||
lchan2lch_par(lch_par, lchan);
|
lchan2lch_par(lch_par, lchan);
|
||||||
break;
|
break;
|
||||||
|
case GsmL1_Sapi_Ptcch:
|
||||||
|
lch_par->ptcch.u8Bsic = lchan->ts->trx->bts->bsic;
|
||||||
|
break;
|
||||||
|
case GsmL1_Sapi_Prach:
|
||||||
|
lch_par->prach.u8Bsic = lchan->ts->trx->bts->bsic;
|
||||||
|
break;
|
||||||
|
case GsmL1_Sapi_Pdtch:
|
||||||
|
case GsmL1_Sapi_Pacch:
|
||||||
|
/* Be sure that every packet is received, even if it
|
||||||
|
* fails. In this case the length might be lower or 0.
|
||||||
|
*/
|
||||||
|
act_req->fBFILevel = -200.0;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGP(DL1C, LOGL_INFO, "%s MPH-ACTIVATE.req (hL2=0x%08x)\n",
|
LOGP(DL1C, LOGL_INFO, "%s MPH-ACTIVATE.req (hL2=0x%08x, %s ",
|
||||||
gsm_lchan_name(lchan), act_req->hLayer2);
|
gsm_lchan_name(lchan), act_req->hLayer2,
|
||||||
|
get_value_string(femtobts_l1sapi_names, act_req->sapi));
|
||||||
|
LOGPC(DL1C, LOGL_INFO, "%s)\n",
|
||||||
|
get_value_string(femtobts_dir_names, act_req->dir));
|
||||||
|
|
||||||
/* send the primitive for all GsmL1_Sapi_* that match the LCHAN */
|
/* send the primitive for all GsmL1_Sapi_* that match the LCHAN */
|
||||||
l1if_req_compl(fl1h, msg, 0, lchan_act_compl_cb, lchan);
|
l1if_req_compl(fl1h, msg, 0, lchan_act_compl_cb, lchan);
|
||||||
|
|
||||||
}
|
}
|
||||||
lchan->state = LCHAN_S_ACT_REQ;
|
lchan_set_state(lchan, LCHAN_S_ACT_REQ);
|
||||||
|
|
||||||
|
/* set the initial ciphering parameters for both directions */
|
||||||
|
l1if_set_ciphering(fl1h, lchan, 0);
|
||||||
|
l1if_set_ciphering(fl1h, lchan, 1);
|
||||||
|
|
||||||
lchan_init_lapdm(lchan);
|
lchan_init_lapdm(lchan);
|
||||||
|
|
||||||
@@ -629,6 +755,8 @@ static void dump_lch_par(int logl, GsmL1_LogChParam_t *lch_par, GsmL1_Sapi_t sap
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* FIXME: PRACH / PTCCH */
|
/* FIXME: PRACH / PTCCH */
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
LOGPC(DL1C, logl, ")\n");
|
LOGPC(DL1C, logl, ")\n");
|
||||||
}
|
}
|
||||||
@@ -649,6 +777,29 @@ static int chmod_modif_compl_cb(struct msgb *l1_msg, void *data)
|
|||||||
&cc->cfgParams.setLogChParams.logChParams,
|
&cc->cfgParams.setLogChParams.logChParams,
|
||||||
cc->cfgParams.setLogChParams.sapi);
|
cc->cfgParams.setLogChParams.sapi);
|
||||||
break;
|
break;
|
||||||
|
case GsmL1_ConfigParamId_SetTxPowerLevel:
|
||||||
|
LOGPC(DL1C, LOGL_INFO, "setTxPower %f dBm\n",
|
||||||
|
cc->cfgParams.setTxPowerLevel.fTxPowerLevel);
|
||||||
|
break;
|
||||||
|
case GsmL1_ConfigParamId_SetCipheringParams:
|
||||||
|
switch (lchan->ciph_state) {
|
||||||
|
case LCHAN_CIPH_RX_REQ:
|
||||||
|
LOGPC(DL1C, LOGL_INFO, "RX_REQ -> RX_CONF\n");
|
||||||
|
lchan->ciph_state = LCHAN_CIPH_RX_CONF;
|
||||||
|
break;
|
||||||
|
case LCHAN_CIPH_TXRX_REQ:
|
||||||
|
LOGPC(DL1C, LOGL_INFO, "TX_REQ -> TX_CONF\n");
|
||||||
|
lchan->ciph_state = LCHAN_CIPH_TXRX_CONF;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGPC(DL1C, LOGL_INFO, "unhandled state %u\n", lchan->ciph_state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GsmL1_ConfigParamId_SetNbTsc:
|
||||||
|
default:
|
||||||
|
LOGPC(DL1C, LOGL_INFO, "\n");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
msgb_free(l1_msg);
|
msgb_free(l1_msg);
|
||||||
@@ -683,7 +834,7 @@ static int tx_confreq_logchpar(struct gsm_lchan *lchan, uint8_t direction)
|
|||||||
gsm_lchan_name(lchan),
|
gsm_lchan_name(lchan),
|
||||||
get_value_string(femtobts_l1sapi_names,
|
get_value_string(femtobts_l1sapi_names,
|
||||||
conf_req->cfgParams.setLogChParams.sapi));
|
conf_req->cfgParams.setLogChParams.sapi));
|
||||||
LOGP(DL1C, LOGL_INFO, "cfgParams Tn=%u, subCh=%u, dir=0x%x ",
|
LOGPC(DL1C, LOGL_INFO, "cfgParams Tn=%u, subCh=%u, dir=0x%x ",
|
||||||
conf_req->cfgParams.setLogChParams.u8Tn,
|
conf_req->cfgParams.setLogChParams.u8Tn,
|
||||||
conf_req->cfgParams.setLogChParams.subCh,
|
conf_req->cfgParams.setLogChParams.subCh,
|
||||||
conf_req->cfgParams.setLogChParams.dir);
|
conf_req->cfgParams.setLogChParams.dir);
|
||||||
@@ -694,10 +845,63 @@ static int tx_confreq_logchpar(struct gsm_lchan *lchan, uint8_t direction)
|
|||||||
return l1if_req_compl(fl1h, msg, 0, chmod_modif_compl_cb, lchan);
|
return l1if_req_compl(fl1h, msg, 0, chmod_modif_compl_cb, lchan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int l1if_set_txpower(struct femtol1_hdl *fl1h, float tx_power)
|
||||||
|
{
|
||||||
|
struct msgb *msg = l1p_msgb_alloc();
|
||||||
|
GsmL1_MphConfigReq_t *conf_req;
|
||||||
|
|
||||||
|
conf_req = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphConfigReq, fl1h);
|
||||||
|
conf_req->cfgParamId = GsmL1_ConfigParamId_SetTxPowerLevel;
|
||||||
|
conf_req->cfgParams.setTxPowerLevel.fTxPowerLevel = tx_power;
|
||||||
|
|
||||||
|
return l1if_req_compl(fl1h, msg, 0, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
const enum GsmL1_CipherId_t rsl2l1_ciph[] = {
|
||||||
|
[0] = GsmL1_CipherId_A50,
|
||||||
|
[1] = GsmL1_CipherId_A50,
|
||||||
|
[2] = GsmL1_CipherId_A51,
|
||||||
|
[3] = GsmL1_CipherId_A52,
|
||||||
|
[4] = GsmL1_CipherId_A53,
|
||||||
|
};
|
||||||
|
|
||||||
|
int l1if_set_ciphering(struct femtol1_hdl *fl1h,
|
||||||
|
struct gsm_lchan *lchan,
|
||||||
|
int dir_downlink)
|
||||||
|
{
|
||||||
|
struct msgb *msg = l1p_msgb_alloc();
|
||||||
|
struct GsmL1_MphConfigReq_t *cfgr;
|
||||||
|
|
||||||
|
cfgr = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphConfigReq, fl1h);
|
||||||
|
|
||||||
|
cfgr->cfgParamId = GsmL1_ConfigParamId_SetCipheringParams;
|
||||||
|
cfgr->cfgParams.setCipheringParams.u8Tn = lchan->ts->nr;
|
||||||
|
cfgr->cfgParams.setCipheringParams.subCh = lchan_to_GsmL1_SubCh_t(lchan);
|
||||||
|
|
||||||
|
if (dir_downlink)
|
||||||
|
cfgr->cfgParams.setCipheringParams.dir = GsmL1_Dir_TxDownlink;
|
||||||
|
else
|
||||||
|
cfgr->cfgParams.setCipheringParams.dir = GsmL1_Dir_RxUplink;
|
||||||
|
|
||||||
|
if (lchan->encr.alg_id >= ARRAY_SIZE(rsl2l1_ciph))
|
||||||
|
return -EINVAL;
|
||||||
|
cfgr->cfgParams.setCipheringParams.cipherId = rsl2l1_ciph[lchan->encr.alg_id];
|
||||||
|
|
||||||
|
LOGP(DL1C, LOGL_NOTICE, "%s SET_CIPHERING (ALG=%u %s)\n",
|
||||||
|
gsm_lchan_name(lchan),
|
||||||
|
cfgr->cfgParams.setCipheringParams.cipherId,
|
||||||
|
get_value_string(femtobts_dir_names,
|
||||||
|
cfgr->cfgParams.setCipheringParams.dir));
|
||||||
|
|
||||||
|
memcpy(cfgr->cfgParams.setCipheringParams.u8Kc,
|
||||||
|
lchan->encr.key, lchan->encr.key_len);
|
||||||
|
|
||||||
|
return l1if_req_compl(fl1h, msg, 0, chmod_modif_compl_cb, lchan);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int bts_model_rsl_mode_modify(struct gsm_lchan *lchan)
|
int bts_model_rsl_mode_modify(struct gsm_lchan *lchan)
|
||||||
{
|
{
|
||||||
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
|
|
||||||
|
|
||||||
/* channel mode, encryption and/or multirate have changed */
|
/* channel mode, encryption and/or multirate have changed */
|
||||||
|
|
||||||
/* update multi-rate config */
|
/* update multi-rate config */
|
||||||
@@ -715,25 +919,29 @@ static int lchan_deact_compl_cb(struct msgb *l1_msg, void *data)
|
|||||||
GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
|
GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
|
||||||
GsmL1_MphDeactivateCnf_t *ic = &l1p->u.mphDeactivateCnf;
|
GsmL1_MphDeactivateCnf_t *ic = &l1p->u.mphDeactivateCnf;
|
||||||
|
|
||||||
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.conf (%s)\n",
|
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.conf (%s ",
|
||||||
gsm_lchan_name(lchan),
|
gsm_lchan_name(lchan),
|
||||||
get_value_string(femtobts_l1sapi_names, ic->sapi));
|
get_value_string(femtobts_l1sapi_names, ic->sapi));
|
||||||
|
LOGPC(DL1C, LOGL_INFO, "%s)\n",
|
||||||
|
get_value_string(femtobts_dir_names, ic->dir));
|
||||||
|
|
||||||
if (ic->status == GsmL1_Status_Success) {
|
if (ic->status == GsmL1_Status_Success) {
|
||||||
DEBUGP(DL1C, "Successful deactivation of L1 SAPI %s on TS %u\n",
|
DEBUGP(DL1C, "Successful deactivation of L1 SAPI %s on TS %u\n",
|
||||||
get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn);
|
get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn);
|
||||||
lchan->state = LCHAN_S_ACTIVE;
|
lchan_set_state(lchan, LCHAN_S_NONE);
|
||||||
} else {
|
} else {
|
||||||
LOGP(DL1C, LOGL_ERROR, "Error deactivating L1 SAPI %s on TS %u: %s\n",
|
LOGP(DL1C, LOGL_ERROR, "Error deactivating L1 SAPI %s on TS %u: %s\n",
|
||||||
get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn,
|
get_value_string(femtobts_l1sapi_names, ic->sapi), ic->u8Tn,
|
||||||
get_value_string(femtobts_l1status_names, ic->status));
|
get_value_string(femtobts_l1status_names, ic->status));
|
||||||
lchan->state = LCHAN_S_NONE;
|
lchan_set_state(lchan, LCHAN_S_REL_ERR);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ic->sapi) {
|
switch (ic->sapi) {
|
||||||
case GsmL1_Sapi_Sdcch:
|
case GsmL1_Sapi_Sdcch:
|
||||||
case GsmL1_Sapi_TchF:
|
case GsmL1_Sapi_TchF:
|
||||||
/* FIXME: Send RSL CHAN REL ACK */
|
case GsmL1_Sapi_TchH:
|
||||||
|
if (ic->dir == GsmL1_Dir_TxDownlink)
|
||||||
|
rsl_tx_rf_rel_ack(lchan);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -751,18 +959,29 @@ int lchan_deactivate(struct gsm_lchan *lchan)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = s4l->num_sapis-1; i >= 0; i--) {
|
for (i = s4l->num_sapis-1; i >= 0; i--) {
|
||||||
struct msgb *msg = l1p_msgb_alloc();
|
struct msgb *msg;
|
||||||
GsmL1_MphDeactivateReq_t *deact_req;
|
GsmL1_MphDeactivateReq_t *deact_req;
|
||||||
|
|
||||||
|
if (s4l->sapis[i].sapi == GsmL1_Sapi_Sacch && lchan->sacch_deact) {
|
||||||
|
LOGP(DL1C, LOGL_INFO, "%s SACCH already deactivated.\n",
|
||||||
|
gsm_lchan_name(lchan));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
msg = l1p_msgb_alloc();
|
||||||
|
|
||||||
deact_req = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphDeactivateReq, fl1h);
|
deact_req = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphDeactivateReq, fl1h);
|
||||||
deact_req->u8Tn = lchan->ts->nr;
|
deact_req->u8Tn = lchan->ts->nr;
|
||||||
deact_req->subCh = lchan_to_GsmL1_SubCh_t(lchan);
|
deact_req->subCh = lchan_to_GsmL1_SubCh_t(lchan);
|
||||||
deact_req->dir = s4l->sapis[i].dir;
|
deact_req->dir = s4l->sapis[i].dir;
|
||||||
deact_req->sapi = s4l->sapis[i].sapi;
|
deact_req->sapi = s4l->sapis[i].sapi;
|
||||||
|
|
||||||
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.req (%s)\n",
|
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.req (%s ",
|
||||||
gsm_lchan_name(lchan),
|
gsm_lchan_name(lchan),
|
||||||
get_value_string(femtobts_l1sapi_names, deact_req->sapi));
|
get_value_string(femtobts_l1sapi_names, deact_req->sapi));
|
||||||
|
LOGPC(DL1C, LOGL_INFO, "%s)\n",
|
||||||
|
get_value_string(femtobts_dir_names, deact_req->dir));
|
||||||
|
|
||||||
/* Stop the alive timer once we deactivate the SCH */
|
/* Stop the alive timer once we deactivate the SCH */
|
||||||
if (deact_req->sapi == GsmL1_Sapi_Sch)
|
if (deact_req->sapi == GsmL1_Sapi_Sch)
|
||||||
@@ -772,12 +991,13 @@ int lchan_deactivate(struct gsm_lchan *lchan)
|
|||||||
l1if_req_compl(fl1h, msg, 0, lchan_deact_compl_cb, lchan);
|
l1if_req_compl(fl1h, msg, 0, lchan_deact_compl_cb, lchan);
|
||||||
|
|
||||||
}
|
}
|
||||||
lchan->state = LCHAN_S_ACT_REQ;
|
lchan_set_state(lchan, LCHAN_S_REL_REQ);
|
||||||
|
lchan->ciph_state = 0; /* FIXME: do this in common/\*.c */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lchan_deactivate_sacch(struct gsm_lchan *lchan)
|
static int lchan_deactivate_sacch(struct gsm_lchan *lchan)
|
||||||
{
|
{
|
||||||
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
|
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
|
||||||
struct msgb *msg = l1p_msgb_alloc();
|
struct msgb *msg = l1p_msgb_alloc();
|
||||||
@@ -789,8 +1009,11 @@ int lchan_deactivate_sacch(struct gsm_lchan *lchan)
|
|||||||
deact_req->dir = DIR_BOTH;
|
deact_req->dir = DIR_BOTH;
|
||||||
deact_req->sapi = GsmL1_Sapi_Sacch;
|
deact_req->sapi = GsmL1_Sapi_Sacch;
|
||||||
|
|
||||||
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.req (SACCH)\n",
|
lchan->sacch_deact = 1;
|
||||||
gsm_lchan_name(lchan));
|
|
||||||
|
LOGP(DL1C, LOGL_INFO, "%s MPH-DEACTIVATE.req (SACCH %s)\n",
|
||||||
|
gsm_lchan_name(lchan),
|
||||||
|
get_value_string(femtobts_dir_names, deact_req->dir));
|
||||||
|
|
||||||
/* send the primitive for all GsmL1_Sapi_* that match the LCHAN */
|
/* send the primitive for all GsmL1_Sapi_* that match the LCHAN */
|
||||||
return l1if_req_compl(fl1h, msg, 0, lchan_deact_compl_cb, lchan);
|
return l1if_req_compl(fl1h, msg, 0, lchan_deact_compl_cb, lchan);
|
||||||
@@ -837,6 +1060,9 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
|||||||
case NM_OC_BTS:
|
case NM_OC_BTS:
|
||||||
case NM_OC_SITE_MANAGER:
|
case NM_OC_SITE_MANAGER:
|
||||||
case NM_OC_BASEB_TRANSC:
|
case NM_OC_BASEB_TRANSC:
|
||||||
|
case NM_OC_GPRS_NSE:
|
||||||
|
case NM_OC_GPRS_CELL:
|
||||||
|
case NM_OC_GPRS_NSVC:
|
||||||
oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
|
oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
|
||||||
rc = oml_mo_opstart_ack(mo);
|
rc = oml_mo_opstart_ack(mo);
|
||||||
break;
|
break;
|
||||||
@@ -851,5 +1077,36 @@ int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
|||||||
{
|
{
|
||||||
/* blindly accept all state changes */
|
/* blindly accept all state changes */
|
||||||
mo->nm_state.administrative = adm_state;
|
mo->nm_state.administrative = adm_state;
|
||||||
return oml_mo_fom_ack_nack(mo, NM_MT_CHG_ADM_STATE, 0);
|
return oml_mo_statechg_ack(mo);
|
||||||
|
}
|
||||||
|
int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp)
|
||||||
|
{
|
||||||
|
//uint8_t mode = *TLVP_VAL(tp, RSL_IE_CHAN_MODE);
|
||||||
|
//uint8_t type = *TLVP_VAL(tp, RSL_IE_ACT_TYPE);
|
||||||
|
|
||||||
|
lchan->sacch_deact = 0;
|
||||||
|
lchan_activate(lchan);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bts_model_rsl_chan_rel(struct gsm_lchan *lchan)
|
||||||
|
{
|
||||||
|
/* A duplicate RF Release Request, ignore it */
|
||||||
|
if (lchan->state == LCHAN_S_REL_REQ)
|
||||||
|
return 0;
|
||||||
|
lchan_deactivate(lchan);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan)
|
||||||
|
{
|
||||||
|
return lchan_deactivate_sacch(lchan);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx)
|
||||||
|
{
|
||||||
|
struct femtol1_hdl *fl1 = trx_femtol1_hdl(trx);
|
||||||
|
|
||||||
|
trx_close(trx);
|
||||||
|
return l1if_activate_rf(fl1, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/* VTY interface for sysmoBTS */
|
/* VTY interface for sysmoBTS */
|
||||||
|
|
||||||
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||||
|
* (C) 2012 by Holger Hans Peter Freyther
|
||||||
*
|
*
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*
|
*
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
@@ -35,68 +37,155 @@
|
|||||||
|
|
||||||
#include <osmocom/vty/vty.h>
|
#include <osmocom/vty/vty.h>
|
||||||
#include <osmocom/vty/command.h>
|
#include <osmocom/vty/command.h>
|
||||||
|
#include <osmocom/vty/misc.h>
|
||||||
|
|
||||||
#include <osmo-bts/gsm_data.h>
|
#include <osmo-bts/gsm_data.h>
|
||||||
#include <osmo-bts/logging.h>
|
#include <osmo-bts/logging.h>
|
||||||
|
#include <osmo-bts/vty.h>
|
||||||
|
|
||||||
#include "femtobts.h"
|
#include "femtobts.h"
|
||||||
#include "l1_if.h"
|
#include "l1_if.h"
|
||||||
|
|
||||||
|
#define TRX_STR "Transceiver related commands\n" "TRX number\n"
|
||||||
|
|
||||||
|
#define SHOW_TRX_STR \
|
||||||
|
SHOW_STR \
|
||||||
|
TRX_STR
|
||||||
|
#define DSP_TRACE_F_STR "DSP Trace Flag\n"
|
||||||
|
|
||||||
static struct gsm_bts *vty_bts;
|
static struct gsm_bts *vty_bts;
|
||||||
|
|
||||||
/* This generates the logging command string for VTY. */
|
/* configuration */
|
||||||
const char *vty_cmd_string_from_valstr(const struct value_string *vals,
|
|
||||||
const char *prefix)
|
DEFUN(cfg_trx_gsmtap_sapi, cfg_trx_gsmtap_sapi_cmd,
|
||||||
|
"HIDDEN", "HIDDEN")
|
||||||
{
|
{
|
||||||
int len = 0, offset = 0, ret, i, rem;
|
struct gsm_bts_trx *trx = vty->index;
|
||||||
int size = strlen(prefix);
|
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||||
const struct value_string *vs;
|
int sapi;
|
||||||
char *str;
|
|
||||||
|
|
||||||
for (vs = vals; vs->value || vs->str; vs++)
|
sapi = get_string_value(femtobts_l1sapi_names, argv[0]);
|
||||||
size += strlen(vs->str) + 1;
|
|
||||||
|
|
||||||
rem = size;
|
fl1h->gsmtap_sapi_mask |= (1 << sapi);
|
||||||
str = talloc_zero_size(vty_bts, size);
|
|
||||||
if (!str)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
ret = snprintf(str + offset, rem, prefix);
|
return CMD_SUCCESS;
|
||||||
if (ret < 0)
|
|
||||||
goto err;
|
|
||||||
OSMO_SNPRINTF_RET(ret, rem, offset, len);
|
|
||||||
|
|
||||||
for (vs = vals; vs->value || vs->str; vs++) {
|
|
||||||
if (vs->str) {
|
|
||||||
int j, name_len = strlen(vs->str)+1;
|
|
||||||
char name[name_len];
|
|
||||||
|
|
||||||
for (j = 0; j < name_len; j++)
|
|
||||||
name[j] = tolower(vs->str[j]);
|
|
||||||
|
|
||||||
name[name_len-1] = '\0';
|
|
||||||
ret = snprintf(str + offset, rem, "%s|", name);
|
|
||||||
if (ret < 0)
|
|
||||||
goto err;
|
|
||||||
OSMO_SNPRINTF_RET(ret, rem, offset, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
offset--; /* to remove the trailing | */
|
|
||||||
rem++;
|
|
||||||
|
|
||||||
ret = snprintf(str + offset, rem, ")");
|
|
||||||
if (ret < 0)
|
|
||||||
goto err;
|
|
||||||
OSMO_SNPRINTF_RET(ret, rem, offset, len);
|
|
||||||
err:
|
|
||||||
str[size-1] = '\0';
|
|
||||||
return str;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_trx_no_gsmtap_sapi, cfg_trx_no_gsmtap_sapi_cmd,
|
||||||
|
"HIDDEN", "HIDDEN")
|
||||||
|
{
|
||||||
|
struct gsm_bts_trx *trx = vty->index;
|
||||||
|
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||||
|
int sapi;
|
||||||
|
|
||||||
|
sapi = get_string_value(femtobts_l1sapi_names, argv[0]);
|
||||||
|
|
||||||
|
fl1h->gsmtap_sapi_mask &= ~(1 << sapi);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_trx_clkcal_def, cfg_trx_clkcal_def_cmd,
|
||||||
|
"clock-calibration default",
|
||||||
|
"Set the clock calibration value\n" "Default Clock DAC value\n")
|
||||||
|
{
|
||||||
|
struct gsm_bts_trx *trx = vty->index;
|
||||||
|
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||||
|
|
||||||
|
fl1h->clk_cal = 0xffff;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HW_SYSMOBTS_V1
|
||||||
|
DEFUN(cfg_trx_clkcal, cfg_trx_clkcal_cmd,
|
||||||
|
"clock-calibration <0-4095>",
|
||||||
|
"Set the clock calibration value\n" "Clock DAC value\n")
|
||||||
|
{
|
||||||
|
unsigned int clkcal = atoi(argv[0]);
|
||||||
|
struct gsm_bts_trx *trx = vty->index;
|
||||||
|
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||||
|
|
||||||
|
fl1h->clk_cal = clkcal & 0xfff;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
DEFUN(cfg_trx_clkcal, cfg_trx_clkcal_cmd,
|
||||||
|
"clock-calibration <-4095-4095>",
|
||||||
|
"Set the clock calibration value\n" "Offset in PPB\n")
|
||||||
|
{
|
||||||
|
int clkcal = atoi(argv[0]);
|
||||||
|
struct gsm_bts_trx *trx = vty->index;
|
||||||
|
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||||
|
|
||||||
|
fl1h->clk_cal = clkcal;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DEFUN(cfg_trx_clksrc, cfg_trx_clksrc_cmd,
|
||||||
|
"clock-source (tcxo|ocxo|ext|gps)",
|
||||||
|
"Set the clock source value\n"
|
||||||
|
"Use the TCXO\n"
|
||||||
|
"Use the OCXO\n"
|
||||||
|
"Use an external clock\n"
|
||||||
|
"Use the GPS pps\n")
|
||||||
|
{
|
||||||
|
struct gsm_bts_trx *trx = vty->index;
|
||||||
|
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = get_string_value(femtobts_clksrc_names, argv[0]);
|
||||||
|
if (rc < 0)
|
||||||
|
return CMD_WARNING;
|
||||||
|
|
||||||
|
fl1h->clk_src = rc;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_trx_cal_path, cfg_trx_cal_path_cmd,
|
||||||
|
"trx-calibration-path PATH",
|
||||||
|
"Set the path name to TRX calibration data\n" "Path name\n")
|
||||||
|
{
|
||||||
|
struct gsm_bts_trx *trx = vty->index;
|
||||||
|
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||||
|
|
||||||
|
if (fl1h->calib_path)
|
||||||
|
talloc_free(fl1h->calib_path);
|
||||||
|
|
||||||
|
fl1h->calib_path = talloc_strdup(fl1h, argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* runtime */
|
||||||
|
|
||||||
|
DEFUN(show_trx_clksrc, show_trx_clksrc_cmd,
|
||||||
|
"show trx <0-0> clock-source",
|
||||||
|
SHOW_TRX_STR "Display the clock source for this TRX")
|
||||||
|
{
|
||||||
|
int trx_nr = atoi(argv[0]);
|
||||||
|
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
|
||||||
|
struct femtol1_hdl *fl1h;
|
||||||
|
|
||||||
|
if (!trx)
|
||||||
|
return CMD_WARNING;
|
||||||
|
|
||||||
|
fl1h = trx_femtol1_hdl(trx);
|
||||||
|
|
||||||
|
vty_out(vty, "TRX Clock Source: %s%s",
|
||||||
|
get_value_string(femtobts_clksrc_names, fl1h->clk_src),
|
||||||
|
VTY_NEWLINE);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
DEFUN(show_dsp_trace_f, show_dsp_trace_f_cmd,
|
DEFUN(show_dsp_trace_f, show_dsp_trace_f_cmd,
|
||||||
"show trx <0-0> dsp-trace-flags",
|
"show trx <0-0> dsp-trace-flags",
|
||||||
SHOW_STR "Display the current setting of the DSP trace flags")
|
SHOW_TRX_STR "Display the current setting of the DSP trace flags")
|
||||||
{
|
{
|
||||||
int trx_nr = atoi(argv[0]);
|
int trx_nr = atoi(argv[0]);
|
||||||
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
|
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
|
||||||
@@ -130,7 +219,7 @@ DEFUN(show_dsp_trace_f, show_dsp_trace_f_cmd,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFUN(dsp_trace_f, dsp_trace_f_cmd, "HIDDEN", "HIDDEN")
|
DEFUN(dsp_trace_f, dsp_trace_f_cmd, "HIDDEN", TRX_STR)
|
||||||
{
|
{
|
||||||
int trx_nr = atoi(argv[0]);
|
int trx_nr = atoi(argv[0]);
|
||||||
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
|
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
|
||||||
@@ -150,7 +239,7 @@ DEFUN(dsp_trace_f, dsp_trace_f_cmd, "HIDDEN", "HIDDEN")
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFUN(no_dsp_trace_f, no_dsp_trace_f_cmd, "HIDDEN", "HIDDEN")
|
DEFUN(no_dsp_trace_f, no_dsp_trace_f_cmd, "HIDDEN", NO_STR TRX_STR)
|
||||||
{
|
{
|
||||||
int trx_nr = atoi(argv[0]);
|
int trx_nr = atoi(argv[0]);
|
||||||
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
|
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
|
||||||
@@ -170,20 +259,222 @@ DEFUN(no_dsp_trace_f, no_dsp_trace_f_cmd, "HIDDEN", "HIDDEN")
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFUN(show_sys_info, show_sys_info_cmd,
|
||||||
|
"show trx <0-0> system-information",
|
||||||
|
SHOW_TRX_STR "Display information about system\n")
|
||||||
|
{
|
||||||
|
int trx_nr = atoi(argv[0]);
|
||||||
|
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
|
||||||
|
struct femtol1_hdl *fl1h;
|
||||||
|
int i;
|
||||||
|
|
||||||
int femtol1_vty_init(struct gsm_bts *bts)
|
if (!trx) {
|
||||||
|
vty_out(vty, "Cannot find TRX number %u%s",
|
||||||
|
trx_nr, VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
fl1h = trx_femtol1_hdl(trx);
|
||||||
|
|
||||||
|
vty_out(vty, "DSP Version: %u.%u.%u, FPGA Version: %u.%u.%u%s",
|
||||||
|
fl1h->hw_info.dsp_version[0],
|
||||||
|
fl1h->hw_info.dsp_version[1],
|
||||||
|
fl1h->hw_info.dsp_version[2],
|
||||||
|
fl1h->hw_info.fpga_version[0],
|
||||||
|
fl1h->hw_info.fpga_version[1],
|
||||||
|
fl1h->hw_info.fpga_version[2], VTY_NEWLINE);
|
||||||
|
|
||||||
|
vty_out(vty, "GSM Band Support: ");
|
||||||
|
for (i = 0; i < sizeof(fl1h->hw_info.band_support); i++) {
|
||||||
|
if (fl1h->hw_info.band_support & (1 << i))
|
||||||
|
vty_out(vty, "%s ", gsm_band_name(1 << i));
|
||||||
|
}
|
||||||
|
vty_out(vty, "%s", VTY_NEWLINE);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(activate_lchan, activate_lchan_cmd,
|
||||||
|
"trx <0-0> <0-7> (activate|deactivate) <0-7>",
|
||||||
|
TRX_STR
|
||||||
|
"Timeslot number\n"
|
||||||
|
"Activate Logical Channel\n"
|
||||||
|
"Deactivate Logical Channel\n"
|
||||||
|
"Logical Channel Number\n" )
|
||||||
|
{
|
||||||
|
int trx_nr = atoi(argv[0]);
|
||||||
|
int ts_nr = atoi(argv[1]);
|
||||||
|
int lchan_nr = atoi(argv[3]);
|
||||||
|
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
|
||||||
|
struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
|
||||||
|
struct gsm_lchan *lchan = &ts->lchan[lchan_nr];
|
||||||
|
|
||||||
|
if (!strcmp(argv[2], "activate"))
|
||||||
|
lchan_activate(lchan);
|
||||||
|
else
|
||||||
|
lchan_deactivate(lchan);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(set_tx_power, set_tx_power_cmd,
|
||||||
|
"trx <0-0> tx-power <-110-23>",
|
||||||
|
TRX_STR
|
||||||
|
"Set transmit power (override BSC)\n"
|
||||||
|
"Transmit power in dBm\n")
|
||||||
|
{
|
||||||
|
int trx_nr = atoi(argv[0]);
|
||||||
|
int power = atoi(argv[1]);
|
||||||
|
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
|
||||||
|
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||||
|
|
||||||
|
l1if_set_txpower(fl1h, (float) power);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(loopback, loopback_cmd,
|
||||||
|
"trx <0-0> <0-7> loopback <0-1>",
|
||||||
|
TRX_STR
|
||||||
|
"Timeslot number\n"
|
||||||
|
"Set TCH loopback\n"
|
||||||
|
"Logical Channel Number\n")
|
||||||
|
{
|
||||||
|
int trx_nr = atoi(argv[0]);
|
||||||
|
int ts_nr = atoi(argv[1]);
|
||||||
|
int lchan_nr = atoi(argv[2]);
|
||||||
|
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
|
||||||
|
struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
|
||||||
|
struct gsm_lchan *lchan = &ts->lchan[lchan_nr];
|
||||||
|
|
||||||
|
lchan->loopback = 1;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(no_loopback, no_loopback_cmd,
|
||||||
|
"no trx <0-0> <0-7> loopback <0-1>",
|
||||||
|
NO_STR TRX_STR
|
||||||
|
"Timeslot number\n"
|
||||||
|
"Set TCH loopback\n"
|
||||||
|
"Logical Channel Number\n")
|
||||||
|
{
|
||||||
|
int trx_nr = atoi(argv[0]);
|
||||||
|
int ts_nr = atoi(argv[1]);
|
||||||
|
int lchan_nr = atoi(argv[2]);
|
||||||
|
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
|
||||||
|
struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
|
||||||
|
struct gsm_lchan *lchan = &ts->lchan[lchan_nr];
|
||||||
|
|
||||||
|
lchan->loopback = 0;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: move to libosmocore ? */
|
||||||
|
static char buf_casecnvt[256];
|
||||||
|
char *osmo_str_tolower(const char *in)
|
||||||
|
{
|
||||||
|
int len, i;
|
||||||
|
|
||||||
|
if (!in)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
len = strlen(in);
|
||||||
|
if (len > sizeof(buf_casecnvt))
|
||||||
|
len = sizeof(buf_casecnvt);
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
buf_casecnvt[i] = tolower(in[i]);
|
||||||
|
if (in[i] == '\0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i < sizeof(buf_casecnvt))
|
||||||
|
buf_casecnvt[i] = '\0';
|
||||||
|
|
||||||
|
/* just to make sure we're always zero-terminated */
|
||||||
|
buf_casecnvt[sizeof(buf_casecnvt)-1] = '\0';
|
||||||
|
|
||||||
|
return buf_casecnvt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
|
||||||
|
{
|
||||||
|
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
vty_out(vty, " clock-calibration %d%s", fl1h->clk_cal,
|
||||||
|
VTY_NEWLINE);
|
||||||
|
vty_out(vty, " trx-calibration-path %s%s", fl1h->calib_path,
|
||||||
|
VTY_NEWLINE);
|
||||||
|
vty_out(vty, " clock-source %s%s",
|
||||||
|
get_value_string(femtobts_clksrc_names, fl1h->clk_src),
|
||||||
|
VTY_NEWLINE);
|
||||||
|
|
||||||
|
for (i = 0; i < 32; i++) {
|
||||||
|
if (fl1h->gsmtap_sapi_mask & (1 << i)) {
|
||||||
|
const char *name = get_value_string(femtobts_l1sapi_names, i);
|
||||||
|
vty_out(vty, " gsmtap-sapi %s%s", osmo_str_tolower(name),
|
||||||
|
VTY_NEWLINE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int bts_model_vty_init(struct gsm_bts *bts)
|
||||||
{
|
{
|
||||||
vty_bts = bts;
|
vty_bts = bts;
|
||||||
|
|
||||||
/* runtime-patch the command strings with debug levels */
|
/* runtime-patch the command strings with debug levels */
|
||||||
dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(femtobts_tracef_names,
|
dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_tracef_names,
|
||||||
"trx <0-0> dsp-trace-flag (");
|
"trx <0-0> dsp-trace-flag (",
|
||||||
no_dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(femtobts_tracef_names,
|
"|",")", VTY_DO_LOWER);
|
||||||
"no trx <0-0> dsp-trace-flag (");
|
dsp_trace_f_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_tracef_docs,
|
||||||
|
TRX_STR DSP_TRACE_F_STR,
|
||||||
|
"\n", "", 0);
|
||||||
|
|
||||||
|
no_dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_tracef_names,
|
||||||
|
"no trx <0-0> dsp-trace-flag (",
|
||||||
|
"|",")", VTY_DO_LOWER);
|
||||||
|
no_dsp_trace_f_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_tracef_docs,
|
||||||
|
NO_STR TRX_STR DSP_TRACE_F_STR,
|
||||||
|
"\n", "", 0);
|
||||||
|
|
||||||
|
cfg_trx_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
|
||||||
|
"gsmtap-sapi (",
|
||||||
|
"|",")", VTY_DO_LOWER);
|
||||||
|
cfg_trx_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
|
||||||
|
"GSMTAP SAPI\n",
|
||||||
|
"\n", "", 0);
|
||||||
|
|
||||||
|
cfg_trx_no_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
|
||||||
|
"no gsmtap-sapi (",
|
||||||
|
"|",")", VTY_DO_LOWER);
|
||||||
|
cfg_trx_no_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
|
||||||
|
NO_STR "GSMTAP SAPI\n",
|
||||||
|
"\n", "", 0);
|
||||||
|
|
||||||
install_element_ve(&show_dsp_trace_f_cmd);
|
install_element_ve(&show_dsp_trace_f_cmd);
|
||||||
|
install_element_ve(&show_sys_info_cmd);
|
||||||
|
install_element_ve(&show_trx_clksrc_cmd);
|
||||||
install_element_ve(&dsp_trace_f_cmd);
|
install_element_ve(&dsp_trace_f_cmd);
|
||||||
install_element_ve(&no_dsp_trace_f_cmd);
|
install_element_ve(&no_dsp_trace_f_cmd);
|
||||||
|
|
||||||
|
install_element(ENABLE_NODE, &activate_lchan_cmd);
|
||||||
|
install_element(ENABLE_NODE, &set_tx_power_cmd);
|
||||||
|
|
||||||
|
install_element(ENABLE_NODE, &loopback_cmd);
|
||||||
|
install_element(ENABLE_NODE, &no_loopback_cmd);
|
||||||
|
|
||||||
|
install_element(TRX_NODE, &cfg_trx_clkcal_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_trx_clkcal_def_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_trx_clksrc_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_trx_cal_path_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_trx_gsmtap_sapi_cmd);
|
||||||
|
install_element(TRX_NODE, &cfg_trx_no_gsmtap_sapi_cmd);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* Traffic channel support for Sysmocom BTS L1 */
|
/* Traffic channel support for Sysmocom BTS L1 */
|
||||||
|
|
||||||
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
/* (C) 2011-2012 by Harald Welte <laforge@gnumonks.org>
|
||||||
*
|
*
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*
|
*
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
#include <osmo-bts/gsm_data.h>
|
#include <osmo-bts/gsm_data.h>
|
||||||
#include <osmo-bts/measurement.h>
|
#include <osmo-bts/measurement.h>
|
||||||
|
|
||||||
#include <sysmocom/femtobts/femtobts.h>
|
#include <sysmocom/femtobts/superfemto.h>
|
||||||
#include <sysmocom/femtobts/gsml1prim.h>
|
#include <sysmocom/femtobts/gsml1prim.h>
|
||||||
#include <sysmocom/femtobts/gsml1const.h>
|
#include <sysmocom/femtobts/gsml1const.h>
|
||||||
#include <sysmocom/femtobts/gsml1types.h>
|
#include <sysmocom/femtobts/gsml1types.h>
|
||||||
@@ -97,9 +97,18 @@ void osmo_nibble_shift_left_unal(uint8_t *out, const uint8_t *in,
|
|||||||
|
|
||||||
static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len)
|
static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len)
|
||||||
{
|
{
|
||||||
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
|
struct msgb *msg;
|
||||||
uint8_t *cur;
|
uint8_t *cur;
|
||||||
|
|
||||||
|
msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
|
||||||
|
if (!msg)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
#ifdef USE_L1_RTP_MODE
|
||||||
|
/* new L1 can deliver bits like we need them */
|
||||||
|
cur = msgb_put(msg, GSM_FR_BYTES);
|
||||||
|
memcpy(cur, l1_payload, GSM_FR_BYTES);
|
||||||
|
#else
|
||||||
/* step1: reverse the bit-order of each payload byte */
|
/* step1: reverse the bit-order of each payload byte */
|
||||||
osmo_revbytebits_buf(l1_payload, payload_len);
|
osmo_revbytebits_buf(l1_payload, payload_len);
|
||||||
|
|
||||||
@@ -109,6 +118,7 @@ static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len
|
|||||||
osmo_nibble_shift_right(cur, l1_payload, GSM_FR_BITS/4);
|
osmo_nibble_shift_right(cur, l1_payload, GSM_FR_BITS/4);
|
||||||
|
|
||||||
cur[0] |= 0xD0;
|
cur[0] |= 0xD0;
|
||||||
|
#endif /* USE_L1_RTP_MODE */
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
@@ -119,24 +129,37 @@ static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len
|
|||||||
* \param[in] payload_len length of \a rtp_payload
|
* \param[in] payload_len length of \a rtp_payload
|
||||||
* \returns number of \a l1_payload bytes filled
|
* \returns number of \a l1_payload bytes filled
|
||||||
*/
|
*/
|
||||||
static int rtppayload_to_l1_fr(uint8_t *l1_payload, uint8_t *rtp_payload,
|
static int rtppayload_to_l1_fr(uint8_t *l1_payload, const uint8_t *rtp_payload,
|
||||||
unsigned int payload_len)
|
unsigned int payload_len)
|
||||||
{
|
{
|
||||||
|
#ifdef USE_L1_RTP_MODE
|
||||||
|
/* new L1 can deliver bits like we need them */
|
||||||
|
memcpy(l1_payload, rtp_payload, GSM_FR_BYTES);
|
||||||
|
#else
|
||||||
/* step2: we need to shift the RTP payload left by one nibble*/
|
/* step2: we need to shift the RTP payload left by one nibble*/
|
||||||
osmo_nibble_shift_left_unal(l1_payload, rtp_payload, GSM_FR_BITS/4);
|
osmo_nibble_shift_left_unal(l1_payload, rtp_payload, GSM_FR_BITS/4);
|
||||||
|
|
||||||
/* step1: reverse the bit-order of each payload byte */
|
/* step1: reverse the bit-order of each payload byte */
|
||||||
osmo_revbytebits_buf(l1_payload, payload_len);
|
osmo_revbytebits_buf(l1_payload, payload_len);
|
||||||
|
#endif /* USE_L1_RTP_MODE */
|
||||||
return GSM_FR_BYTES;
|
return GSM_FR_BYTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef GsmL1_TchPlType_Efr
|
#if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
|
||||||
static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload, uint8_t payload_len)
|
static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload, uint8_t payload_len)
|
||||||
{
|
{
|
||||||
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
|
struct msgb *msg;
|
||||||
uint8_t *cur;
|
uint8_t *cur;
|
||||||
|
|
||||||
|
msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
|
||||||
|
if (!msg)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
#ifdef USE_L1_RTP_MODE
|
||||||
|
/* new L1 can deliver bits like we need them */
|
||||||
|
cur = msgb_put(msg, GSM_EFR_BYTES);
|
||||||
|
memcpy(cur, l1_payload, GSM_EFR_BYTES);
|
||||||
|
#else
|
||||||
/* step1: reverse the bit-order of each payload byte */
|
/* step1: reverse the bit-order of each payload byte */
|
||||||
osmo_revbytebits_buf(l1_payload, payload_len);
|
osmo_revbytebits_buf(l1_payload, payload_len);
|
||||||
|
|
||||||
@@ -146,18 +169,34 @@ static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload, uint8_t payload_le
|
|||||||
osmo_nibble_shift_right(cur, l1_payload, GSM_EFR_BITS/4);
|
osmo_nibble_shift_right(cur, l1_payload, GSM_EFR_BITS/4);
|
||||||
|
|
||||||
cur[0] |= 0xC0;
|
cur[0] |= 0xC0;
|
||||||
|
#endif /* USE_L1_RTP_MODE */
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rtppayload_to_l1_efr(uint8_t *l1_payload, const uint8_t *rtp_payload,
|
||||||
|
unsigned int payload_len)
|
||||||
|
{
|
||||||
|
#ifndef USE_L1_RTP_MODE
|
||||||
|
#error We don't support EFR with L1 that doesn't support RTP mode!
|
||||||
|
#else
|
||||||
|
memcpy(l1_payload, rtp_payload, payload_len);
|
||||||
|
|
||||||
|
return payload_len;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
#warning No EFR support in L1
|
#warning No EFR support in L1
|
||||||
#endif
|
#endif /* L1_HAS_EFR */
|
||||||
|
|
||||||
static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len)
|
static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len)
|
||||||
{
|
{
|
||||||
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
|
struct msgb *msg;
|
||||||
uint8_t *cur;
|
uint8_t *cur;
|
||||||
|
|
||||||
|
msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
|
||||||
|
if (!msg)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
if (payload_len != GSM_HR_BYTES) {
|
if (payload_len != GSM_HR_BYTES) {
|
||||||
LOGP(DL1C, LOGL_ERROR, "L1 HR frame length %u != expected %u\n",
|
LOGP(DL1C, LOGL_ERROR, "L1 HR frame length %u != expected %u\n",
|
||||||
payload_len, GSM_HR_BYTES);
|
payload_len, GSM_HR_BYTES);
|
||||||
@@ -167,8 +206,10 @@ static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len
|
|||||||
cur = msgb_put(msg, GSM_HR_BYTES);
|
cur = msgb_put(msg, GSM_HR_BYTES);
|
||||||
memcpy(cur, l1_payload, GSM_HR_BYTES);
|
memcpy(cur, l1_payload, GSM_HR_BYTES);
|
||||||
|
|
||||||
|
#ifndef USE_L1_RTP_MODE
|
||||||
/* reverse the bit-order of each payload byte */
|
/* reverse the bit-order of each payload byte */
|
||||||
osmo_revbytebits_buf(cur, GSM_HR_BYTES);
|
osmo_revbytebits_buf(cur, GSM_HR_BYTES);
|
||||||
|
#endif /* USE_L1_RTP_MODE */
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
@@ -179,7 +220,7 @@ static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len
|
|||||||
* \param[in] payload_len length of \a rtp_payload
|
* \param[in] payload_len length of \a rtp_payload
|
||||||
* \returns number of \a l1_payload bytes filled
|
* \returns number of \a l1_payload bytes filled
|
||||||
*/
|
*/
|
||||||
static int rtppayload_to_l1_hr(uint8_t *l1_payload, uint8_t *rtp_payload,
|
static int rtppayload_to_l1_hr(uint8_t *l1_payload, const uint8_t *rtp_payload,
|
||||||
unsigned int payload_len)
|
unsigned int payload_len)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -191,8 +232,10 @@ static int rtppayload_to_l1_hr(uint8_t *l1_payload, uint8_t *rtp_payload,
|
|||||||
|
|
||||||
memcpy(l1_payload, rtp_payload, GSM_HR_BYTES);
|
memcpy(l1_payload, rtp_payload, GSM_HR_BYTES);
|
||||||
|
|
||||||
|
#ifndef USE_L1_RTP_MODE
|
||||||
/* reverse the bit-order of each payload byte */
|
/* reverse the bit-order of each payload byte */
|
||||||
osmo_revbytebits_buf(l1_payload, GSM_HR_BYTES);
|
osmo_revbytebits_buf(l1_payload, GSM_HR_BYTES);
|
||||||
|
#endif /* USE_L1_RTP_MODE */
|
||||||
|
|
||||||
return GSM_HR_BYTES;
|
return GSM_HR_BYTES;
|
||||||
}
|
}
|
||||||
@@ -203,14 +246,19 @@ static int rtppayload_to_l1_hr(uint8_t *l1_payload, uint8_t *rtp_payload,
|
|||||||
static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_len,
|
static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_len,
|
||||||
struct amr_multirate_conf *amr_mrc)
|
struct amr_multirate_conf *amr_mrc)
|
||||||
{
|
{
|
||||||
struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
|
struct msgb *msg;
|
||||||
uint8_t *cur;
|
uint8_t *cur;
|
||||||
u_int8_t cmr;
|
u_int8_t cmr;
|
||||||
uint8_t ft = l1_payload[2] & 0xF;
|
uint8_t ft = l1_payload[2] & 0xF;
|
||||||
uint8_t cmr_idx = l1_payload[1];
|
|
||||||
uint8_t amr_if2_len = payload_len - 2;
|
uint8_t amr_if2_len = payload_len - 2;
|
||||||
|
|
||||||
|
msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
|
||||||
|
if (!msg)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
uint8_t cmr_idx = l1_payload[1];
|
||||||
|
|
||||||
/* CMR == Unset means CMR was not transmitted at this TDMA */
|
/* CMR == Unset means CMR was not transmitted at this TDMA */
|
||||||
if (cmr_idx >= GsmL1_AmrCodecMode_Unset)
|
if (cmr_idx >= GsmL1_AmrCodecMode_Unset)
|
||||||
cmr = AMR_CMR_NONE;
|
cmr = AMR_CMR_NONE;
|
||||||
@@ -225,6 +273,10 @@ static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_le
|
|||||||
cmr = AMR_CMR_NONE;
|
cmr = AMR_CMR_NONE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_L1_RTP_MODE
|
||||||
|
cur = msgb_put(msg, amr_if2_len);
|
||||||
|
memcpy(cur, l1_payload+2, amr_if2_len);
|
||||||
|
#else
|
||||||
/* RFC 3267 4.4.1 Payload Header */
|
/* RFC 3267 4.4.1 Payload Header */
|
||||||
msgb_put_u8(msg, (cmr << 4));
|
msgb_put_u8(msg, (cmr << 4));
|
||||||
|
|
||||||
@@ -239,6 +291,8 @@ static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_le
|
|||||||
/* step2: shift everything left by one nibble */
|
/* step2: shift everything left by one nibble */
|
||||||
osmo_nibble_shift_left_unal(cur, l1_payload+2, amr_if2_len*2 -1);
|
osmo_nibble_shift_left_unal(cur, l1_payload+2, amr_if2_len*2 -1);
|
||||||
|
|
||||||
|
#endif /* USE_L1_RTP_MODE */
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +316,7 @@ int get_amr_mode_idx(const struct amr_multirate_conf *amr_mrc, uint8_t cmi)
|
|||||||
* \param[in] payload_len length of \a rtp_payload
|
* \param[in] payload_len length of \a rtp_payload
|
||||||
* \returns number of \a l1_payload bytes filled
|
* \returns number of \a l1_payload bytes filled
|
||||||
*/
|
*/
|
||||||
static int rtppayload_to_l1_amr(uint8_t *l1_payload, uint8_t *rtp_payload,
|
static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
|
||||||
uint8_t payload_len,
|
uint8_t payload_len,
|
||||||
struct gsm_lchan *lchan)
|
struct gsm_lchan *lchan)
|
||||||
{
|
{
|
||||||
@@ -275,12 +329,19 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, uint8_t *rtp_payload,
|
|||||||
uint8_t amr_if2_core_len = payload_len - 2;
|
uint8_t amr_if2_core_len = payload_len - 2;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
#ifdef USE_L1_RTP_MODE
|
||||||
|
memcpy(l1_payload+2, rtp_payload, payload_len);
|
||||||
|
#else
|
||||||
/* step1: shift everything right one nibble; make space for FT */
|
/* step1: shift everything right one nibble; make space for FT */
|
||||||
osmo_nibble_shift_right(l1_payload+2, rtp_payload+2, amr_if2_core_len*2);
|
osmo_nibble_shift_right(l1_payload+2, rtp_payload+2, amr_if2_core_len*2);
|
||||||
/* step2: reverse the bit-order within every byte of the IF2
|
/* step2: reverse the bit-order within every byte of the IF2
|
||||||
* core frame contained in the RTP payload */
|
* core frame contained in the RTP payload */
|
||||||
osmo_revbytebits_buf(l1_payload+2, amr_if2_core_len+1);
|
osmo_revbytebits_buf(l1_payload+2, amr_if2_core_len+1);
|
||||||
|
|
||||||
|
/* lower 4 bit of first FR2 byte contains FT */
|
||||||
|
l1_payload[2] |= ft;
|
||||||
|
#endif /* USE_L1_RTP_MODE */
|
||||||
|
|
||||||
/* CMI in downlink tells the L1 encoder which encoding function
|
/* CMI in downlink tells the L1 encoder which encoding function
|
||||||
* it will use, so we have to use the frame type */
|
* it will use, so we have to use the frame type */
|
||||||
switch (ft) {
|
switch (ft) {
|
||||||
@@ -341,9 +402,6 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, uint8_t *rtp_payload,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* lower 4 bit of first FR2 byte contains FT */
|
|
||||||
l1_payload[2] |= ft;
|
|
||||||
|
|
||||||
if (ft == AMR_FT_SID_AMR) {
|
if (ft == AMR_FT_SID_AMR) {
|
||||||
/* store the last SID frame in lchan context */
|
/* store the last SID frame in lchan context */
|
||||||
unsigned int copy_len;
|
unsigned int copy_len;
|
||||||
@@ -370,21 +428,38 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, uint8_t *rtp_payload,
|
|||||||
* yet, as things like the frame number, etc. are unknown at the time we
|
* yet, as things like the frame number, etc. are unknown at the time we
|
||||||
* pre-fill the primtive.
|
* pre-fill the primtive.
|
||||||
*/
|
*/
|
||||||
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, uint8_t *rtp_pl,
|
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
||||||
unsigned int rtp_pl_len)
|
unsigned int rtp_pl_len)
|
||||||
{
|
{
|
||||||
struct gsm_lchan *lchan = rs->priv;
|
struct gsm_lchan *lchan = rs->priv;
|
||||||
struct msgb *msg = l1p_msgb_alloc();
|
struct msgb *msg;
|
||||||
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
|
GsmL1_Prim_t *l1p;
|
||||||
GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
|
GsmL1_PhDataReq_t *data_req;
|
||||||
GsmL1_MsgUnitParam_t *msu_param = &data_req->msgUnitParam;
|
GsmL1_MsgUnitParam_t *msu_param;
|
||||||
uint8_t *payload_type = &msu_param->u8Buffer[0];
|
uint8_t *payload_type;
|
||||||
uint8_t *l1_payload = &msu_param->u8Buffer[1];
|
uint8_t *l1_payload;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan),
|
DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan),
|
||||||
osmo_hexdump(rtp_pl, rtp_pl_len));
|
osmo_hexdump(rtp_pl, rtp_pl_len));
|
||||||
|
|
||||||
|
/* skip processing of incoming RTP frames if we are in loopback mode */
|
||||||
|
if (lchan->loopback)
|
||||||
|
return;
|
||||||
|
|
||||||
|
msg = l1p_msgb_alloc();
|
||||||
|
if (!msg) {
|
||||||
|
LOGP(DRTP, LOGL_ERROR, "%s: Failed to allocate Rx payload.\n",
|
||||||
|
gsm_lchan_name(lchan));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
l1p = msgb_l1prim(msg);
|
||||||
|
data_req = &l1p->u.phDataReq;
|
||||||
|
msu_param = &data_req->msgUnitParam;
|
||||||
|
payload_type = &msu_param->u8Buffer[0];
|
||||||
|
l1_payload = &msu_param->u8Buffer[1];
|
||||||
|
|
||||||
switch (lchan->tch_mode) {
|
switch (lchan->tch_mode) {
|
||||||
case GSM48_CMODE_SPEECH_V1:
|
case GSM48_CMODE_SPEECH_V1:
|
||||||
if (lchan->type == GSM_LCHAN_TCH_F) {
|
if (lchan->type == GSM_LCHAN_TCH_F) {
|
||||||
@@ -397,10 +472,11 @@ void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, uint8_t *rtp_pl,
|
|||||||
rtp_pl, rtp_pl_len);
|
rtp_pl, rtp_pl_len);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#ifdef GsmL1_TchPlType_Efr
|
#if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
|
||||||
case GSM48_CMODE_SPEECH_EFR:
|
case GSM48_CMODE_SPEECH_EFR:
|
||||||
*payload_type = GsmL1_TchPlType_Efr;
|
*payload_type = GsmL1_TchPlType_Efr;
|
||||||
rc = FIXME;
|
rc = rtppayload_to_l1_efr(l1_payload, rtp_pl,
|
||||||
|
rtp_pl_len);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case GSM48_CMODE_SPEECH_AMR:
|
case GSM48_CMODE_SPEECH_AMR:
|
||||||
@@ -466,9 +542,45 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
|
|||||||
}
|
}
|
||||||
payload_len = data_ind->msgUnitParam.u8Size - 1;
|
payload_len = data_ind->msgUnitParam.u8Size - 1;
|
||||||
|
|
||||||
|
if (lchan->loopback) {
|
||||||
|
GsmL1_Prim_t *rl1p;
|
||||||
|
GsmL1_PhDataReq_t *data_req;
|
||||||
|
GsmL1_MsgUnitParam_t *msu_param;
|
||||||
|
|
||||||
|
struct msgb *tmp;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
/* generate a new msgb from the paylaod */
|
||||||
|
rmsg = l1p_msgb_alloc();
|
||||||
|
if (!rmsg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rl1p = msgb_l1prim(rmsg);
|
||||||
|
data_req = &rl1p->u.phDataReq;
|
||||||
|
msu_param = &data_req->msgUnitParam;
|
||||||
|
|
||||||
|
memcpy(msu_param->u8Buffer,
|
||||||
|
data_ind->msgUnitParam.u8Buffer,
|
||||||
|
data_ind->msgUnitParam.u8Size);
|
||||||
|
msu_param->u8Size = data_ind->msgUnitParam.u8Size;
|
||||||
|
|
||||||
|
/* make sure the queue doesn't get too long */
|
||||||
|
llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
|
||||||
|
count++;
|
||||||
|
while (count >= 1) {
|
||||||
|
tmp = msgb_dequeue(&lchan->dl_tch_queue);
|
||||||
|
msgb_free(tmp);
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
msgb_enqueue(&lchan->dl_tch_queue, rmsg);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
switch (payload_type) {
|
switch (payload_type) {
|
||||||
case GsmL1_TchPlType_Fr:
|
case GsmL1_TchPlType_Fr:
|
||||||
#ifdef GsmL1_TchPlType_Efr
|
#if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
|
||||||
case GsmL1_TchPlType_Efr:
|
case GsmL1_TchPlType_Efr:
|
||||||
#endif
|
#endif
|
||||||
if (lchan->type != GSM_LCHAN_TCH_F)
|
if (lchan->type != GSM_LCHAN_TCH_F)
|
||||||
@@ -500,8 +612,8 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
|
|||||||
case GsmL1_TchPlType_Hr:
|
case GsmL1_TchPlType_Hr:
|
||||||
rmsg = l1_to_rtppayload_hr(payload, payload_len);
|
rmsg = l1_to_rtppayload_hr(payload, payload_len);
|
||||||
break;
|
break;
|
||||||
#ifdef GsmL1_TchPlType_Efr
|
#if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
|
||||||
case GsmL1_TchPlType_Efr
|
case GsmL1_TchPlType_Efr:
|
||||||
rmsg = l1_to_rtppayload_efr(payload, payload_len);
|
rmsg = l1_to_rtppayload_efr(payload, payload_len);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
@@ -532,12 +644,22 @@ err_payload_match:
|
|||||||
|
|
||||||
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan)
|
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan)
|
||||||
{
|
{
|
||||||
struct msgb *msg = l1p_msgb_alloc();
|
struct msgb *msg;
|
||||||
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
|
GsmL1_Prim_t *l1p;
|
||||||
GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
|
GsmL1_PhDataReq_t *data_req;
|
||||||
GsmL1_MsgUnitParam_t *msu_param = &data_req->msgUnitParam;
|
GsmL1_MsgUnitParam_t *msu_param;
|
||||||
uint8_t *payload_type = &msu_param->u8Buffer[0];
|
uint8_t *payload_type;
|
||||||
uint8_t *l1_payload = &msu_param->u8Buffer[1];
|
uint8_t *l1_payload;
|
||||||
|
|
||||||
|
msg = l1p_msgb_alloc();
|
||||||
|
if (!msg)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
l1p = msgb_l1prim(msg);
|
||||||
|
data_req = &l1p->u.phDataReq;
|
||||||
|
msu_param = &data_req->msgUnitParam;
|
||||||
|
payload_type = &msu_param->u8Buffer[0];
|
||||||
|
l1_payload = &msu_param->u8Buffer[1];
|
||||||
|
|
||||||
switch (lchan->tch_mode) {
|
switch (lchan->tch_mode) {
|
||||||
case GSM48_CMODE_SPEECH_AMR:
|
case GSM48_CMODE_SPEECH_AMR:
|
||||||
|
|||||||
41
tests/Makefile.am
Normal file
41
tests/Makefile.am
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
SUBDIRS = paging
|
||||||
|
|
||||||
|
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||||
|
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
|
||||||
|
:;{ \
|
||||||
|
echo '# Signature of the current package.' && \
|
||||||
|
echo 'm4_define([AT_PACKAGE_NAME],' && \
|
||||||
|
echo ' [$(PACKAGE_NAME)])' && \
|
||||||
|
echo 'm4_define([AT_PACKAGE_TARNAME],' && \
|
||||||
|
echo ' [$(PACKAGE_TARNAME)])' && \
|
||||||
|
echo 'm4_define([AT_PACKAGE_VERSION],' && \
|
||||||
|
echo ' [$(PACKAGE_VERSION)])' && \
|
||||||
|
echo 'm4_define([AT_PACKAGE_STRING],' && \
|
||||||
|
echo ' [$(PACKAGE_STRING)])' && \
|
||||||
|
echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \
|
||||||
|
echo ' [$(PACKAGE_BUGREPORT)])'; \
|
||||||
|
echo 'm4_define([AT_PACKAGE_URL],' && \
|
||||||
|
echo ' [$(PACKAGE_URL)])'; \
|
||||||
|
} >'$(srcdir)/package.m4'
|
||||||
|
|
||||||
|
EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE)
|
||||||
|
TESTSUITE = $(srcdir)/testsuite
|
||||||
|
|
||||||
|
check-local: atconfig $(TESTSUITE)
|
||||||
|
$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
|
||||||
|
|
||||||
|
installcheck-local: atconfig $(TESTSUITE)
|
||||||
|
$(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \
|
||||||
|
$(TESTSUITEFLAGS)
|
||||||
|
|
||||||
|
clean-local:
|
||||||
|
test ! -f '$(TESTSUITE)' || \
|
||||||
|
$(SHELL) '$(TESTSUITE)' --clean
|
||||||
|
$(RM) -f atconfig
|
||||||
|
|
||||||
|
AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te
|
||||||
|
AUTOTEST = $(AUTOM4TE) --language=autotest
|
||||||
|
$(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4
|
||||||
|
$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
|
||||||
|
mv $@.tmp $@
|
||||||
|
|
||||||
8
tests/paging/Makefile.am
Normal file
8
tests/paging/Makefile.am
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||||
|
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
|
||||||
|
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) -lortp
|
||||||
|
noinst_PROGRAMS = paging_test
|
||||||
|
EXTRA_DIST = paging_test.ok
|
||||||
|
|
||||||
|
paging_test_SOURCES = paging_test.c
|
||||||
|
paging_test_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
|
||||||
164
tests/paging/paging_test.c
Normal file
164
tests/paging/paging_test.c
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
/* testing the paging code */
|
||||||
|
|
||||||
|
/* (C) 2011 by Holger Hans Peter Freyther
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <osmocom/core/talloc.h>
|
||||||
|
|
||||||
|
#include <osmo-bts/bts.h>
|
||||||
|
#include <osmo-bts/logging.h>
|
||||||
|
#include <osmo-bts/paging.h>
|
||||||
|
#include <osmo-bts/gsm_data.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static struct gsm_bts *bts;
|
||||||
|
static struct gsm_bts_role_bts *btsb;
|
||||||
|
int pcu_direct = 0;
|
||||||
|
|
||||||
|
static const uint8_t static_ilv[] = {
|
||||||
|
0x08, 0x59, 0x51, 0x30, 0x99, 0x00, 0x00, 0x00, 0x19
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ASSERT_TRUE(rc) \
|
||||||
|
if (!(rc)) { \
|
||||||
|
printf("Assert failed in %s:%d.\n", \
|
||||||
|
__FILE__, __LINE__); \
|
||||||
|
abort(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_paging_smoke(void)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
uint8_t out_buf[GSM_MACBLOCK_LEN];
|
||||||
|
struct gsm_time g_time;
|
||||||
|
printf("Testing that paging messages expire.\n");
|
||||||
|
|
||||||
|
/* add paging entry */
|
||||||
|
rc = paging_add_identity(btsb->paging_state, 0, static_ilv, 0);
|
||||||
|
ASSERT_TRUE(rc == 0);
|
||||||
|
ASSERT_TRUE(paging_queue_length(btsb->paging_state) == 1);
|
||||||
|
|
||||||
|
/* generate messages */
|
||||||
|
g_time.fn = 0;
|
||||||
|
g_time.t1 = 0;
|
||||||
|
g_time.t2 = 0;
|
||||||
|
g_time.t3 = 6;
|
||||||
|
rc = paging_gen_msg(btsb->paging_state, out_buf, &g_time);
|
||||||
|
ASSERT_TRUE(rc == 13);
|
||||||
|
|
||||||
|
ASSERT_TRUE(paging_group_queue_empty(btsb->paging_state, 0));
|
||||||
|
ASSERT_TRUE(paging_queue_length(btsb->paging_state) == 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: test all the cases of different amount tmsi/imsi and check
|
||||||
|
* if we fill the slots in a optimal way.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_paging_sleep(void)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
uint8_t out_buf[GSM_MACBLOCK_LEN];
|
||||||
|
struct gsm_time g_time;
|
||||||
|
printf("Testing that paging messages expire with sleep.\n");
|
||||||
|
|
||||||
|
/* add paging entry */
|
||||||
|
rc = paging_add_identity(btsb->paging_state, 0, static_ilv, 0);
|
||||||
|
ASSERT_TRUE(rc == 0);
|
||||||
|
ASSERT_TRUE(paging_queue_length(btsb->paging_state) == 1);
|
||||||
|
|
||||||
|
/* sleep */
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
/* generate messages */
|
||||||
|
g_time.fn = 0;
|
||||||
|
g_time.t1 = 0;
|
||||||
|
g_time.t2 = 0;
|
||||||
|
g_time.t3 = 6;
|
||||||
|
rc = paging_gen_msg(btsb->paging_state, out_buf, &g_time);
|
||||||
|
ASSERT_TRUE(rc == 13);
|
||||||
|
|
||||||
|
ASSERT_TRUE(paging_group_queue_empty(btsb->paging_state, 0));
|
||||||
|
ASSERT_TRUE(paging_queue_length(btsb->paging_state) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
void *tall_msgb_ctx;
|
||||||
|
|
||||||
|
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
|
||||||
|
tall_msgb_ctx = talloc_named_const(tall_bts_ctx, 1, "msgb");
|
||||||
|
msgb_set_talloc_ctx(tall_msgb_ctx);
|
||||||
|
|
||||||
|
bts_log_init(NULL);
|
||||||
|
|
||||||
|
bts = gsm_bts_alloc(tall_bts_ctx);
|
||||||
|
if (bts_init(bts) < 0) {
|
||||||
|
fprintf(stderr, "unable to to open bts\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
btsb = bts_role_bts(bts);
|
||||||
|
test_paging_smoke();
|
||||||
|
test_paging_sleep();
|
||||||
|
printf("Success\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* stub to link */
|
||||||
|
const uint8_t abis_mac[6] = { 0,1,2,3,4,5 };
|
||||||
|
const char *software_version = "0815";
|
||||||
|
|
||||||
|
int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
||||||
|
void *obj, uint8_t adm_state)
|
||||||
|
{ return 0; }
|
||||||
|
int bts_model_init(struct gsm_bts *bts)
|
||||||
|
{ return 0; }
|
||||||
|
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
|
||||||
|
struct tlv_parsed *new_attr, void *obj)
|
||||||
|
{ return 0; }
|
||||||
|
int bts_model_rsl_chan_rel(struct gsm_lchan *lchan)
|
||||||
|
{ return 0;}
|
||||||
|
|
||||||
|
int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan)
|
||||||
|
{ return 0; }
|
||||||
|
|
||||||
|
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx)
|
||||||
|
{ return 0; }
|
||||||
|
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
|
||||||
|
struct tlv_parsed *old_attr, struct tlv_parsed *new_attr,
|
||||||
|
void *obj)
|
||||||
|
{ return 0; }
|
||||||
|
int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
||||||
|
void *obj)
|
||||||
|
{ return 0; }
|
||||||
|
int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp)
|
||||||
|
{ return 0; }
|
||||||
|
int bts_model_rsl_mode_modify(struct gsm_lchan *lchan)
|
||||||
|
{ return 0; }
|
||||||
|
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
||||||
|
unsigned int rtp_pl_len) {}
|
||||||
|
|
||||||
|
int l1if_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
|
||||||
|
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
|
||||||
|
{ return 0; }
|
||||||
|
|
||||||
|
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx)
|
||||||
|
{ return 0; }
|
||||||
2
tests/paging/paging_test.ok
Normal file
2
tests/paging/paging_test.ok
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Testing that paging messages expire.
|
||||||
|
Success
|
||||||
8
tests/testsuite.at
Normal file
8
tests/testsuite.at
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
AT_INIT
|
||||||
|
AT_BANNER([Regression tests.])
|
||||||
|
|
||||||
|
AT_SETUP([paging])
|
||||||
|
AT_KEYWORDS([paging])
|
||||||
|
cat $abs_srcdir/paging/paging_test.ok > expout
|
||||||
|
AT_CHECK([$abs_top_builddir/tests/paging/paging_test], [], [expout], [ignore])
|
||||||
|
AT_CLEANUP
|
||||||
Reference in New Issue
Block a user