Add OC-2G BTS sources
Change-Id: I327384fe5ac944dc3996a3f00932d6f1a10d5a35
This commit is contained in:
parent
ee9e8e9eb2
commit
9c75c387c0
22
configure.ac
22
configure.ac
|
@ -279,6 +279,27 @@ if test "$enable_litecell15" = "yes"; then
|
|||
CPPFLAGS=$oldCPPFLAGS
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([whether to enable NuRAN Wireless OC-2G hardware support])
|
||||
AC_ARG_ENABLE(oc2g,
|
||||
AC_HELP_STRING([--enable-oc2g],
|
||||
[enable code for NuRAN Wireless OC-2G bts [default=no]]),
|
||||
[enable_oc2g="yes"],[enable_oc2g="no"])
|
||||
AC_ARG_WITH([oc2g], [AS_HELP_STRING([--with-oc2g=INCLUDE_DIR], [Location of the OC-2G API header files])],
|
||||
[oc2g_incdir="$withval"],[oc2g_incdir="$incdir"])
|
||||
AC_SUBST([OC2G_INCDIR], -I$oc2g_incdir)
|
||||
AC_MSG_RESULT([$enable_oc2g])
|
||||
AM_CONDITIONAL(ENABLE_OC2GBTS, test "x$enable_oc2g" = "xyes")
|
||||
if test "$enable_oc2g" = "yes"; then
|
||||
oldCPPFLAGS=$CPPFLAGS
|
||||
CPPFLAGS="$CPPFLAGS $OC2G_INCDIR -I$srcdir/include"
|
||||
AC_CHECK_HEADER([nrw/oc2g/oc2g.h],[],
|
||||
[AC_MSG_ERROR([nrw/oc2g/oc2g.h can not be found in $oc2g_incdir])],
|
||||
[#include <nrw/oc2g/oc2g.h>])
|
||||
PKG_CHECK_MODULES(LIBSYSTEMD, libsystemd)
|
||||
PKG_CHECK_MODULES(LIBGPS, libgps)
|
||||
CPPFLAGS=$oldCPPFLAGS
|
||||
fi
|
||||
|
||||
# https://www.freedesktop.org/software/systemd/man/daemon.html
|
||||
AC_ARG_WITH([systemdsystemunitdir],
|
||||
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
|
||||
|
@ -307,6 +328,7 @@ AC_OUTPUT(
|
|||
src/osmo-bts-omldummy/Makefile
|
||||
src/osmo-bts-sysmo/Makefile
|
||||
src/osmo-bts-litecell15/Makefile
|
||||
src/osmo-bts-oc2g/Makefile
|
||||
src/osmo-bts-trx/Makefile
|
||||
src/osmo-bts-octphy/Makefile
|
||||
include/Makefile
|
||||
|
|
|
@ -30,6 +30,10 @@ case "$bts_model" in
|
|||
./contrib/jenkins_lc15.sh
|
||||
;;
|
||||
|
||||
oc2g)
|
||||
./contrib/jenkins_oc2g.sh
|
||||
;;
|
||||
|
||||
trx)
|
||||
./contrib/jenkins_bts_trx.sh
|
||||
;;
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
#!/bin/sh
|
||||
# jenkins build helper script for osmo-bts-oc2g
|
||||
|
||||
# shellcheck source=contrib/jenkins_common.sh
|
||||
. $(dirname "$0")/jenkins_common.sh
|
||||
|
||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||
|
||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export LD_LIBRARY_PATH="$inst/lib"
|
||||
|
||||
osmo-build-dep.sh libosmo-abis
|
||||
|
||||
cd "$deps"
|
||||
osmo-layer1-headers.sh oc2g "$FIRMWARE_VERSION"
|
||||
|
||||
configure_flags="\
|
||||
--enable-sanitize \
|
||||
--with-oc2g=$deps/layer1-headers/inc/ \
|
||||
--enable-oc2g \
|
||||
"
|
||||
|
||||
build_bts "osmo-bts-oc2g" "$configure_flags"
|
||||
|
||||
osmo-clean-workspace.sh
|
|
@ -0,0 +1,29 @@
|
|||
[Unit]
|
||||
Description=osmo-bts manager for OC-2G
|
||||
After=oc2g-sysdev-remap.service
|
||||
Wants=oc2g-sysdev-remap.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
NotifyAccess=all
|
||||
WatchdogSec=21780s
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
|
||||
# Make sure directories and symbolic link exist
|
||||
ExecStartPre=/bin/sh -c 'test -d /mnt/storage/var/run/oc2gbts-mgr || mkdir -p /mnt/storage/var/run/oc2gbts-mgr ; test -d /var/run/oc2gbts-mgr || ln -sf /mnt/storage/var/run/oc2gbts-mgr/ /var/run'
|
||||
# Make sure BTS operation hour exist
|
||||
ExecStartPre=/bin/sh -c 'test -f /mnt/storage/var/run/oc2gbts-mgr/hours-running || echo 0 > /mnt/storage/var/run/oc2gbts-mgr/hours-running'
|
||||
# Shutdown all PA correctly
|
||||
ExecStartPre=/bin/sh -c 'echo disabled > /var/oc2g/pa-state/pa0/state;'
|
||||
#ExecStartPre=/bin/sh -c 'echo 0 > /var/oc2g/pa-supply/max_microvolts; echo 0 > /var/oc2g/pa-supply/min_microvolts'
|
||||
|
||||
ExecStart=/usr/bin/oc2gbts-mgr -s -c /etc/osmocom/oc2gbts-mgr.cfg
|
||||
|
||||
# Shutdown all PA correctly
|
||||
ExecStopPost=/bin/sh -c 'echo disabled > /var/oc2g/pa-state/pa0/state;'
|
||||
#ExecStopPost=/bin/sh -c 'echo 0 > /var/oc2g/pa-supply/max_microvolts; echo 0 > /var/oc2g/pa-supply/min_microvolts'
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=osmo-bts-mgr.service
|
|
@ -0,0 +1,21 @@
|
|||
[Unit]
|
||||
Description=osmo-bts for OC-2G
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStartPre=/bin/sh -c 'echo 1 > /sys/class/leds/usr0/brightness'
|
||||
ExecStartPre=/bin/sh -c 'echo 1 > /sys/class/leds/usr1/brightness'
|
||||
ExecStart=/usr/bin/osmo-bts-oc2g -s -c /etc/osmocom/osmo-bts.cfg -M
|
||||
ExecStopPost=/bin/sh -c 'echo 1 > /sys/class/leds/usr0/brightness'
|
||||
ExecStopPost=/bin/sh -c 'echo 0 > /sys/class/leds/usr1/brightness'
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
RestartPreventExitStatus=1
|
||||
|
||||
# The msg queues must be read fast enough
|
||||
CPUSchedulingPolicy=rr
|
||||
CPUSchedulingPriority=1
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=osmo-bts.service
|
|
@ -0,0 +1,33 @@
|
|||
!
|
||||
! oc2gbts-mgr (0.3.0.284-a7c2-dirty) configuration saved from vty
|
||||
!!
|
||||
!
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print category 0
|
||||
logging timestamp 0
|
||||
logging level temp info
|
||||
logging level fw info
|
||||
logging level find info
|
||||
logging level calib info
|
||||
logging level lglobal notice
|
||||
logging level llapd notice
|
||||
logging level linp notice
|
||||
logging level lmux notice
|
||||
logging level lmi notice
|
||||
logging level lmib notice
|
||||
logging level lsms notice
|
||||
logging level lctrl notice
|
||||
logging level lgtp notice
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
oc2gbts-mgr
|
||||
limits supply_volt
|
||||
threshold warning min 17500
|
||||
threshold critical min 19000
|
||||
limits supply_pwr
|
||||
threshold warning max 110
|
||||
threshold critical max 120
|
|
@ -0,0 +1,38 @@
|
|||
!
|
||||
! OsmoBTS (0.0.1.100-0455-dirty) configuration saved from vty
|
||||
!!
|
||||
!
|
||||
log stderr
|
||||
logging color 1
|
||||
logging timestamp 0
|
||||
logging level rsl info
|
||||
logging level oml info
|
||||
logging level rll notice
|
||||
logging level rr notice
|
||||
logging level meas notice
|
||||
logging level pag info
|
||||
logging level l1c info
|
||||
logging level l1p info
|
||||
logging level dsp debug
|
||||
logging level abis notice
|
||||
logging level rtp notice
|
||||
logging level lglobal notice
|
||||
logging level llapd notice
|
||||
logging level linp notice
|
||||
logging level lmux notice
|
||||
logging level lmi notice
|
||||
logging level lmib notice
|
||||
logging level lsms notice
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
phy 0
|
||||
instance 0
|
||||
trx-calibration-path /mnt/rom/factory/calib
|
||||
bts 0
|
||||
band 900
|
||||
ipa unit-id 1500 0
|
||||
oml remote-ip 10.42.0.1
|
||||
trx 0
|
||||
phy 0 instance 0
|
|
@ -289,6 +289,8 @@ struct gsm_lchan {
|
|||
/* indicates if DTXd was active during DL measurement
|
||||
period */
|
||||
bool dl_active;
|
||||
/* last UL SPEECH resume flag */
|
||||
bool is_speech_resume;
|
||||
} dtx;
|
||||
uint8_t last_cmr;
|
||||
uint32_t last_fn;
|
||||
|
@ -411,6 +413,9 @@ struct gsm_bts_trx {
|
|||
uint16_t arfcn;
|
||||
int nominal_power; /* in dBm */
|
||||
unsigned int max_power_red; /* in actual dB */
|
||||
uint8_t max_power_backoff_8psk; /* in actual dB OC-2G only */
|
||||
uint8_t c0_idle_power_red; /* in actual dB OC-2G only */
|
||||
|
||||
|
||||
struct trx_power_params power_params;
|
||||
int ms_power_control;
|
||||
|
@ -437,6 +442,7 @@ struct gsm_bts_trx {
|
|||
enum gsm_bts_type_variant {
|
||||
BTS_UNKNOWN,
|
||||
BTS_OSMO_LITECELL15,
|
||||
BTS_OSMO_OC2G,
|
||||
BTS_OSMO_OCTPHY,
|
||||
BTS_OSMO_SYSMO,
|
||||
BTS_OSMO_TRX,
|
||||
|
@ -746,7 +752,14 @@ struct gsm_bts {
|
|||
struct timeval tv_clock;
|
||||
struct osmo_timer_list fn_timer;
|
||||
} vbts;
|
||||
|
||||
#ifdef ENABLE_OC2GBTS
|
||||
/* specific to Open Cellular 2G BTS */
|
||||
struct {
|
||||
uint8_t led_ctrl_mode; /* 0: control by BTS, 1: not control by BTS */
|
||||
struct llist_head ceased_alarm_list; /* ceased alarm list*/
|
||||
unsigned int rtp_drift_thres_ms; /* RTP timestamp drift detection threshold */
|
||||
} oc2g;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -135,6 +135,20 @@ struct phy_instance {
|
|||
uint8_t tx_pwr_adj_mode; /* 0: no auto adjust power, 1: auto adjust power using RMS detector */
|
||||
uint8_t tx_pwr_red_8psk; /* 8-PSK maximum Tx power reduction level in dB */
|
||||
} lc15;
|
||||
struct {
|
||||
/* configuration */
|
||||
uint32_t dsp_trace_f;
|
||||
char *calib_path;
|
||||
int minTxPower;
|
||||
int maxTxPower;
|
||||
struct oc2gl1_hdl *hdl;
|
||||
uint8_t max_cell_size; /* 0:166 qbits*/
|
||||
uint8_t pedestal_mode; /* 0: unused TS is OFF, 1: unused TS is in minimum Tx power */
|
||||
uint8_t dsp_alive_period; /* DSP alive timer period */
|
||||
uint8_t tx_pwr_adj_mode; /* 0: no auto adjust power, 1: auto adjust power using RMS detector */
|
||||
uint8_t tx_pwr_red_8psk; /* 8-PSK maximum Tx power reduction level in dB */
|
||||
uint8_t tx_c0_idle_pwr_red; /* C0 idle slot Tx power reduction level in dB */
|
||||
} oc2g;
|
||||
} u;
|
||||
};
|
||||
|
||||
|
|
|
@ -15,3 +15,8 @@ endif
|
|||
if ENABLE_LC15BTS
|
||||
SUBDIRS += osmo-bts-litecell15
|
||||
endif
|
||||
|
||||
if ENABLE_OC2GBTS
|
||||
SUBDIRS += osmo-bts-oc2g
|
||||
endif
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ const char *btsatttr2str(enum bts_attribute v)
|
|||
const struct value_string osmo_bts_variant_names[_NUM_BTS_VARIANT + 1] = {
|
||||
{ BTS_UNKNOWN, "unknown" },
|
||||
{ BTS_OSMO_LITECELL15, "osmo-bts-lc15" },
|
||||
{ BTS_OSMO_OC2G, "osmo-bts-oc2g" },
|
||||
{ BTS_OSMO_OCTPHY, "osmo-bts-octphy" },
|
||||
{ BTS_OSMO_SYSMO, "osmo-bts-sysmo" },
|
||||
{ BTS_OSMO_TRX, "omso-bts-trx" },
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include $(OC2G_INCDIR)
|
||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCODEC_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBGPS_CFLAGS) $(ORTP_CFLAGS) $(LIBSYSTEMD_CFLAGS)
|
||||
COMMON_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCTRL_LIBS) $(ORTP_LIBS)
|
||||
|
||||
AM_CFLAGS += -DENABLE_OC2GBTS
|
||||
|
||||
EXTRA_DIST = misc/oc2gbts_mgr.h misc/oc2gbts_misc.h misc/oc2gbts_par.h misc/oc2gbts_led.h \
|
||||
misc/oc2gbts_temp.h misc/oc2gbts_power.h misc/oc2gbts_clock.h \
|
||||
misc/oc2gbts_bid.h misc/oc2gbts_nl.h \
|
||||
hw_misc.h l1_if.h l1_transp.h oc2gbts.h oml_router.h utils.h
|
||||
|
||||
bin_PROGRAMS = osmo-bts-oc2g oc2gbts-mgr oc2gbts-util
|
||||
|
||||
COMMON_SOURCES = main.c oc2gbts.c l1_if.c oml.c oc2gbts_vty.c tch.c hw_misc.c calib_file.c \
|
||||
utils.c misc/oc2gbts_par.c misc/oc2gbts_bid.c oml_router.c
|
||||
|
||||
osmo_bts_oc2g_SOURCES = $(COMMON_SOURCES) l1_transp_hw.c
|
||||
osmo_bts_oc2g_LDADD = $(top_builddir)/src/common/libbts.a $(COMMON_LDADD)
|
||||
|
||||
oc2gbts_mgr_SOURCES = \
|
||||
misc/oc2gbts_mgr.c misc/oc2gbts_misc.c \
|
||||
misc/oc2gbts_par.c misc/oc2gbts_nl.c \
|
||||
misc/oc2gbts_temp.c misc/oc2gbts_power.c \
|
||||
misc/oc2gbts_clock.c misc/oc2gbts_bid.c \
|
||||
misc/oc2gbts_mgr_vty.c \
|
||||
misc/oc2gbts_mgr_nl.c \
|
||||
misc/oc2gbts_mgr_temp.c \
|
||||
misc/oc2gbts_mgr_calib.c \
|
||||
misc/oc2gbts_led.c \
|
||||
misc/oc2gbts_bts.c \
|
||||
misc/oc2gbts_swd.c
|
||||
|
||||
oc2gbts_mgr_LDADD = $(top_builddir)/src/common/libbts.a $(LIBGPS_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBSYSTEMD_LIBS) $(COMMON_LDADD)
|
||||
|
||||
oc2gbts_util_SOURCES = misc/oc2gbts_util.c misc/oc2gbts_par.c
|
||||
oc2gbts_util_LDADD = $(LIBOSMOCORE_LIBS)
|
|
@ -0,0 +1,469 @@
|
|||
/* NuRAN Wireless OC-2G BTS L1 calibration file routines*/
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
* Copyright (C) 2016 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* (C) 2012 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
|
||||
#include <nrw/oc2g/oc2g.h>
|
||||
#include <nrw/oc2g/gsml1const.h>
|
||||
|
||||
#include "l1_if.h"
|
||||
#include "oc2gbts.h"
|
||||
#include "utils.h"
|
||||
#include "osmo-bts/oml.h"
|
||||
|
||||
/* Maximum calibration data chunk size */
|
||||
#define MAX_CALIB_TBL_SIZE 65536
|
||||
/* Calibration header version */
|
||||
#define CALIB_HDR_V1 0x01
|
||||
|
||||
struct calib_file_desc {
|
||||
const char *fname;
|
||||
int rx;
|
||||
int trx;
|
||||
int rxpath;
|
||||
};
|
||||
|
||||
static const struct calib_file_desc calib_files[] = {
|
||||
{
|
||||
.fname = "calib_rx0.conf",
|
||||
.rx = 1,
|
||||
.trx = 0,
|
||||
.rxpath = 0,
|
||||
}, {
|
||||
.fname = "calib_tx0.conf",
|
||||
.rx = 0,
|
||||
.trx = 0,
|
||||
},
|
||||
};
|
||||
|
||||
struct calTbl_t
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint8_t u8Version; /* Header version (1) */
|
||||
uint8_t u8Parity; /* Parity byte (xor) */
|
||||
uint8_t u8Type; /* Table type (0:TX Downlink, 1:RX-A Uplink, 2:RX-B Uplink) */
|
||||
uint8_t u8Band; /* GSM Band (0:GSM-850, 1:EGSM-900, 2:DCS-1800, 3:PCS-1900) */
|
||||
uint32_t u32Len; /* Table length in bytes including the header */
|
||||
struct
|
||||
{
|
||||
uint32_t u32DescOfst; /* Description section offset */
|
||||
uint32_t u32DateOfst; /* Date section offset */
|
||||
uint32_t u32StationOfst; /* Calibration test station section offset */
|
||||
uint32_t u32FpgaFwVerOfst; /* Calibration FPGA firmware version section offset */
|
||||
uint32_t u32DspFwVerOfst; /* Calibration DSP firmware section offset */
|
||||
uint32_t u32DataOfst; /* Calibration data section offset */
|
||||
} toc;
|
||||
} v1;
|
||||
} hdr;
|
||||
|
||||
uint8_t u8RawData[MAX_CALIB_TBL_SIZE - 32];
|
||||
};
|
||||
|
||||
|
||||
static int calib_file_send(struct oc2gl1_hdl *fl1h,
|
||||
const struct calib_file_desc *desc);
|
||||
static int calib_verify(struct oc2gl1_hdl *fl1h,
|
||||
const struct calib_file_desc *desc);
|
||||
|
||||
/* determine next calibration file index based on supported bands */
|
||||
static int get_next_calib_file_idx(struct oc2gl1_hdl *fl1h, int last_idx)
|
||||
{
|
||||
struct phy_link *plink = fl1h->phy_inst->phy_link;
|
||||
int i;
|
||||
|
||||
for (i = last_idx+1; i < ARRAY_SIZE(calib_files); i++) {
|
||||
if (calib_files[i].trx == plink->num)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int calib_file_open(struct oc2gl1_hdl *fl1h,
|
||||
const struct calib_file_desc *desc)
|
||||
{
|
||||
struct calib_send_state *st = &fl1h->st;
|
||||
char *calib_path = fl1h->phy_inst->u.oc2g.calib_path;
|
||||
char fname[PATH_MAX];
|
||||
|
||||
if (st->fp) {
|
||||
LOGP(DL1C, LOGL_NOTICE, "L1 calibration file was left opened !!\n");
|
||||
fclose(st->fp);
|
||||
st->fp = NULL;
|
||||
}
|
||||
|
||||
fname[0] = '\0';
|
||||
snprintf(fname, sizeof(fname)-1, "%s/%s", calib_path, desc->fname);
|
||||
fname[sizeof(fname)-1] = '\0';
|
||||
|
||||
st->fp = fopen(fname, "rb");
|
||||
if (!st->fp) {
|
||||
LOGP(DL1C, LOGL_NOTICE, "Failed to open '%s' for calibration data.\n", fname);
|
||||
|
||||
/* TODO (oramadan): Fix OML alarms
|
||||
if( fl1h->phy_inst->trx ){
|
||||
fl1h->phy_inst->trx->mo.obj_inst.trx_nr = fl1h->phy_inst->trx->nr;
|
||||
|
||||
alarm_sig_data.mo = &fl1h->phy_inst->trx->mo;
|
||||
alarm_sig_data.add_text = (char*)&fname[0];
|
||||
osmo_signal_dispatch(SS_NM, S_NM_OML_BTS_FAIL_OPEN_CALIB_ALARM, &alarm_sig_data);
|
||||
}
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int calib_file_close(struct oc2gl1_hdl *fl1h)
|
||||
{
|
||||
struct calib_send_state *st = &fl1h->st;
|
||||
|
||||
if (st->fp) {
|
||||
fclose(st->fp);
|
||||
st->fp = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* iteratively download the calibration data into the L1 */
|
||||
|
||||
static int calib_send_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
|
||||
void *data);
|
||||
|
||||
/* send a chunk of calibration tabledata for a single specified file */
|
||||
static int calib_file_send_next_chunk(struct oc2gl1_hdl *fl1h)
|
||||
{
|
||||
struct calib_send_state *st = &fl1h->st;
|
||||
Oc2g_Prim_t *prim;
|
||||
struct msgb *msg;
|
||||
size_t n;
|
||||
|
||||
msg = sysp_msgb_alloc();
|
||||
prim = msgb_sysprim(msg);
|
||||
|
||||
prim->id = Oc2g_PrimId_SetCalibTblReq;
|
||||
prim->u.setCalibTblReq.offset = (uint32_t)ftell(st->fp);
|
||||
n = fread(prim->u.setCalibTblReq.u8Data, 1,
|
||||
sizeof(prim->u.setCalibTblReq.u8Data), st->fp);
|
||||
prim->u.setCalibTblReq.length = n;
|
||||
|
||||
|
||||
if (n == 0) {
|
||||
/* The table data has been completely sent and acknowledged */
|
||||
LOGP(DL1C, LOGL_NOTICE, "L1 calibration table %s loaded\n",
|
||||
calib_files[st->last_file_idx].fname);
|
||||
|
||||
calib_file_close(fl1h);
|
||||
|
||||
msgb_free(msg);
|
||||
|
||||
/* Send the next one if any */
|
||||
st->last_file_idx = get_next_calib_file_idx(fl1h, st->last_file_idx);
|
||||
if (st->last_file_idx >= 0) {
|
||||
return calib_file_send(fl1h,
|
||||
&calib_files[st->last_file_idx]);
|
||||
}
|
||||
|
||||
LOGP(DL1C, LOGL_INFO, "L1 calibration table loading complete!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return l1if_req_compl(fl1h, msg, calib_send_compl_cb, NULL);
|
||||
}
|
||||
|
||||
/* send the calibration table for a single specified file */
|
||||
static int calib_file_send(struct oc2gl1_hdl *fl1h,
|
||||
const struct calib_file_desc *desc)
|
||||
{
|
||||
struct calib_send_state *st = &fl1h->st;
|
||||
int rc;
|
||||
|
||||
rc = calib_file_open(fl1h, desc);
|
||||
if (rc < 0) {
|
||||
/* still, we'd like to continue trying to load
|
||||
* calibration for all other bands */
|
||||
st->last_file_idx = get_next_calib_file_idx(fl1h, st->last_file_idx);
|
||||
if (st->last_file_idx >= 0)
|
||||
return calib_file_send(fl1h,
|
||||
&calib_files[st->last_file_idx]);
|
||||
|
||||
LOGP(DL1C, LOGL_INFO, "L1 calibration table loading complete!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = calib_verify(fl1h, desc);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_NOTICE,"Verify L1 calibration table %s -> failed (%d)\n", desc->fname, rc);
|
||||
|
||||
/* TODO (oramadan): Fix OML alarms
|
||||
if (fl1h->phy_inst->trx) {
|
||||
fl1h->phy_inst->trx->mo.obj_inst.trx_nr = fl1h->phy_inst->trx->nr;
|
||||
|
||||
alarm_sig_data.mo = &fl1h->phy_inst->trx->mo;
|
||||
alarm_sig_data.add_text = (char*)&desc->fname[0];
|
||||
memcpy(alarm_sig_data.spare, &rc, sizeof(int));
|
||||
osmo_signal_dispatch(SS_NM, S_NM_OML_BTS_FAIL_VERIFY_CALIB_ALARM, &alarm_sig_data);
|
||||
}
|
||||
*/
|
||||
|
||||
st->last_file_idx = get_next_calib_file_idx(fl1h, st->last_file_idx);
|
||||
|
||||
if (st->last_file_idx >= 0)
|
||||
return calib_file_send(fl1h,
|
||||
&calib_files[st->last_file_idx]);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
LOGP(DL1C, LOGL_INFO, "Verify L1 calibration table %s -> done\n", desc->fname);
|
||||
|
||||
return calib_file_send_next_chunk(fl1h);
|
||||
}
|
||||
|
||||
/* completion callback after every SetCalibTbl is confirmed */
|
||||
static int calib_send_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
|
||||
void *data)
|
||||
{
|
||||
struct oc2gl1_hdl *fl1h = trx_oc2gl1_hdl(trx);
|
||||
struct calib_send_state *st = &fl1h->st;
|
||||
Oc2g_Prim_t *prim = msgb_sysprim(l1_msg);
|
||||
|
||||
if (prim->u.setCalibTblCnf.status != GsmL1_Status_Success) {
|
||||
LOGP(DL1C, LOGL_ERROR, "L1 rejected calibration table\n");
|
||||
|
||||
msgb_free(l1_msg);
|
||||
|
||||
calib_file_close(fl1h);
|
||||
|
||||
/* Skip this one and try the next one */
|
||||
st->last_file_idx = get_next_calib_file_idx(fl1h, st->last_file_idx);
|
||||
if (st->last_file_idx >= 0) {
|
||||
return calib_file_send(fl1h,
|
||||
&calib_files[st->last_file_idx]);
|
||||
}
|
||||
|
||||
LOGP(DL1C, LOGL_INFO, "L1 calibration table loading complete!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
msgb_free(l1_msg);
|
||||
|
||||
/* Keep sending the calibration file data */
|
||||
return calib_file_send_next_chunk(fl1h);
|
||||
}
|
||||
|
||||
int calib_load(struct oc2gl1_hdl *fl1h)
|
||||
{
|
||||
int rc;
|
||||
struct calib_send_state *st = &fl1h->st;
|
||||
char *calib_path = fl1h->phy_inst->u.oc2g.calib_path;
|
||||
|
||||
if (!calib_path) {
|
||||
LOGP(DL1C, LOGL_NOTICE, "Calibration file path not specified\n");
|
||||
|
||||
/* TODO (oramadan): Fix OML alarms
|
||||
if( fl1h->phy_inst->trx ){
|
||||
fl1h->phy_inst->trx->mo.obj_inst.trx_nr = fl1h->phy_inst->trx->nr;
|
||||
|
||||
alarm_sig_data.mo = &fl1h->phy_inst->trx->mo;
|
||||
osmo_signal_dispatch(SS_NM, S_NM_OML_BTS_NO_CALIB_PATH_ALARM, &alarm_sig_data);
|
||||
}
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = get_next_calib_file_idx(fl1h, -1);
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
st->last_file_idx = rc;
|
||||
|
||||
return calib_file_send(fl1h, &calib_files[st->last_file_idx]);
|
||||
}
|
||||
|
||||
|
||||
static int calib_verify(struct oc2gl1_hdl *fl1h, const struct calib_file_desc *desc)
|
||||
{
|
||||
int rc, sz;
|
||||
struct calib_send_state *st = &fl1h->st;
|
||||
struct phy_link *plink = fl1h->phy_inst->phy_link;
|
||||
char *rbuf;
|
||||
struct calTbl_t *calTbl;
|
||||
char calChkSum ;
|
||||
|
||||
|
||||
/* calculate file size in bytes */
|
||||
fseek(st->fp, 0L, SEEK_END);
|
||||
sz = ftell(st->fp);
|
||||
|
||||
/* rewind read poiner */
|
||||
fseek(st->fp, 0L, SEEK_SET);
|
||||
|
||||
/* read file */
|
||||
rbuf = (char *) malloc( sizeof(char) * sz );
|
||||
|
||||
rc = fread(rbuf, 1, sizeof(char) * sz, st->fp);
|
||||
if (rc != sz) {
|
||||
LOGP(DL1C, LOGL_ERROR, "%s reading error\n", desc->fname);
|
||||
free(rbuf);
|
||||
|
||||
/* close file */
|
||||
rc = calib_file_close(fl1h);
|
||||
if (rc < 0 ) {
|
||||
LOGP(DL1C, LOGL_ERROR, "%s can not close\n", desc->fname);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
calTbl = (struct calTbl_t*) rbuf;
|
||||
/* calculate file checksum */
|
||||
calChkSum = 0;
|
||||
while (sz--) {
|
||||
calChkSum ^= rbuf[sz];
|
||||
}
|
||||
|
||||
/* validate Tx calibration parity */
|
||||
if (calChkSum) {
|
||||
LOGP(DL1C, LOGL_ERROR, "%s has invalid checksum %x.\n", desc->fname, calChkSum);
|
||||
return -4;
|
||||
}
|
||||
|
||||
/* validate Tx calibration header */
|
||||
if (calTbl->hdr.v1.u8Version != CALIB_HDR_V1) {
|
||||
LOGP(DL1C, LOGL_ERROR, "%s has invalid header version %u.\n", desc->fname, calTbl->hdr.v1.u8Version);
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* validate calibration description */
|
||||
if (calTbl->hdr.v1.toc.u32DescOfst == 0xFFFFFFFF) {
|
||||
LOGP(DL1C, LOGL_ERROR, "%s has invalid calibration description offset.\n", desc->fname);
|
||||
return -6;
|
||||
}
|
||||
|
||||
/* validate calibration date */
|
||||
if (calTbl->hdr.v1.toc.u32DateOfst == 0xFFFFFFFF) {
|
||||
LOGP(DL1C, LOGL_ERROR, "%s has invalid calibration date offset.\n", desc->fname);
|
||||
return -7;
|
||||
}
|
||||
|
||||
LOGP(DL1C, LOGL_INFO, "L1 calibration table %s created on %s\n",
|
||||
desc->fname,
|
||||
calTbl->u8RawData + calTbl->hdr.v1.toc.u32DateOfst);
|
||||
|
||||
/* validate calibration station */
|
||||
if (calTbl->hdr.v1.toc.u32StationOfst == 0xFFFFFFFF) {
|
||||
LOGP(DL1C, LOGL_ERROR, "%s has invalid calibration station ID offset.\n", desc->fname);
|
||||
return -8;
|
||||
}
|
||||
|
||||
/* validate FPGA FW version */
|
||||
if (calTbl->hdr.v1.toc.u32FpgaFwVerOfst == 0xFFF) {
|
||||
LOGP(DL1C, LOGL_ERROR, "%s has invalid FPGA FW version offset.\n", desc->fname);
|
||||
return -9;
|
||||
}
|
||||
|
||||
/* validate DSP FW version */
|
||||
if (calTbl->hdr.v1.toc.u32DspFwVerOfst == 0xFFFFFFFF) {
|
||||
LOGP(DL1C, LOGL_ERROR, "%s has invalid DSP FW version offset.\n", desc->fname);
|
||||
return -10;
|
||||
}
|
||||
|
||||
/* validate Tx calibration data offset */
|
||||
if (calTbl->hdr.v1.toc.u32DataOfst == 0xFFFFFFFF) {
|
||||
LOGP(DL1C, LOGL_ERROR, "%s has invalid calibration data offset.\n", desc->fname);
|
||||
return -11;
|
||||
}
|
||||
|
||||
if (!desc->rx) {
|
||||
|
||||
/* parse min/max Tx power */
|
||||
fl1h->phy_inst->u.oc2g.minTxPower = calTbl->u8RawData[calTbl->hdr.v1.toc.u32DataOfst + (5 << 2)];
|
||||
fl1h->phy_inst->u.oc2g.maxTxPower = calTbl->u8RawData[calTbl->hdr.v1.toc.u32DataOfst + (6 << 2)];
|
||||
|
||||
/* override nominal Tx power of given TRX if needed */
|
||||
if (fl1h->phy_inst->trx->nominal_power > fl1h->phy_inst->u.oc2g.maxTxPower) {
|
||||
LOGP(DL1C, LOGL_INFO, "Set TRX %u nominal Tx power to %d dBm (%d)\n",
|
||||
plink->num,
|
||||
fl1h->phy_inst->u.oc2g.maxTxPower,
|
||||
fl1h->phy_inst->trx->nominal_power);
|
||||
|
||||
fl1h->phy_inst->trx->nominal_power = fl1h->phy_inst->u.oc2g.maxTxPower;
|
||||
}
|
||||
|
||||
if (fl1h->phy_inst->trx->nominal_power < fl1h->phy_inst->u.oc2g.minTxPower) {
|
||||
LOGP(DL1C, LOGL_INFO, "Set TRX %u nominal Tx power to %d dBm (%d)\n",
|
||||
plink->num,
|
||||
fl1h->phy_inst->u.oc2g.minTxPower,
|
||||
fl1h->phy_inst->trx->nominal_power);
|
||||
|
||||
fl1h->phy_inst->trx->nominal_power = fl1h->phy_inst->u.lc15.minTxPower;
|
||||
}
|
||||
|
||||
if (fl1h->phy_inst->trx->power_params.trx_p_max_out_mdBm > to_mdB(fl1h->phy_inst->u.oc2g.maxTxPower) ) {
|
||||
LOGP(DL1C, LOGL_INFO, "Set TRX %u Tx power parameter to %d dBm (%d)\n",
|
||||
plink->num,
|
||||
to_mdB(fl1h->phy_inst->u.oc2g.maxTxPower),
|
||||
fl1h->phy_inst->trx->power_params.trx_p_max_out_mdBm);
|
||||
|
||||
fl1h->phy_inst->trx->power_params.trx_p_max_out_mdBm = to_mdB(fl1h->phy_inst->u.oc2g.maxTxPower);
|
||||
}
|
||||
|
||||
if (fl1h->phy_inst->trx->power_params.trx_p_max_out_mdBm < to_mdB(fl1h->phy_inst->u.oc2g.minTxPower) ) {
|
||||
LOGP(DL1C, LOGL_INFO, "Set TRX %u Tx power parameter to %d dBm (%d)\n",
|
||||
plink->num,
|
||||
to_mdB(fl1h->phy_inst->u.oc2g.minTxPower),
|
||||
fl1h->phy_inst->trx->power_params.trx_p_max_out_mdBm);
|
||||
|
||||
fl1h->phy_inst->trx->power_params.trx_p_max_out_mdBm = to_mdB(fl1h->phy_inst->u.oc2g.minTxPower);
|
||||
}
|
||||
|
||||
LOGP(DL1C, LOGL_DEBUG, "%s: minTxPower=%d, maxTxPower=%d\n",
|
||||
desc->fname,
|
||||
fl1h->phy_inst->u.oc2g.minTxPower,
|
||||
fl1h->phy_inst->u.oc2g.maxTxPower );
|
||||
}
|
||||
|
||||
/* rewind read pointer for subsequence tasks */
|
||||
fseek(st->fp, 0L, SEEK_SET);
|
||||
free(rbuf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/* Misc HW routines for NuRAN Wireless OC-2G BTS */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* (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"
|
||||
|
||||
int oc2gbts_led_set(enum oc2gbts_led_color c)
|
||||
{
|
||||
int fd, rc;
|
||||
uint8_t cmd[2];
|
||||
|
||||
switch (c) {
|
||||
case LED_OFF:
|
||||
cmd[0] = 0;
|
||||
cmd[1] = 0;
|
||||
break;
|
||||
case LED_RED:
|
||||
cmd[0] = 1;
|
||||
cmd[1] = 0;
|
||||
break;
|
||||
case LED_GREEN:
|
||||
cmd[0] = 0;
|
||||
cmd[1] = 1;
|
||||
break;
|
||||
case LED_ORANGE:
|
||||
cmd[0] = 1;
|
||||
cmd[1] = 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fd = open("/var/oc2g/leds/led0/brightness", O_WRONLY);
|
||||
if (fd < 0)
|
||||
return -ENODEV;
|
||||
|
||||
rc = write(fd, cmd[0] ? "1" : "0", 2);
|
||||
if (rc != 2) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
fd = open("/var/oc2g/leds/led1/brightness", O_WRONLY);
|
||||
if (fd < 0)
|
||||
return -ENODEV;
|
||||
|
||||
rc = write(fd, cmd[1] ? "1" : "0", 2);
|
||||
if (rc != 2) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef _HW_MISC_H
|
||||
#define _HW_MISC_H
|
||||
|
||||
enum oc2gbts_led_color {
|
||||
LED_OFF,
|
||||
LED_RED,
|
||||
LED_GREEN,
|
||||
LED_ORANGE,
|
||||
};
|
||||
|
||||
int oc2gbts_led_set(enum oc2gbts_led_color c);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,145 @@
|
|||
#ifndef _L1_IF_H
|
||||
#define _L1_IF_H
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/gsmtap_util.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
|
||||
#include <osmo-bts/phy_link.h>
|
||||
|
||||
#include <nrw/oc2g/gsml1prim.h>
|
||||
#include <nrw/oc2g/gsml1types.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
enum {
|
||||
MQ_SYS_READ,
|
||||
MQ_L1_READ,
|
||||
MQ_TCH_READ,
|
||||
MQ_PDTCH_READ,
|
||||
_NUM_MQ_READ
|
||||
};
|
||||
|
||||
enum {
|
||||
MQ_SYS_WRITE,
|
||||
MQ_L1_WRITE,
|
||||
MQ_TCH_WRITE,
|
||||
MQ_PDTCH_WRITE,
|
||||
_NUM_MQ_WRITE
|
||||
};
|
||||
|
||||
struct calib_send_state {
|
||||
FILE *fp;
|
||||
const char *path;
|
||||
int last_file_idx;
|
||||
};
|
||||
|
||||
struct oc2gl1_hdl {
|
||||
struct gsm_time gsm_time;
|
||||
HANDLE hLayer1; /* handle to the L1 instance in the DSP */
|
||||
uint32_t dsp_trace_f; /* currently operational DSP trace flags */
|
||||
struct llist_head wlc_list;
|
||||
struct llist_head alarm_list; /* list of sent alarms */
|
||||
|
||||
struct phy_instance *phy_inst;
|
||||
|
||||
struct osmo_timer_list alive_timer;
|
||||
unsigned int alive_prim_cnt;
|
||||
|
||||
struct osmo_fd read_ofd[_NUM_MQ_READ]; /* osmo file descriptors */
|
||||
struct osmo_wqueue write_q[_NUM_MQ_WRITE];
|
||||
|
||||
struct {
|
||||
/* from DSP/FPGA after L1 Init */
|
||||
uint8_t dsp_version[3];
|
||||
uint8_t fpga_version[3];
|
||||
uint32_t band_support;
|
||||
uint8_t ver_major;
|
||||
uint8_t ver_minor;
|
||||
uint32_t options;
|
||||
} hw_info;
|
||||
|
||||
struct calib_send_state st;
|
||||
|
||||
uint8_t last_rf_mute[8];
|
||||
|
||||
struct {
|
||||
struct osmo_timer_list dsp_alive_timer;
|
||||
unsigned int dsp_alive_cnt;
|
||||
uint8_t dsp_alive_period;
|
||||
} hw_alive;
|
||||
};
|
||||
|
||||
#define msgb_l1prim(msg) ((GsmL1_Prim_t *)(msg)->l1h)
|
||||
#define msgb_sysprim(msg) ((Oc2g_Prim_t *)(msg)->l1h)
|
||||
|
||||
typedef int l1if_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg, void *data);
|
||||
|
||||
/* send a request primitive to the L1 and schedule completion call-back */
|
||||
int l1if_req_compl(struct oc2gl1_hdl *fl1h, struct msgb *msg,
|
||||
l1if_compl_cb *cb, void *cb_data);
|
||||
int l1if_gsm_req_compl(struct oc2gl1_hdl *fl1h, struct msgb *msg,
|
||||
l1if_compl_cb *cb, void *cb_data);
|
||||
|
||||
struct oc2gl1_hdl *l1if_open(struct phy_instance *pinst);
|
||||
int l1if_close(struct oc2gl1_hdl *hdl);
|
||||
int l1if_reset(struct oc2gl1_hdl *hdl);
|
||||
int l1if_activate_rf(struct oc2gl1_hdl *hdl, int on);
|
||||
int l1if_set_trace_flags(struct oc2gl1_hdl *hdl, uint32_t flags);
|
||||
int l1if_set_txpower(struct oc2gl1_hdl *fl1h, float tx_power);
|
||||
int l1if_set_txpower_backoff_8psk(struct oc2gl1_hdl *fl1h, uint8_t backoff);
|
||||
int l1if_set_txpower_c0_idle_pwr_red(struct oc2gl1_hdl *fl1h, uint8_t red);
|
||||
int l1if_set_max_cell_size(struct oc2gl1_hdl *fl1h, uint8_t cell_size);
|
||||
int l1if_mute_rf(struct oc2gl1_hdl *hdl, uint8_t mute[8], l1if_compl_cb *cb);
|
||||
|
||||
struct msgb *l1p_msgb_alloc(void);
|
||||
struct msgb *sysp_msgb_alloc(void);
|
||||
|
||||
uint32_t l1if_lchan_to_hLayer(struct gsm_lchan *lchan);
|
||||
struct gsm_lchan *l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer);
|
||||
|
||||
/* tch.c */
|
||||
int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
|
||||
const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn,
|
||||
bool use_cache, bool marker);
|
||||
int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg);
|
||||
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
|
||||
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn);
|
||||
|
||||
/* ciphering */
|
||||
int l1if_set_ciphering(struct oc2gl1_hdl *fl1h,
|
||||
struct gsm_lchan *lchan,
|
||||
int dir_downlink);
|
||||
|
||||
/* channel control */
|
||||
int l1if_rsl_chan_act(struct gsm_lchan *lchan);
|
||||
int l1if_rsl_chan_rel(struct gsm_lchan *lchan);
|
||||
int l1if_rsl_chan_mod(struct gsm_lchan *lchan);
|
||||
int l1if_rsl_deact_sacch(struct gsm_lchan *lchan);
|
||||
int l1if_rsl_mode_modify(struct gsm_lchan *lchan);
|
||||
|
||||
/* calibration loading */
|
||||
int calib_load(struct oc2gl1_hdl *fl1h);
|
||||
|
||||
/* public helpers for test */
|
||||
int bts_check_for_ciph_cmd(struct oc2gl1_hdl *fl1h,
|
||||
struct msgb *msg, struct gsm_lchan *lchan);
|
||||
int l1if_ms_pwr_ctrl(struct gsm_lchan *lchan, const int uplink_target,
|
||||
const uint8_t ms_power, const float rxLevel);
|
||||
|
||||
static inline struct oc2gl1_hdl *trx_oc2gl1_hdl(struct gsm_bts_trx *trx)
|
||||
{
|
||||
struct phy_instance *pinst = trx_phy_instance(trx);
|
||||
OSMO_ASSERT(pinst);
|
||||
return pinst->u.oc2g.hdl;
|
||||
}
|
||||
|
||||
static inline struct gsm_bts_trx *oc2gl1_hdl_trx(struct oc2gl1_hdl *fl1h)
|
||||
{
|
||||
OSMO_ASSERT(fl1h->phy_inst);
|
||||
return fl1h->phy_inst->trx;
|
||||
}
|
||||
|
||||
#endif /* _L1_IF_H */
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef _L1_TRANSP_H
|
||||
#define _L1_TRANSP_H
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
/* functions a transport calls on arrival of primitive from BTS */
|
||||
int l1if_handle_l1prim(int wq, struct oc2gl1_hdl *fl1h, struct msgb *msg);
|
||||
int l1if_handle_sysprim(struct oc2gl1_hdl *fl1h, struct msgb *msg);
|
||||
|
||||
/* functions exported by a transport */
|
||||
int l1if_transport_open(int q, struct oc2gl1_hdl *fl1h);
|
||||
int l1if_transport_close(int q, struct oc2gl1_hdl *fl1h);
|
||||
|
||||
#endif /* _L1_TRANSP_H */
|
|
@ -0,0 +1,326 @@
|
|||
/* Interface handler for Nuran Wireless OC-2G L1 (real hardware) */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* (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 <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
|
||||
#include <nrw/oc2g/oc2g.h>
|
||||
#include <nrw/oc2g/gsml1prim.h>
|
||||
#include <nrw/oc2g/gsml1const.h>
|
||||
#include <nrw/oc2g/gsml1types.h>
|
||||
|
||||
#include "oc2gbts.h"
|
||||
#include "l1_if.h"
|
||||
#include "l1_transp.h"
|
||||
|
||||
|
||||
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/oc2g_dsp2arm_trx"
|
||||
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/oc2g_arm2dsp_trx"
|
||||
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_sig_dsp2arm_trx"
|
||||
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_sig_arm2dsp_trx"
|
||||
|
||||
#define DEV_TCH_DSP2ARM_NAME "/dev/msgq/gsml1_tch_dsp2arm_trx"
|
||||
#define DEV_TCH_ARM2DSP_NAME "/dev/msgq/gsml1_tch_arm2dsp_trx"
|
||||
#define DEV_PDTCH_DSP2ARM_NAME "/dev/msgq/gsml1_pdtch_dsp2arm_trx"
|
||||
#define DEV_PDTCH_ARM2DSP_NAME "/dev/msgq/gsml1_pdtch_arm2dsp_trx"
|
||||
|
||||
static const char *rd_devnames[] = {
|
||||
[MQ_SYS_READ] = DEV_SYS_DSP2ARM_NAME,
|
||||
[MQ_L1_READ] = DEV_L1_DSP2ARM_NAME,
|
||||
[MQ_TCH_READ] = DEV_TCH_DSP2ARM_NAME,
|
||||
[MQ_PDTCH_READ] = DEV_PDTCH_DSP2ARM_NAME,
|
||||
};
|
||||
|
||||
static const char *wr_devnames[] = {
|
||||
[MQ_SYS_WRITE] = DEV_SYS_ARM2DSP_NAME,
|
||||
[MQ_L1_WRITE] = DEV_L1_ARM2DSP_NAME,
|
||||
[MQ_TCH_WRITE] = DEV_TCH_ARM2DSP_NAME,
|
||||
[MQ_PDTCH_WRITE]= DEV_PDTCH_ARM2DSP_NAME,
|
||||
};
|
||||
|
||||
/*
|
||||
* Make sure that all structs we read fit into the OC2GBTS_PRIM_SIZE
|
||||
*/
|
||||
osmo_static_assert(sizeof(GsmL1_Prim_t) + 128 <= OC2GBTS_PRIM_SIZE, l1_prim)
|
||||
osmo_static_assert(sizeof(Oc2g_Prim_t) + 128 <= OC2GBTS_PRIM_SIZE, super_prim)
|
||||
|
||||
static int wqueue_vector_cb(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
struct osmo_wqueue *queue;
|
||||
|
||||
queue = container_of(fd, struct osmo_wqueue, bfd);
|
||||
|
||||
if (what & BSC_FD_READ)
|
||||
queue->read_cb(fd);
|
||||
|
||||
if (what & BSC_FD_EXCEPT)
|
||||
queue->except_cb(fd);
|
||||
|
||||
if (what & BSC_FD_WRITE) {
|
||||
struct iovec iov[5];
|
||||
struct msgb *msg, *tmp;
|
||||
int written, count = 0;
|
||||
|
||||
fd->when &= ~BSC_FD_WRITE;
|
||||
|
||||
llist_for_each_entry(msg, &queue->msg_queue, list) {
|
||||
/* more writes than we have */
|
||||
if (count >= ARRAY_SIZE(iov))
|
||||
break;
|
||||
|
||||
iov[count].iov_base = msg->l1h;
|
||||
iov[count].iov_len = msgb_l1len(msg);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
/* TODO: check if all lengths are the same. */
|
||||
|
||||
|
||||
/* Nothing scheduled? This should not happen. */
|
||||
if (count == 0) {
|
||||
if (!llist_empty(&queue->msg_queue))
|
||||
fd->when |= BSC_FD_WRITE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
written = writev(fd->fd, iov, count);
|
||||
if (written < 0) {
|
||||
/* nothing written?! */
|
||||
if (!llist_empty(&queue->msg_queue))
|
||||
fd->when |= BSC_FD_WRITE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* now delete the written entries */
|
||||
written = written / iov[0].iov_len;
|
||||
count = 0;
|
||||
llist_for_each_entry_safe(msg, tmp, &queue->msg_queue, list) {
|
||||
queue->current_length -= 1;
|
||||
|
||||
llist_del(&msg->list);
|
||||
msgb_free(msg);
|
||||
|
||||
count += 1;
|
||||
if (count >= written)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!llist_empty(&queue->msg_queue))
|
||||
fd->when |= BSC_FD_WRITE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prim_size_for_queue(int queue)
|
||||
{
|
||||
switch (queue) {
|
||||
case MQ_SYS_WRITE:
|
||||
return sizeof(Oc2g_Prim_t);
|
||||
case MQ_L1_WRITE:
|
||||
case MQ_TCH_WRITE:
|
||||
case MQ_PDTCH_WRITE:
|
||||
return sizeof(GsmL1_Prim_t);
|
||||
default:
|
||||
/* The compiler can't know that priv_nr is an enum. Assist. */
|
||||
LOGP(DL1C, LOGL_FATAL, "writing on a wrong queue: %d\n",
|
||||
queue);
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* callback when there's something to read from the l1 msg_queue */
|
||||
static int read_dispatch_one(struct oc2gl1_hdl *fl1h, struct msgb *msg, int queue)
|
||||
{
|
||||
switch (queue) {
|
||||
case MQ_SYS_WRITE:
|
||||
return l1if_handle_sysprim(fl1h, msg);
|
||||
case MQ_L1_WRITE:
|
||||
case MQ_TCH_WRITE:
|
||||
case MQ_PDTCH_WRITE:
|
||||
return l1if_handle_l1prim(queue, fl1h, msg);
|
||||
default:
|
||||
/* The compiler can't know that priv_nr is an enum. Assist. */
|
||||
LOGP(DL1C, LOGL_FATAL, "writing on a wrong queue: %d\n",
|
||||
queue);
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
const uint32_t prim_size = prim_size_for_queue(ofd->priv_nr);
|
||||
uint32_t count;
|
||||
|
||||
struct iovec iov[3];
|
||||
struct msgb *msg[ARRAY_SIZE(iov)];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(iov); ++i) {
|
||||
msg[i] = msgb_alloc_headroom(prim_size + 128, 128, "1l_fd");
|
||||
msg[i]->l1h = msg[i]->data;
|
||||
|
||||
iov[i].iov_base = msg[i]->l1h;
|
||||
iov[i].iov_len = msgb_tailroom(msg[i]);
|
||||
}
|
||||
|
||||
rc = readv(ofd->fd, iov, ARRAY_SIZE(iov));
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_ERROR, "failed to read from fd: %s\n", strerror(errno));
|
||||
/* N. B: we do not abort to let the cycle below cleanup allocated memory properly,
|
||||
the return value is ignored by the caller anyway.
|
||||
TODO: use libexplain's explain_readv() to provide detailed error description */
|
||||
count = 0;
|
||||
} else
|
||||
count = rc / prim_size;
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
msgb_put(msg[i], prim_size);
|
||||
read_dispatch_one(ofd->data, msg[i], ofd->priv_nr);
|
||||
}
|
||||
|
||||
for (i = count; i < ARRAY_SIZE(iov); ++i)
|
||||
msgb_free(msg[i]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* callback when we can write to one of the l1 msg_queue devices */
|
||||
static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = write(ofd->fd, msg->l1h, msgb_l1len(msg));
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
|
||||
strerror(errno));
|
||||
return rc;
|
||||
} else if (rc < msg->len) {
|
||||
LOGP(DL1C, LOGL_ERROR, "short write to L1 msg_queue: "
|
||||
"%u < %u\n", rc, msg->len);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l1if_transport_open(int q, struct oc2gl1_hdl *hdl)
|
||||
{
|
||||
struct phy_link *plink = hdl->phy_inst->phy_link;
|
||||
int rc;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
/* Step 1: Open all msg_queue file descriptors */
|
||||
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
|
||||
struct osmo_wqueue *wq = &hdl->write_q[q];
|
||||
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, "%s%d", rd_devnames[q], plink->num);
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
rc = open(buf, O_RDONLY);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
|
||||
buf, strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
read_ofd->fd = rc;
|
||||
read_ofd->priv_nr = q;
|
||||
read_ofd->data = hdl;
|
||||
read_ofd->cb = l1if_fd_cb;
|
||||
read_ofd->when = BSC_FD_READ;
|
||||
rc = osmo_fd_register(read_ofd);
|
||||
if (rc < 0) {
|
||||
close(read_ofd->fd);
|
||||
read_ofd->fd = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, "%s%d", wr_devnames[q], plink->num);
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
rc = open(buf, O_WRONLY);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
|
||||
buf, strerror(errno));
|
||||
goto out_read;
|
||||
}
|
||||
osmo_wqueue_init(wq, 10);
|
||||
wq->write_cb = l1fd_write_cb;
|
||||
write_ofd->cb = wqueue_vector_cb;
|
||||
write_ofd->fd = rc;
|
||||
write_ofd->priv_nr = q;
|
||||
write_ofd->data = hdl;
|
||||
write_ofd->when = BSC_FD_WRITE;
|
||||
rc = osmo_fd_register(write_ofd);
|
||||
if (rc < 0) {
|
||||
close(write_ofd->fd);
|
||||
write_ofd->fd = -1;
|
||||
goto out_read;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_read:
|
||||
close(hdl->read_ofd[q].fd);
|
||||
osmo_fd_unregister(&hdl->read_ofd[q]);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int l1if_transport_close(int q, struct oc2gl1_hdl *hdl)
|
||||
{
|
||||
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
|
||||
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
|
||||
|
||||
osmo_fd_unregister(read_ofd);
|
||||
close(read_ofd->fd);
|
||||
read_ofd->fd = -1;
|
||||
|
||||
osmo_fd_unregister(write_ofd);
|
||||
close(write_ofd->fd);
|
||||
write_ofd->fd = -1;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
/* Main program for NuRAN Wireless OC-2G BTS */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
* Copyright (C) 2016 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* (C) 2011-2013 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <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 <sched.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/vty.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/pcu_if.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
|
||||
static int write_status_file(char *status_file, char *status_str)
|
||||
{
|
||||
FILE *outf;
|
||||
char tmp[PATH_MAX+1];
|
||||
|
||||
snprintf(tmp, sizeof(tmp)-1, "/var/run/osmo-bts/%s", status_file);
|
||||
tmp[PATH_MAX-1] = '\0';
|
||||
|
||||
outf = fopen(tmp, "w");
|
||||
if (!outf)
|
||||
return -1;
|
||||
|
||||
fprintf(outf, "%s\n", status_str);
|
||||
|
||||
fclose(outf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*NTQD: Change how rx_nr is handle in multi-trx*/
|
||||
#define OC2GBTS_RF_LOCK_PATH "/var/lock/bts_rf_lock"
|
||||
|
||||
#include "utils.h"
|
||||
#include "l1_if.h"
|
||||
#include "hw_misc.h"
|
||||
#include "oml_router.h"
|
||||
#include "misc/oc2gbts_bid.h"
|
||||
|
||||
unsigned int dsp_trace = 0x00000000;
|
||||
|
||||
int bts_model_init(struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_trx *trx;
|
||||
struct stat st;
|
||||
static struct osmo_fd accept_fd, read_fd;
|
||||
int rc;
|
||||
|
||||
bts->variant = BTS_OSMO_OC2G;
|
||||
bts->support.ciphers = CIPHER_A5(1) | CIPHER_A5(2) | CIPHER_A5(3);
|
||||
/* specific default values for OC2G platform */
|
||||
|
||||
/* TODO(oramadan) MERGE
|
||||
bts->oc2g.led_ctrl_mode = OC2G_BTS_LED_CTRL_MODE_DEFAULT;
|
||||
/* RTP drift threshold default * /
|
||||
bts->oc2g.rtp_drift_thres_ms = OC2G_BTS_RTP_DRIFT_THRES_DEFAULT;
|
||||
*/
|
||||
|
||||
rc = oml_router_init(bts, OML_ROUTER_PATH, &accept_fd, &read_fd);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error creating the OML router: %s rc=%d\n",
|
||||
OML_ROUTER_PATH, rc);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
trx->nominal_power = 40;
|
||||
trx->power_params.trx_p_max_out_mdBm = to_mdB(bts->c0->nominal_power);
|
||||
}
|
||||
|
||||
if (stat(OC2GBTS_RF_LOCK_PATH, &st) == 0) {
|
||||
LOGP(DL1C, LOGL_NOTICE, "Not starting BTS due to RF_LOCK file present\n");
|
||||
exit(23);
|
||||
}
|
||||
|
||||
gsm_bts_set_feature(bts, BTS_FEAT_GPRS);
|
||||
gsm_bts_set_feature(bts, BTS_FEAT_EGPRS);
|
||||
gsm_bts_set_feature(bts, BTS_FEAT_OML_ALERTS);
|
||||
gsm_bts_set_feature(bts, BTS_FEAT_AGCH_PCH_PROP);
|
||||
gsm_bts_set_feature(bts, BTS_FEAT_SPEECH_F_V1);
|
||||
gsm_bts_set_feature(bts, BTS_FEAT_SPEECH_H_V1);
|
||||
gsm_bts_set_feature(bts, BTS_FEAT_SPEECH_F_EFR);
|
||||
gsm_bts_set_feature(bts, BTS_FEAT_SPEECH_F_AMR);
|
||||
gsm_bts_set_feature(bts, BTS_FEAT_SPEECH_H_AMR);
|
||||
|
||||
bts_model_vty_init(bts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bts_model_phy_link_set_defaults(struct phy_link *plink)
|
||||
{
|
||||
}
|
||||
|
||||
void bts_model_phy_instance_set_defaults(struct phy_instance *pinst)
|
||||
{
|
||||
}
|
||||
|
||||
int bts_model_oml_estab(struct gsm_bts *bts)
|
||||
{
|
||||
/* update status file */
|
||||
write_status_file("state", "");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bts_update_status(enum bts_global_status which, int on)
|
||||
{
|
||||
static uint64_t states = 0;
|
||||
uint64_t old_states = states;
|
||||
int led_rf_active_on;
|
||||
|
||||
if (on)
|
||||
states |= (1ULL << which);
|
||||
else
|
||||
states &= ~(1ULL << which);
|
||||
|
||||
led_rf_active_on =
|
||||
(states & (1ULL << BTS_STATUS_RF_ACTIVE)) &&
|
||||
!(states & (1ULL << BTS_STATUS_RF_MUTE));
|
||||
|
||||
LOGP(DL1C, LOGL_INFO,
|
||||
"Set global status #%d to %d (%04llx -> %04llx), LEDs: ACT %d\n",
|
||||
which, on,
|
||||
(long long)old_states, (long long)states,
|
||||
led_rf_active_on);
|
||||
|
||||
oc2gbts_led_set(led_rf_active_on ? LED_GREEN : LED_OFF);
|
||||
}
|
||||
|
||||
void bts_model_print_help()
|
||||
{
|
||||
printf( " -w --hw-version Print the targeted HW Version\n"
|
||||
" -M --pcu-direct Force PCU to access message queue for PDCH dchannel directly\n"
|
||||
" -p --dsp-trace Set DSP trace flags\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void print_hwversion()
|
||||
{
|
||||
printf(get_hwversion_desc());
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int bts_model_handle_options(int argc, char **argv)
|
||||
{
|
||||
int num_errors = 0;
|
||||
|
||||
while (1) {
|
||||
int option_idx = 0, c;
|
||||
static const struct option long_options[] = {
|
||||
{ "dsp-trace", 1, 0, 'p' },
|
||||
{ "hw-version", 0, 0, 'w' },
|
||||
{ "pcu-direct", 0, 0, 'M' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "p:wM",
|
||||
long_options, &option_idx);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'p':
|
||||
dsp_trace = strtoul(optarg, NULL, 16);
|
||||
break;
|
||||
case 'M':
|
||||
pcu_direct = 1;
|
||||
break;
|
||||
case 'w':
|
||||
print_hwversion();
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
num_errors++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return num_errors;
|
||||
}
|
||||
|
||||
void bts_model_abis_close(struct gsm_bts *bts)
|
||||
{
|
||||
/* write to status file */
|
||||
write_status_file("state", "ABIS DOWN");
|
||||
|
||||
/* for now, we simply terminate the program and re-spawn */
|
||||
bts_shutdown(bts, "Abis close");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* create status file with initial state */
|
||||
write_status_file("state", "ABIS DOWN");
|
||||
|
||||
return bts_main(argc, argv);
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "oc2gbts_bid.h"
|
||||
|
||||
#define BOARD_REV_MAJ_SYSFS "/var/oc2g/platform/rev/major"
|
||||
#define BOARD_REV_MIN_SYSFS "/var/oc2g/platform/rev/minor"
|
||||
#define BOARD_OPT_SYSFS "/var/oc2g/platform/option"
|
||||
|
||||
static const int option_type_mask[_NUM_OPTION_TYPES] = {
|
||||
[OC2GBTS_OPTION_OCXO] = 0x07,
|
||||
[OC2GBTS_OPTION_FPGA] = 0x03,
|
||||
[OC2GBTS_OPTION_PA] = 0x01,
|
||||
[OC2GBTS_OPTION_BAND] = 0x03,
|
||||
[OC2GBTS_OPTION_TX_ATT] = 0x01,
|
||||
[OC2GBTS_OPTION_TX_FIL] = 0x01,
|
||||
[OC2GBTS_OPTION_RX_ATT] = 0x01,
|
||||
[OC2GBTS_OPTION_RMS_FWD] = 0x01,
|
||||
[OC2GBTS_OPTION_RMS_REFL] = 0x01,
|
||||
[OC2GBTS_OPTION_DDR_32B] = 0x01,
|
||||
[OC2GBTS_OPTION_DDR_ECC] = 0x01,
|
||||
[OC2GBTS_OPTION_PA_TEMP] = 0x01,
|
||||
};
|
||||
|
||||
static const int option_type_shift[_NUM_OPTION_TYPES] = {
|
||||
[OC2GBTS_OPTION_OCXO] = 0,
|
||||
[OC2GBTS_OPTION_FPGA] = 3,
|
||||
[OC2GBTS_OPTION_PA] = 5,
|
||||
[OC2GBTS_OPTION_BAND] = 6,
|
||||
[OC2GBTS_OPTION_TX_ATT] = 8,
|
||||
[OC2GBTS_OPTION_TX_FIL] = 9,
|
||||
[OC2GBTS_OPTION_RX_ATT] = 10,
|
||||
[OC2GBTS_OPTION_RMS_FWD] = 11,
|
||||
[OC2GBTS_OPTION_RMS_REFL] = 12,
|
||||
[OC2GBTS_OPTION_DDR_32B] = 13,
|
||||
[OC2GBTS_OPTION_DDR_ECC] = 14,
|
||||
[OC2GBTS_OPTION_PA_TEMP] = 15,
|
||||
};
|
||||
|
||||
|
||||
static char board_rev_maj = -1;
|
||||
static char board_rev_min = -1;
|
||||
static int board_option = -1;
|
||||
|
||||
void oc2gbts_rev_get(char *rev_maj, char *rev_min)
|
||||
{
|
||||
FILE *fp;
|
||||
char rev;
|
||||
|
||||
*rev_maj = 0;
|
||||
*rev_min = 0;
|
||||
|
||||
if (board_rev_maj != -1 && board_rev_min != -1) {
|
||||
*rev_maj = board_rev_maj;
|
||||
*rev_min = board_rev_min;
|
||||
}
|
||||
|
||||
fp = fopen(BOARD_REV_MAJ_SYSFS, "r");
|
||||
if (fp == NULL) return;
|
||||
|
||||
if (fscanf(fp, "%c", &rev) != 1) {
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
*rev_maj = board_rev_maj = rev;
|
||||
|
||||
fp = fopen(BOARD_REV_MIN_SYSFS, "r");
|
||||
if (fp == NULL) return;
|
||||
|
||||
if (fscanf(fp, "%c", &rev) != 1) {
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
*rev_min = board_rev_min = rev;
|
||||
}
|
||||
|
||||
const char* get_hwversion_desc()
|
||||
{
|
||||
int rev;
|
||||
int model;
|
||||
size_t len;
|
||||
static char model_name[64] = {0, };
|
||||
len = snprintf(model_name, sizeof(model_name), "NuRAN OC-2G BTS");
|
||||
|
||||
char rev_maj = 0, rev_min = 0;
|
||||
|
||||
int rc = 0;
|
||||
oc2gbts_rev_get(&rev_maj, &rev_min);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (rev >= 0) {
|
||||
len += snprintf(model_name + len, sizeof(model_name) - len,
|
||||
" Rev %d.%d", (uint8_t)rev_maj, (uint8_t)rev_min);
|
||||
}
|
||||
|
||||
model = oc2gbts_model_get();
|
||||
if (model >= 0) {
|
||||
snprintf(model_name + len, sizeof(model_name) - len,
|
||||
"%s (%05X)", model_name, model);
|
||||
}
|
||||
return model_name;
|
||||
}
|
||||
|
||||
int oc2gbts_model_get(void)
|
||||
{
|
||||
FILE *fp;
|
||||
int opt;
|
||||
|
||||
|
||||
if (board_option == -1) {
|
||||
fp = fopen(BOARD_OPT_SYSFS, "r");
|
||||
if (fp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fscanf(fp, "%X", &opt) != 1) {
|
||||
fclose( fp );
|
||||
return -1;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
board_option = opt;
|
||||
}
|
||||
return board_option;
|
||||
}
|
||||
|
||||
int oc2gbts_option_get(enum oc2gbts_option_type type)
|
||||
{
|
||||
int rc;
|
||||
int option;
|
||||
|
||||
if (type >= _NUM_OPTION_TYPES) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (board_option == -1) {
|
||||
rc = oc2gbts_model_get();
|
||||
if (rc < 0) return rc;
|
||||
}
|
||||
|
||||
option = (board_option >> option_type_shift[type])
|
||||
& option_type_mask[type];
|
||||
|
||||
return option;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef _OC2GBTS_BOARD_H
|
||||
#define _OC2GBTS_BOARD_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum oc2gbts_option_type {
|
||||
OC2GBTS_OPTION_OCXO,
|
||||
OC2GBTS_OPTION_FPGA,
|
||||
OC2GBTS_OPTION_PA,
|
||||
OC2GBTS_OPTION_BAND,
|
||||
OC2GBTS_OPTION_TX_ATT,
|
||||
OC2GBTS_OPTION_TX_FIL,
|
||||
OC2GBTS_OPTION_RX_ATT,
|
||||
OC2GBTS_OPTION_RMS_FWD,
|
||||
OC2GBTS_OPTION_RMS_REFL,
|
||||
OC2GBTS_OPTION_DDR_32B,
|
||||
OC2GBTS_OPTION_DDR_ECC,
|
||||
OC2GBTS_OPTION_PA_TEMP,
|
||||
_NUM_OPTION_TYPES
|
||||
};
|
||||
|
||||
enum oc2gbts_ocxo_type {
|
||||
OC2GBTS_OCXO_BILAY_NVG45AV2072,
|
||||
OC2GBTS_OCXO_TAITIEN_NJ26M003,
|
||||
_NUM_OCXO_TYPES
|
||||
};
|
||||
|
||||
enum oc2gbts_fpga_type {
|
||||
OC2GBTS_FPGA_35T,
|
||||
OC2GBTS_FPGA_50T,
|
||||
OC2GBTS_FPGA_75T,
|
||||
OC2GBTS_FPGA_100T,
|
||||
_NUM_FPGA_TYPES
|
||||
};
|
||||
|
||||
enum oc2gbts_gsm_band {
|
||||
OC2GBTS_BAND_850,
|
||||
OC2GBTS_BAND_900,
|
||||
OC2GBTS_BAND_1800,
|
||||
OC2GBTS_BAND_1900,
|
||||
};
|
||||
|
||||
void oc2gbts_rev_get(char *rev_maj, char *rev_min);
|
||||
int oc2gbts_model_get(void);
|
||||
int oc2gbts_option_get(enum oc2gbts_option_type type);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,129 @@
|
|||
/* Copyright (C) 2016 by NuRAN Wireless <support@nuranwireless.com>
|
||||
*
|
||||
* 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 <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include "oc2gbts_mgr.h"
|
||||
#include "oc2gbts_bts.h"
|
||||
|
||||
static int check_eth_status(char *dev_name)
|
||||
{
|
||||
int fd, rc;
|
||||
struct ifreq ifr;
|
||||
|
||||
fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
memcpy(&ifr.ifr_name, dev_name, sizeof(ifr.ifr_name));
|
||||
rc = ioctl(fd, SIOCGIFFLAGS, &ifr);
|
||||
close(fd);
|
||||
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if ((ifr.ifr_flags & IFF_UP) && (ifr.ifr_flags & IFF_RUNNING))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void check_bts_led_pattern(uint8_t *led)
|
||||
{
|
||||
FILE *fp;
|
||||
char str[64] = "\0";
|
||||
int rc;
|
||||
|
||||
/* check for existing of BTS state file */
|
||||
if ((fp = fopen("/var/run/osmo-bts/state", "r")) == NULL) {
|
||||
led[BLINK_PATTERN_INIT] = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* check Ethernet interface status */
|
||||
rc = check_eth_status("eth0");
|
||||
if (rc > 0) {
|
||||
LOGP(DTEMP, LOGL_DEBUG,"External link is DOWN\n");
|
||||
led[BLINK_PATTERN_EXT_LINK_MALFUNC] = 1;
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
/* check for BTS is still alive */
|
||||
if (system("pidof osmo-bts-oc2g > /dev/null")) {
|
||||
LOGP(DTEMP, LOGL_DEBUG,"BTS process has stopped\n");
|
||||
led[BLINK_PATTERN_INT_PROC_MALFUNC] = 1;
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
/* check for BTS state */
|
||||
while (fgets(str, 64, fp) != NULL) {
|
||||
LOGP(DTEMP, LOGL_DEBUG,"BTS state is %s\n", (strstr(str, "ABIS DOWN") != NULL) ? "DOWN" : "UP");
|
||||
if (strstr(str, "ABIS DOWN") != NULL)
|
||||
led[BLINK_PATTERN_INT_PROC_MALFUNC] = 1;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int check_sensor_led_pattern( struct oc2gbts_mgr_instance *mgr, uint8_t *led)
|
||||
{
|
||||
if(mgr->alarms.temp_high == 1)
|
||||
led[BLINK_PATTERN_TEMP_HIGH] = 1;
|
||||
|
||||
if(mgr->alarms.temp_max == 1)
|
||||
led[BLINK_PATTERN_TEMP_MAX] = 1;
|
||||
|
||||
if(mgr->alarms.supply_low == 1)
|
||||
led[BLINK_PATTERN_SUPPLY_VOLT_LOW] = 1;
|
||||
|
||||
if(mgr->alarms.supply_min == 1)
|
||||
led[BLINK_PATTERN_SUPPLY_VOLT_MIN] = 1;
|
||||
|
||||
if(mgr->alarms.vswr_high == 1)
|
||||
led[BLINK_PATTERN_VSWR_HIGH] = 1;
|
||||
|
||||
if(mgr->alarms.vswr_max == 1)
|
||||
led[BLINK_PATTERN_VSWR_MAX] = 1;
|
||||
|
||||
if(mgr->alarms.supply_pwr_high == 1)
|
||||
led[BLINK_PATTERN_SUPPLY_PWR_HIGH] = 1;
|
||||
|
||||
if(mgr->alarms.supply_pwr_max == 1)
|
||||
led[BLINK_PATTERN_SUPPLY_PWR_MAX] = 1;
|
||||
|
||||
if(mgr->alarms.pa_pwr_high == 1)
|
||||
led[BLINK_PATTERN_PA_PWR_HIGH] = 1;
|
||||
|
||||
if(mgr->alarms.pa_pwr_max == 1)
|
||||
led[BLINK_PATTERN_PA_PWR_MAX] = 1;
|
||||
|
||||
if(mgr->alarms.gps_fix_lost == 1)
|
||||
led[BLINK_PATTERN_GPS_FIX_LOST] = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef _OC2GBTS_BTS_H_
|
||||
#define _OC2GBTS_BTS_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
|
||||
/* public function prototypes */
|
||||
void check_bts_led_pattern(uint8_t *led);
|
||||
int check_sensor_led_pattern( struct oc2gbts_mgr_instance *mgr, uint8_t *led);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,263 @@
|
|||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "oc2gbts_clock.h"
|
||||
|
||||
#define CLKERR_ERR_SYSFS "/var/oc2g/clkerr/clkerr1_average"
|
||||
#define CLKERR_ACC_SYSFS "/var/oc2g/clkerr/clkerr1_average_accuracy"
|
||||
#define CLKERR_INT_SYSFS "/var/oc2g/clkerr/clkerr1_average_interval"
|
||||
#define CLKERR_FLT_SYSFS "/var/oc2g/clkerr/clkerr1_fault"
|
||||
#define CLKERR_RFS_SYSFS "/var/oc2g/clkerr/refresh"
|
||||
#define CLKERR_RST_SYSFS "/var/oc2g/clkerr/reset"
|
||||
|
||||
#define OCXODAC_VAL_SYSFS "/var/oc2g/ocxo/voltage"
|
||||
#define OCXODAC_ROM_SYSFS "/var/oc2g/ocxo/eeprom"
|
||||
|
||||
/* clock error */
|
||||
static int clkerr_fd_err = -1;
|
||||
static int clkerr_fd_accuracy = -1;
|
||||
static int clkerr_fd_interval = -1;
|
||||
static int clkerr_fd_fault = -1;
|
||||
static int clkerr_fd_refresh = -1;
|
||||
static int clkerr_fd_reset = -1;
|
||||
|
||||
/* ocxo dac */
|
||||
static int ocxodac_fd_value = -1;
|
||||
static int ocxodac_fd_save = -1;
|
||||
|
||||
|
||||
static int sysfs_read_val(int fd, int *val)
|
||||
{
|
||||
int rc;
|
||||
char szVal[32] = {0};
|
||||
|
||||
lseek( fd, 0, SEEK_SET );
|
||||
|
||||
rc = read(fd, szVal, sizeof(szVal) - 1);
|
||||
if (rc < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
rc = sscanf(szVal, "%d", val);
|
||||
if (rc != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sysfs_write_val(int fd, int val)
|
||||
{
|
||||
int n, rc;
|
||||
char szVal[32] = {0};
|
||||
|
||||
n = sprintf(szVal, "%d", val);
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
rc = write(fd, szVal, n+1);
|
||||
if (rc < 0) {
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sysfs_write_str(int fd, const char *str)
|
||||
{
|
||||
int rc;
|
||||
|
||||
lseek( fd, 0, SEEK_SET );
|
||||
rc = write(fd, str, strlen(str)+1);
|
||||
if (rc < 0) {
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int oc2gbts_clock_err_open(void)
|
||||
{
|
||||
int rc;
|
||||
int fault;
|
||||
|
||||
if (clkerr_fd_err < 0) {
|
||||
clkerr_fd_err = open(CLKERR_ERR_SYSFS, O_RDONLY);
|
||||
if (clkerr_fd_err < 0) {
|
||||
oc2gbts_clock_err_close();
|
||||
return clkerr_fd_err;
|
||||
}
|
||||
}
|
||||
|
||||
if (clkerr_fd_accuracy < 0) {
|
||||
clkerr_fd_accuracy = open(CLKERR_ACC_SYSFS, O_RDONLY);
|
||||
if (clkerr_fd_accuracy < 0) {
|
||||
oc2gbts_clock_err_close();
|
||||
return clkerr_fd_accuracy;
|
||||
}
|
||||
}
|
||||
|
||||
if (clkerr_fd_interval < 0) {
|
||||
clkerr_fd_interval = open(CLKERR_INT_SYSFS, O_RDONLY);
|
||||
if (clkerr_fd_interval < 0) {
|
||||
oc2gbts_clock_err_close();
|
||||
return clkerr_fd_interval;
|
||||
}
|
||||
}
|
||||
|
||||
if (clkerr_fd_fault < 0) {
|
||||
clkerr_fd_fault = open(CLKERR_FLT_SYSFS, O_RDONLY);
|
||||
if (clkerr_fd_fault < 0) {
|
||||
oc2gbts_clock_err_close();
|
||||
return clkerr_fd_fault;
|
||||
}
|
||||
}
|
||||
|
||||
if (clkerr_fd_refresh < 0) {
|
||||
clkerr_fd_refresh = open(CLKERR_RFS_SYSFS, O_WRONLY);
|
||||
if (clkerr_fd_refresh < 0) {
|
||||
oc2gbts_clock_err_close();
|
||||
return clkerr_fd_refresh;
|
||||
}
|
||||
}
|
||||
|
||||
if (clkerr_fd_reset < 0) {
|
||||
clkerr_fd_reset = open(CLKERR_RST_SYSFS, O_WRONLY);
|
||||
if (clkerr_fd_reset < 0) {
|
||||
oc2gbts_clock_err_close();
|
||||
return clkerr_fd_reset;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void oc2gbts_clock_err_close(void)
|
||||
{
|
||||
if (clkerr_fd_err >= 0) {
|
||||
close(clkerr_fd_err);
|
||||
clkerr_fd_err = -1;
|
||||
}
|
||||
|
||||
if (clkerr_fd_accuracy >= 0) {
|
||||
close(clkerr_fd_accuracy);
|
||||
clkerr_fd_accuracy = -1;
|
||||
}
|
||||
|
||||
if (clkerr_fd_interval >= 0) {
|
||||
close(clkerr_fd_interval);
|
||||
clkerr_fd_interval = -1;
|
||||
}
|
||||
|
||||
if (clkerr_fd_fault >= 0) {
|
||||
close(clkerr_fd_fault);
|
||||
clkerr_fd_fault = -1;
|
||||
}
|
||||
|
||||
if (clkerr_fd_refresh >= 0) {
|
||||
close(clkerr_fd_refresh);
|
||||
clkerr_fd_refresh = -1;
|
||||
}
|
||||
|
||||
if (clkerr_fd_reset >= 0) {
|
||||
close(clkerr_fd_reset);
|
||||
clkerr_fd_reset = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int oc2gbts_clock_err_reset(void)
|
||||
{
|
||||
return sysfs_write_val(clkerr_fd_reset, 1);
|
||||
}
|
||||
|
||||
int oc2gbts_clock_err_get(int *fault, int *error_ppt,
|
||||
int *accuracy_ppq, int *interval_sec)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = sysfs_write_str(clkerr_fd_refresh, "once");
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = sysfs_read_val(clkerr_fd_fault, fault);
|
||||
rc |= sysfs_read_val(clkerr_fd_err, error_ppt);
|
||||
rc |= sysfs_read_val(clkerr_fd_accuracy, accuracy_ppq);
|
||||
rc |= sysfs_read_val(clkerr_fd_interval, interval_sec);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int oc2gbts_clock_dac_open(void)
|
||||
{
|
||||
if (ocxodac_fd_value < 0) {
|
||||
ocxodac_fd_value = open(OCXODAC_VAL_SYSFS, O_RDWR);
|
||||
if (ocxodac_fd_value < 0) {
|
||||
oc2gbts_clock_dac_close();
|
||||
return ocxodac_fd_value;
|
||||
}
|
||||
}
|
||||
|
||||
if (ocxodac_fd_save < 0) {
|
||||
ocxodac_fd_save = open(OCXODAC_ROM_SYSFS, O_WRONLY);
|
||||
if (ocxodac_fd_save < 0) {
|
||||
oc2gbts_clock_dac_close();
|
||||
return ocxodac_fd_save;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void oc2gbts_clock_dac_close(void)
|
||||
{
|
||||
if (ocxodac_fd_value >= 0) {
|
||||
close(ocxodac_fd_value);
|
||||
ocxodac_fd_value = -1;
|
||||
}
|
||||
|
||||
if (ocxodac_fd_save >= 0) {
|
||||
close(ocxodac_fd_save);
|
||||
ocxodac_fd_save = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int oc2gbts_clock_dac_get(int *dac_value)
|
||||
{
|
||||
return sysfs_read_val(ocxodac_fd_value, dac_value);
|
||||
}
|
||||
|
||||
int oc2gbts_clock_dac_set(int dac_value)
|
||||
{
|
||||
return sysfs_write_val(ocxodac_fd_value, dac_value);
|
||||
}
|
||||
|
||||
int oc2gbts_clock_dac_save(void)
|
||||
{
|
||||
return sysfs_write_val(ocxodac_fd_save, 1);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef _OC2GBTS_CLOCK_H
|
||||
#define _OC2GBTS_CLOCK_H
|
||||
|
||||
int oc2gbts_clock_err_open(void);
|
||||
void oc2gbts_clock_err_close(void);
|
||||
int oc2gbts_clock_err_reset(void);
|
||||
int oc2gbts_clock_err_get(int *fault, int *error_ppt,
|
||||
int *accuracy_ppq, int *interval_sec);
|
||||
|
||||
int oc2gbts_clock_dac_open(void);
|
||||
void oc2gbts_clock_dac_close(void);
|
||||
int oc2gbts_clock_dac_get(int *dac_value);
|
||||
int oc2gbts_clock_dac_set(int dac_value);
|
||||
int oc2gbts_clock_dac_save(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,332 @@
|
|||
/* Copyright (C) 2016 by NuRAN Wireless <support@nuranwireless.com>
|
||||
*
|
||||
* 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 "oc2gbts_led.h"
|
||||
#include "oc2gbts_bts.h"
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
static struct oc2gbts_led led_entries[] = {
|
||||
{
|
||||
.name = "led0",
|
||||
.fullname = "led red",
|
||||
.path = "/var/oc2g/leds/led0/brightness"
|
||||
},
|
||||
{
|
||||
.name = "led1",
|
||||
.fullname = "led green",
|
||||
.path = "/var/oc2g/leds/led1/brightness"
|
||||
}
|
||||
};
|
||||
|
||||
static const struct value_string oc2gbts_led_strs[] = {
|
||||
{ OC2GBTS_LED_RED, "LED red" },
|
||||
{ OC2GBTS_LED_GREEN, "LED green" },
|
||||
{ OC2GBTS_LED_ORANGE, "LED orange" },
|
||||
{ OC2GBTS_LED_OFF, "LED off" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static uint8_t led_priority[] = {
|
||||
BLINK_PATTERN_INIT,
|
||||
BLINK_PATTERN_INT_PROC_MALFUNC,
|
||||
BLINK_PATTERN_SUPPLY_PWR_MAX,
|
||||
BLINK_PATTERN_PA_PWR_MAX,
|
||||
BLINK_PATTERN_VSWR_MAX,
|
||||
BLINK_PATTERN_SUPPLY_VOLT_MIN,
|
||||
BLINK_PATTERN_TEMP_MAX,
|
||||
BLINK_PATTERN_EXT_LINK_MALFUNC,
|
||||
BLINK_PATTERN_SUPPLY_VOLT_LOW,
|
||||
BLINK_PATTERN_TEMP_HIGH,
|
||||
BLINK_PATTERN_VSWR_HIGH,
|
||||
BLINK_PATTERN_SUPPLY_PWR_HIGH,
|
||||
BLINK_PATTERN_PA_PWR_HIGH,
|
||||
BLINK_PATTERN_GPS_FIX_LOST,
|
||||
BLINK_PATTERN_NORMAL
|
||||
};
|
||||
|
||||
|
||||
char *blink_pattern_command[] = BLINK_PATTERN_COMMAND;
|
||||
|
||||
static int oc2gbts_led_write(char *path, char *str)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if ((fd = open(path, O_WRONLY)) == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
write(fd, str, strlen(str)+1);
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void led_set_red()
|
||||
{
|
||||
oc2gbts_led_write(led_entries[0].path, "1");
|
||||
oc2gbts_led_write(led_entries[1].path, "0");
|
||||
}
|
||||
|
||||
static void led_set_green()
|
||||
{
|
||||
oc2gbts_led_write(led_entries[0].path, "0");
|
||||
oc2gbts_led_write(led_entries[1].path, "1");
|
||||
}
|
||||
|
||||
static void led_set_orange()
|
||||
{
|
||||
oc2gbts_led_write(led_entries[0].path, "1");
|
||||
oc2gbts_led_write(led_entries[1].path, "1");
|
||||
}
|
||||
|
||||
static void led_set_off()
|
||||
{
|
||||
oc2gbts_led_write(led_entries[0].path, "0");
|
||||
oc2gbts_led_write(led_entries[1].path, "0");
|
||||
}
|
||||
|
||||
static void led_sleep( struct oc2gbts_mgr_instance *mgr, struct oc2gbts_led_timer *led_timer, void (*led_timer_cb)(void *_data)) {
|
||||
/* Cancel any pending timer */
|
||||
osmo_timer_del(&led_timer->timer);
|
||||
/* Start LED timer */
|
||||
led_timer->timer.cb = led_timer_cb;
|
||||
led_timer->timer.data = mgr;
|
||||
mgr->oc2gbts_leds.active_timer = led_timer->idx;
|
||||
osmo_timer_schedule(&led_timer->timer, led_timer->param.sleep_sec, led_timer->param.sleep_usec);
|
||||
LOGP(DTEMP, LOGL_DEBUG,"%s timer scheduled for %d sec + %d usec\n",
|
||||
get_value_string(oc2gbts_led_strs, led_timer->idx),
|
||||
led_timer->param.sleep_sec,
|
||||
led_timer->param.sleep_usec);
|
||||
|
||||
switch (led_timer->idx) {
|
||||
case OC2GBTS_LED_RED:
|
||||
led_set_red();
|
||||
break;
|
||||
case OC2GBTS_LED_GREEN:
|
||||
led_set_green();
|
||||
break;
|
||||
case OC2GBTS_LED_ORANGE:
|
||||
led_set_orange();
|
||||
break;
|
||||
case OC2GBTS_LED_OFF:
|
||||
led_set_off();
|
||||
break;
|
||||
default:
|
||||
led_set_off();
|
||||
}
|
||||
}
|
||||
|
||||
static void led_sleep_cb(void *_data) {
|
||||
struct oc2gbts_mgr_instance *mgr = _data;
|
||||
struct oc2gbts_led_timer_list *led_list;
|
||||
|
||||
/* make sure the timer list is not empty */
|
||||
if (llist_empty(&mgr->oc2gbts_leds.list))
|
||||
return;
|
||||
|
||||
llist_for_each_entry(led_list, &mgr->oc2gbts_leds.list, list) {
|
||||
if (led_list->led_timer.idx == mgr->oc2gbts_leds.active_timer) {
|
||||
LOGP(DTEMP, LOGL_DEBUG,"Delete expired %s timer %d sec + %d usec\n",
|
||||
get_value_string(oc2gbts_led_strs, led_list->led_timer.idx),
|
||||
led_list->led_timer.param.sleep_sec,
|
||||
led_list->led_timer.param.sleep_usec);
|
||||
|
||||
/* Delete current timer */
|
||||
osmo_timer_del(&led_list->led_timer.timer);
|
||||
/* Rotate the timer list */
|
||||
llist_move_tail(led_list, &mgr->oc2gbts_leds.list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute next timer */
|
||||
led_list = llist_first_entry(&mgr->oc2gbts_leds.list, struct oc2gbts_led_timer_list, list);
|
||||
if (led_list) {
|
||||
LOGP(DTEMP, LOGL_DEBUG,"Execute %s timer %d sec + %d usec, total entries=%d\n",
|
||||
get_value_string(oc2gbts_led_strs, led_list->led_timer.idx),
|
||||
led_list->led_timer.param.sleep_sec,
|
||||
led_list->led_timer.param.sleep_usec,
|
||||
llist_count(&mgr->oc2gbts_leds.list));
|
||||
|
||||
led_sleep(mgr, &led_list->led_timer, led_sleep_cb);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void delete_led_timer_entries(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
struct oc2gbts_led_timer_list *led_list, *led_list2;
|
||||
|
||||
if (llist_empty(&mgr->oc2gbts_leds.list))
|
||||
return;
|
||||
|
||||
llist_for_each_entry_safe(led_list, led_list2, &mgr->oc2gbts_leds.list, list) {
|
||||
/* Delete the timer in list */
|
||||
if (led_list) {
|
||||
LOGP(DTEMP, LOGL_DEBUG,"Delete %s timer entry from list, %d sec + %d usec\n",
|
||||
get_value_string(oc2gbts_led_strs, led_list->led_timer.idx),
|
||||
led_list->led_timer.param.sleep_sec,
|
||||
led_list->led_timer.param.sleep_usec);
|
||||
|
||||
/* Delete current timer */
|
||||
osmo_timer_del(&led_list->led_timer.timer);
|
||||
llist_del(&led_list->list);
|
||||
talloc_free(led_list);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int add_led_timer_entry(struct oc2gbts_mgr_instance *mgr, char *cmdstr)
|
||||
{
|
||||
double sec, int_sec, frac_sec;
|
||||
struct oc2gbts_sleep_time led_param;
|
||||
|
||||
led_param.sleep_sec = 0;
|
||||
led_param.sleep_usec = 0;
|
||||
|
||||
if (strstr(cmdstr, "set red") != NULL)
|
||||
mgr->oc2gbts_leds.led_idx = OC2GBTS_LED_RED;
|
||||
else if (strstr(cmdstr, "set green") != NULL)
|
||||
mgr->oc2gbts_leds.led_idx = OC2GBTS_LED_GREEN;
|
||||
else if (strstr(cmdstr, "set orange") != NULL)
|
||||
mgr->oc2gbts_leds.led_idx = OC2GBTS_LED_ORANGE;
|
||||
else if (strstr(cmdstr, "set off") != NULL)
|
||||
mgr->oc2gbts_leds.led_idx = OC2GBTS_LED_OFF;
|
||||
else if (strstr(cmdstr, "sleep") != NULL) {
|
||||
sec = atof(cmdstr + 6);
|
||||
/* split time into integer and fractional of seconds */
|
||||
frac_sec = modf(sec, &int_sec) * 1000000.0;
|
||||
led_param.sleep_sec = (int)int_sec;
|
||||
led_param.sleep_usec = (int)frac_sec;
|
||||
|
||||
if ((mgr->oc2gbts_leds.led_idx >= OC2GBTS_LED_RED) && (mgr->oc2gbts_leds.led_idx < _OC2GBTS_LED_MAX)) {
|
||||
struct oc2gbts_led_timer_list *led_list;
|
||||
|
||||
/* allocate timer entry */
|
||||
led_list = talloc_zero(tall_mgr_ctx, struct oc2gbts_led_timer_list);
|
||||
if (led_list) {
|
||||
led_list->led_timer.idx = mgr->oc2gbts_leds.led_idx;
|
||||
led_list->led_timer.param.sleep_sec = led_param.sleep_sec;
|
||||
led_list->led_timer.param.sleep_usec = led_param.sleep_usec;
|
||||
llist_add_tail(&led_list->list, &mgr->oc2gbts_leds.list);
|
||||
|
||||
LOGP(DTEMP, LOGL_DEBUG,"Add %s timer to list, %d sec + %d usec, total entries=%d\n",
|
||||
get_value_string(oc2gbts_led_strs, mgr->oc2gbts_leds.led_idx),
|
||||
led_list->led_timer.param.sleep_sec,
|
||||
led_list->led_timer.param.sleep_usec,
|
||||
llist_count(&mgr->oc2gbts_leds.list));
|
||||
}
|
||||
}
|
||||
} else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_led_pattern(char *pattern, struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
char str[1024];
|
||||
char *pstr;
|
||||
char *sep;
|
||||
int rc = 0;
|
||||
|
||||
strcpy(str, pattern);
|
||||
pstr = str;
|
||||
while ((sep = strsep(&pstr, ";")) != NULL) {
|
||||
rc = add_led_timer_entry(mgr, sep);
|
||||
if (rc < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*** led interface ***/
|
||||
|
||||
void led_set(struct oc2gbts_mgr_instance *mgr, int pattern_id)
|
||||
{
|
||||
int rc;
|
||||
struct oc2gbts_led_timer_list *led_list;
|
||||
|
||||
if (pattern_id > BLINK_PATTERN_MAX_ITEM - 1) {
|
||||
LOGP(DTEMP, LOGL_ERROR, "Invalid LED pattern : %d. LED pattern must be between %d..%d\n",
|
||||
pattern_id,
|
||||
BLINK_PATTERN_POWER_ON,
|
||||
BLINK_PATTERN_MAX_ITEM - 1);
|
||||
return;
|
||||
}
|
||||
if (pattern_id == mgr->oc2gbts_leds.last_pattern_id)
|
||||
return;
|
||||
|
||||
mgr->oc2gbts_leds.last_pattern_id = pattern_id;
|
||||
|
||||
LOGP(DTEMP, LOGL_INFO, "blink pattern command : %d\n", pattern_id);
|
||||
LOGP(DTEMP, LOGL_INFO, "%s\n", blink_pattern_command[pattern_id]);
|
||||
|
||||
/* Empty existing LED timer in the list */
|
||||
delete_led_timer_entries(mgr);
|
||||
|
||||
/* parse LED pattern */
|
||||
rc = parse_led_pattern(blink_pattern_command[pattern_id], mgr);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR,"LED pattern not found or invalid LED pattern\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* make sure the timer list is not empty */
|
||||
if (llist_empty(&mgr->oc2gbts_leds.list))
|
||||
return;
|
||||
|
||||
/* Start the first LED timer in the list */
|
||||
led_list = llist_first_entry(&mgr->oc2gbts_leds.list, struct oc2gbts_led_timer_list, list);
|
||||
if (led_list) {
|
||||
LOGP(DTEMP, LOGL_DEBUG,"Execute timer %s for %d sec + %d usec\n",
|
||||
get_value_string(oc2gbts_led_strs, led_list->led_timer.idx),
|
||||
led_list->led_timer.param.sleep_sec,
|
||||
led_list->led_timer.param.sleep_usec);
|
||||
|
||||
led_sleep(mgr, &led_list->led_timer, led_sleep_cb);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void select_led_pattern(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
int i;
|
||||
uint8_t led[BLINK_PATTERN_MAX_ITEM] = {0};
|
||||
|
||||
/* set normal LED pattern at first */
|
||||
led[BLINK_PATTERN_NORMAL] = 1;
|
||||
|
||||
/* check on-board sensors for new LED pattern */
|
||||
check_sensor_led_pattern(mgr, led);
|
||||
|
||||
/* check BTS status for new LED pattern */
|
||||
check_bts_led_pattern(led);
|
||||
|
||||
/* check by priority */
|
||||
for (i = 0; i < sizeof(led_priority)/sizeof(uint8_t); i++) {
|
||||
if(led[led_priority[i]] == 1) {
|
||||
led_set(mgr, led_priority[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef _OC2GBTS_LED_H
|
||||
#define _OC2GBTS_LED_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include "oc2gbts_mgr.h"
|
||||
|
||||
/* public function prototypes */
|
||||
void led_set(struct oc2gbts_mgr_instance *mgr, int pattern_id);
|
||||
|
||||
void select_led_pattern(struct oc2gbts_mgr_instance *mgr);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,353 @@
|
|||
/* Main program for NuRAN Wireless OC-2G BTS management daemon */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* sysmobts_mgr.c
|
||||
* (C) 2012 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2014 by Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
|
||||
#include "misc/oc2gbts_misc.h"
|
||||
#include "misc/oc2gbts_mgr.h"
|
||||
#include "misc/oc2gbts_par.h"
|
||||
#include "misc/oc2gbts_bid.h"
|
||||
#include "misc/oc2gbts_power.h"
|
||||
#include "misc/oc2gbts_swd.h"
|
||||
|
||||
#include "oc2gbts_led.h"
|
||||
|
||||
static int no_rom_write = 0;
|
||||
static int daemonize = 0;
|
||||
void *tall_mgr_ctx;
|
||||
|
||||
/* every 6 hours means 365*4 = 1460 rom writes per year (max) */
|
||||
#define SENSOR_TIMER_SECS (6 * 3600)
|
||||
|
||||
/* every 1 hours means 365*24 = 8760 rom writes per year (max) */
|
||||
#define HOURS_TIMER_SECS (1 * 3600)
|
||||
|
||||
/* the initial state */
|
||||
static struct oc2gbts_mgr_instance manager = {
|
||||
.config_file = "oc2gbts-mgr.cfg",
|
||||
.temp = {
|
||||
.supply_temp_limit = {
|
||||
.thresh_warn_max = 80,
|
||||
.thresh_crit_max = 85,
|
||||
.thresh_warn_min = -40,
|
||||
},
|
||||
.soc_temp_limit = {
|
||||
.thresh_warn_max = 95,
|
||||
.thresh_crit_max = 100,
|
||||
.thresh_warn_min = -40,
|
||||
},
|
||||
.fpga_temp_limit = {
|
||||
.thresh_warn_max = 95,
|
||||
.thresh_crit_max = 100,
|
||||
.thresh_warn_min = -40,
|
||||
},
|
||||
.rmsdet_temp_limit = {
|
||||
.thresh_warn_max = 80,
|
||||
.thresh_crit_max = 85,
|
||||
.thresh_warn_min = -40,
|
||||
},
|
||||
.ocxo_temp_limit = {
|
||||
.thresh_warn_max = 80,
|
||||
.thresh_crit_max = 85,
|
||||
.thresh_warn_min = -40,
|
||||
},
|
||||
.tx_temp_limit = {
|
||||
.thresh_warn_max = 80,
|
||||
.thresh_crit_max = 85,
|
||||
.thresh_warn_min = -20,
|
||||
},
|
||||
.pa_temp_limit = {
|
||||
.thresh_warn_max = 80,
|
||||
.thresh_crit_max = 85,
|
||||
.thresh_warn_min = -40,
|
||||
}
|
||||
},
|
||||
.volt = {
|
||||
.supply_volt_limit = {
|
||||
.thresh_warn_max = 30000,
|
||||
.thresh_crit_max = 30500,
|
||||
.thresh_warn_min = 19000,
|
||||
.thresh_crit_min = 17500,
|
||||
}
|
||||
},
|
||||
.pwr = {
|
||||
.supply_pwr_limit = {
|
||||
.thresh_warn_max = 30,
|
||||
.thresh_crit_max = 40,
|
||||
},
|
||||
.pa_pwr_limit = {
|
||||
.thresh_warn_max = 20,
|
||||
.thresh_crit_max = 30,
|
||||
}
|
||||
},
|
||||
.vswr = {
|
||||
.vswr_limit = {
|
||||
.thresh_warn_max = 3000,
|
||||
.thresh_crit_max = 5000,
|
||||
}
|
||||
},
|
||||
.gps = {
|
||||
.gps_fix_limit = {
|
||||
.thresh_warn_max = 7,
|
||||
}
|
||||
},
|
||||
.state = {
|
||||
.action_norm = SENSOR_ACT_NORM_PA_ON,
|
||||
.action_warn = 0,
|
||||
.action_crit = 0,
|
||||
.action_comb = 0,
|
||||
.state = STATE_NORMAL,
|
||||
}
|
||||
};
|
||||
|
||||
static struct osmo_timer_list sensor_timer;
|
||||
static void check_sensor_timer_cb(void *unused)
|
||||
{
|
||||
oc2gbts_check_temp(no_rom_write);
|
||||
oc2gbts_check_power(no_rom_write);
|
||||
oc2gbts_check_vswr(no_rom_write);
|
||||
osmo_timer_schedule(&sensor_timer, SENSOR_TIMER_SECS, 0);
|
||||
/* TODO checks if oc2gbts_check_temp/oc2gbts_check_power/oc2gbts_check_vswr went ok */
|
||||
oc2gbts_swd_event(&manager, SWD_CHECK_SENSOR);
|
||||
}
|
||||
|
||||
static struct osmo_timer_list hours_timer;
|
||||
static void hours_timer_cb(void *unused)
|
||||
{
|
||||
oc2gbts_update_hours(no_rom_write);
|
||||
|
||||
osmo_timer_schedule(&hours_timer, HOURS_TIMER_SECS, 0);
|
||||
/* TODO: validates if oc2gbts_update_hours went correctly */
|
||||
oc2gbts_swd_event(&manager, SWD_UPDATE_HOURS);
|
||||
}
|
||||
|
||||
static void print_help(void)
|
||||
{
|
||||
printf("oc2gbts-mgr [-nsD] [-d cat]\n");
|
||||
printf(" -n Do not write to ROM\n");
|
||||
printf(" -s Disable color\n");
|
||||
printf(" -d CAT enable debugging\n");
|
||||
printf(" -D daemonize\n");
|
||||
printf(" -c Specify the filename of the config file\n");
|
||||
}
|
||||
|
||||
static int parse_options(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt(argc, argv, "nhsd:c:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'n':
|
||||
no_rom_write = 1;
|
||||
break;
|
||||
case 'h':
|
||||
print_help();
|
||||
return -1;
|
||||
case 's':
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
break;
|
||||
case 'd':
|
||||
log_parse_category_mask(osmo_stderr_target, optarg);
|
||||
break;
|
||||
case 'D':
|
||||
daemonize = 1;
|
||||
break;
|
||||
case 'c':
|
||||
manager.config_file = optarg;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void signal_handler(int signal)
|
||||
{
|
||||
fprintf(stderr, "signal %u received\n", signal);
|
||||
|
||||
switch (signal) {
|
||||
case SIGINT:
|
||||
oc2gbts_check_temp(no_rom_write);
|
||||
oc2gbts_check_power(no_rom_write);
|
||||
oc2gbts_check_vswr(no_rom_write);
|
||||
oc2gbts_update_hours(no_rom_write);
|
||||
exit(0);
|
||||
break;
|
||||
case SIGABRT:
|
||||
case SIGUSR1:
|
||||
case SIGUSR2:
|
||||
talloc_report_full(tall_mgr_ctx, stderr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct log_info_cat mgr_log_info_cat[] = {
|
||||
[DTEMP] = {
|
||||
.name = "DTEMP",
|
||||
.description = "Temperature monitoring",
|
||||
.color = "\033[1;35m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DFW] = {
|
||||
.name = "DFW",
|
||||
.description = "Firmware management",
|
||||
.color = "\033[1;36m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DFIND] = {
|
||||
.name = "DFIND",
|
||||
.description = "ipaccess-find handling",
|
||||
.color = "\033[1;37m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DCALIB] = {
|
||||
.name = "DCALIB",
|
||||
.description = "Calibration handling",
|
||||
.color = "\033[1;37m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DSWD] = {
|
||||
.name = "DSWD",
|
||||
.description = "Software Watchdog",
|
||||
.color = "\033[1;37m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct log_info mgr_log_info = {
|
||||
.cat = mgr_log_info_cat,
|
||||
.num_cat = ARRAY_SIZE(mgr_log_info_cat),
|
||||
};
|
||||
|
||||
static int mgr_log_init(void)
|
||||
{
|
||||
osmo_init_logging(&mgr_log_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *tall_msgb_ctx;
|
||||
int rc;
|
||||
pthread_t tid;
|
||||
|
||||
tall_mgr_ctx = talloc_named_const(NULL, 1, "bts manager");
|
||||
tall_msgb_ctx = talloc_named_const(tall_mgr_ctx, 1, "msgb");
|
||||
msgb_set_talloc_ctx(tall_msgb_ctx);
|
||||
|
||||
mgr_log_init();
|
||||
|
||||
osmo_init_ignore_signals();
|
||||
signal(SIGINT, &signal_handler);
|
||||
signal(SIGUSR1, &signal_handler);
|
||||
signal(SIGUSR2, &signal_handler);
|
||||
|
||||
rc = parse_options(argc, argv);
|
||||
if (rc < 0)
|
||||
exit(2);
|
||||
|
||||
oc2gbts_mgr_vty_init();
|
||||
logging_vty_add_cmds(&mgr_log_info);
|
||||
rc = oc2gbts_mgr_parse_config(&manager);
|
||||
if (rc < 0) {
|
||||
LOGP(DFIND, LOGL_FATAL, "Cannot parse config file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rc = telnet_init(tall_mgr_ctx, NULL, OSMO_VTY_PORT_BTSMGR);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error initializing telnet\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
INIT_LLIST_HEAD(&manager.oc2gbts_leds.list);
|
||||
INIT_LLIST_HEAD(&manager.alarms.list);
|
||||
|
||||
/* Initialize the service watchdog notification for SWD_LAST event(s) */
|
||||
if (oc2gbts_swd_init(&manager, (int)(SWD_LAST)) != 0)
|
||||
exit(3);
|
||||
|
||||
/* start temperature check timer */
|
||||
sensor_timer.cb = check_sensor_timer_cb;
|
||||
check_sensor_timer_cb(NULL);
|
||||
|
||||
/* start operational hours timer */
|
||||
hours_timer.cb = hours_timer_cb;
|
||||
hours_timer_cb(NULL);
|
||||
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA)) {
|
||||
/* Enable the PAs */
|
||||
rc = oc2gbts_power_set(OC2GBTS_POWER_PA, 1);
|
||||
if (rc < 0) {
|
||||
exit(3);
|
||||
}
|
||||
}
|
||||
/* handle broadcast messages for ipaccess-find */
|
||||
if (oc2gbts_mgr_nl_init() != 0)
|
||||
exit(3);
|
||||
|
||||
/* Initialize the sensor control */
|
||||
oc2gbts_mgr_sensor_init(&manager);
|
||||
|
||||
if (oc2gbts_mgr_calib_init(&manager) != 0)
|
||||
exit(3);
|
||||
|
||||
if (oc2gbts_mgr_control_init(&manager))
|
||||
exit(3);
|
||||
|
||||
if (daemonize) {
|
||||
rc = osmo_daemonize();
|
||||
if (rc < 0) {
|
||||
perror("Error during daemonize");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
log_reset_context();
|
||||
osmo_select_main(0);
|
||||
oc2gbts_swd_event(&manager, SWD_MAINLOOP);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,398 @@
|
|||
#ifndef _OC2GBTS_MGR_H
|
||||
#define _OC2GBTS_MGR_H
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <gps.h>
|
||||
|
||||
#define OC2GBTS_SENSOR_TIMER_DURATION 60
|
||||
#define OC2GBTS_PREVENT_TIMER_DURATION 15 * 60
|
||||
#define OC2GBTS_PREVENT_TIMER_SHORT_DURATION 5 * 60
|
||||
#define OC2GBTS_PREVENT_TIMER_NONE 0
|
||||
#define OC2GBTS_PREVENT_RETRY INT_MAX - 1
|
||||
|
||||
enum BLINK_PATTERN {
|
||||
BLINK_PATTERN_POWER_ON = 0, //hardware set
|
||||
BLINK_PATTERN_INIT,
|
||||
BLINK_PATTERN_NORMAL,
|
||||
BLINK_PATTERN_EXT_LINK_MALFUNC,
|
||||
BLINK_PATTERN_INT_PROC_MALFUNC,
|
||||
BLINK_PATTERN_SUPPLY_VOLT_LOW,
|
||||
BLINK_PATTERN_SUPPLY_VOLT_MIN,
|
||||
BLINK_PATTERN_VSWR_HIGH,
|
||||
BLINK_PATTERN_VSWR_MAX,
|
||||
BLINK_PATTERN_TEMP_HIGH,
|
||||
BLINK_PATTERN_TEMP_MAX,
|
||||
BLINK_PATTERN_SUPPLY_PWR_HIGH,
|
||||
BLINK_PATTERN_SUPPLY_PWR_MAX,
|
||||
BLINK_PATTERN_PA_PWR_HIGH,
|
||||
BLINK_PATTERN_PA_PWR_MAX,
|
||||
BLINK_PATTERN_GPS_FIX_LOST,
|
||||
BLINK_PATTERN_MAX_ITEM
|
||||
};
|
||||
|
||||
#define BLINK_PATTERN_COMMAND {\
|
||||
"set red; sleep 5.0",\
|
||||
"set orange; sleep 5.0",\
|
||||
"set green; sleep 2.5; set off; sleep 2.5",\
|
||||
"set red; sleep 0.5; set off; sleep 0.5",\
|
||||
"set red; sleep 2.5; set off; sleep 2.5",\
|
||||
"set green; sleep 2.5; set off; sleep 0.5; set red; sleep 0.5; set off; sleep 0.5; set green; sleep 0.5; set off; sleep 0.5",\
|
||||
"set red; sleep 2.5; set off; sleep 0.5; set red; sleep 0.5; set off; sleep 0.5; set green; sleep 0.5; set off; sleep 0.5 ",\
|
||||
"set green; sleep 2.5; set off; sleep 0.5; set orange; sleep 0.5; set off; sleep 0.5; set orange; sleep 0.5; set off; sleep 0.5",\
|
||||
"set red; sleep 2.5; set off; sleep 0.5; set orange; sleep 0.5; set off; sleep 0.5; set orange; sleep 0.5; set off; sleep 0.5",\
|
||||
"set orange; sleep 2.5; set off; sleep 0.5; set red; sleep 0.5; set off; sleep 0.5; set red; sleep 0.5; set off; sleep 0.5 ",\
|
||||
"set red; sleep 2.5; set off; sleep 0.5; set red; sleep 0.5; set off; sleep 0.5; set red; sleep 0.5; set off; sleep 0.5",\
|
||||
"set green; sleep 2.5; set off; sleep 0.5; set orange; sleep 0.5; set off; sleep 0.5; set red; sleep 0.5; set off; sleep 0.5",\
|
||||
"set red; sleep 2.5; set off; sleep 0.5; set orange; sleep 0.5; set off; sleep 0.5; set red; sleep 0.5; set off; sleep 0.5",\
|
||||
"set green; sleep 2.5; set off; sleep 0.5; set red; sleep 0.5; set off; sleep 0.5; set orange; sleep 0.5; set off; sleep 0.5",\
|
||||
"set red; sleep 2.5; set off; sleep 0.5; set red; sleep 0.5; set off; sleep 0.5; set orange; sleep 0.5; set off; sleep 0.5",\
|
||||
"set green; sleep 2.5; set off; sleep 0.5; set green; sleep 0.5; set off; sleep 0.5; set orange; sleep 0.5; set off; sleep 0.5",\
|
||||
}
|
||||
|
||||
enum {
|
||||
DTEMP,
|
||||
DFW,
|
||||
DFIND,
|
||||
DCALIB,
|
||||
DSWD,
|
||||
};
|
||||
|
||||
// TODO NTQD: Define new actions like reducing output power, limit ARM core speed, shutdown second TRX/PA, ...
|
||||
enum {
|
||||
#if 0
|
||||
SENSOR_ACT_PWR_CONTRL = 0x1,
|
||||
#endif
|
||||
SENSOR_ACT_PA_OFF = 0x2,
|
||||
SENSOR_ACT_BTS_SRV_OFF = 0x10,
|
||||
};
|
||||
|
||||
/* actions only for normal state */
|
||||
enum {
|
||||
#if 0
|
||||
SENSOR_ACT_NORM_PW_CONTRL = 0x1,
|
||||
#endif
|
||||
SENSOR_ACT_NORM_PA_ON = 0x2,
|
||||
SENSOR_ACT_NORM_BTS_SRV_ON= 0x10,
|
||||
};
|
||||
|
||||
enum oc2gbts_sensor_state {
|
||||
STATE_NORMAL, /* Everything is fine */
|
||||
STATE_WARNING_HYST, /* Go back to normal next? */
|
||||
STATE_WARNING, /* We are above the warning threshold */
|
||||
STATE_CRITICAL, /* We have an issue. Wait for below warning */
|
||||
};
|
||||
|
||||
enum oc2gbts_leds_name {
|
||||
OC2GBTS_LED_RED = 0,
|
||||
OC2GBTS_LED_GREEN,
|
||||
OC2GBTS_LED_ORANGE,
|
||||
OC2GBTS_LED_OFF,
|
||||
_OC2GBTS_LED_MAX
|
||||
};
|
||||
|
||||
struct oc2gbts_led{
|
||||
char *name;
|
||||
char *fullname;
|
||||
char *path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Temperature Limits. We separate from a threshold
|
||||
* that will generate a warning and one that is so
|
||||
* severe that an action will be taken.
|
||||
*/
|
||||
struct oc2gbts_temp_limit {
|
||||
int thresh_warn_max;
|
||||
int thresh_crit_max;
|
||||
int thresh_warn_min;
|
||||
};
|
||||
|
||||
struct oc2gbts_volt_limit {
|
||||
int thresh_warn_max;
|
||||
int thresh_crit_max;
|
||||
int thresh_warn_min;
|
||||
int thresh_crit_min;
|
||||
};
|
||||
|
||||
struct oc2gbts_pwr_limit {
|
||||
int thresh_warn_max;
|
||||
int thresh_crit_max;
|
||||
};
|
||||
|
||||
struct oc2gbts_vswr_limit {
|
||||
int thresh_warn_max;
|
||||
int thresh_crit_max;
|
||||
};
|
||||
|
||||
struct oc2gbts_gps_fix_limit {
|
||||
int thresh_warn_max;
|
||||
};
|
||||
|
||||
struct oc2gbts_sleep_time {
|
||||
int sleep_sec;
|
||||
int sleep_usec;
|
||||
};
|
||||
|
||||
struct oc2gbts_led_timer {
|
||||
uint8_t idx;
|
||||
struct osmo_timer_list timer;
|
||||
struct oc2gbts_sleep_time param;
|
||||
};
|
||||
|
||||
struct oc2gbts_led_timer_list {
|
||||
struct llist_head list;
|
||||
struct oc2gbts_led_timer led_timer;
|
||||
};
|
||||
|
||||
struct oc2gbts_preventive_list {
|
||||
struct llist_head list;
|
||||
struct oc2gbts_sleep_time param;
|
||||
int action_flag;
|
||||
};
|
||||
|
||||
enum mgr_vty_node {
|
||||
MGR_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
|
||||
ACT_NORM_NODE,
|
||||
ACT_WARN_NODE,
|
||||
ACT_CRIT_NODE,
|
||||
LIMIT_SUPPLY_TEMP_NODE,
|
||||
LIMIT_SOC_NODE,
|
||||
LIMIT_FPGA_NODE,
|
||||
LIMIT_RMSDET_NODE,
|
||||
LIMIT_OCXO_NODE,
|
||||
LIMIT_TX_TEMP_NODE,
|
||||
LIMIT_PA_TEMP_NODE,
|
||||
LIMIT_SUPPLY_VOLT_NODE,
|
||||
LIMIT_VSWR_NODE,
|
||||
LIMIT_SUPPLY_PWR_NODE,
|
||||
LIMIT_PA_PWR_NODE,
|
||||
LIMIT_GPS_FIX_NODE,
|
||||
};
|
||||
|
||||
enum mgr_vty_limit_type {
|
||||
MGR_LIMIT_TYPE_TEMP = 0,
|
||||
MGR_LIMIT_TYPE_VOLT,
|
||||
MGR_LIMIT_TYPE_VSWR,
|
||||
MGR_LIMIT_TYPE_PWR,
|
||||
_MGR_LIMIT_TYPE_MAX,
|
||||
};
|
||||
|
||||
struct oc2gbts_mgr_instance {
|
||||
const char *config_file;
|
||||
|
||||
struct {
|
||||
struct oc2gbts_temp_limit supply_temp_limit;
|
||||
struct oc2gbts_temp_limit soc_temp_limit;
|
||||
struct oc2gbts_temp_limit fpga_temp_limit;
|
||||
struct oc2gbts_temp_limit rmsdet_temp_limit;
|
||||
struct oc2gbts_temp_limit ocxo_temp_limit;
|
||||
struct oc2gbts_temp_limit tx_temp_limit;
|
||||
struct oc2gbts_temp_limit pa_temp_limit;
|
||||
} temp;
|
||||
|
||||
struct {
|
||||
struct oc2gbts_volt_limit supply_volt_limit;
|
||||
} volt;
|
||||
|
||||
struct {
|
||||
struct oc2gbts_pwr_limit supply_pwr_limit;
|
||||
struct oc2gbts_pwr_limit pa_pwr_limit;
|
||||
} pwr;
|
||||
|
||||
struct {
|
||||
struct oc2gbts_vswr_limit vswr_limit;
|
||||
int last_vswr;
|
||||
} vswr;
|
||||
|
||||
struct {
|
||||
struct oc2gbts_gps_fix_limit gps_fix_limit;
|
||||
int last_update;
|
||||
time_t last_gps_fix;
|
||||
time_t gps_fix_now;
|
||||
int gps_open;
|
||||
struct osmo_fd gpsfd;
|
||||
struct gps_data_t gpsdata;
|
||||
struct osmo_timer_list fix_timeout;
|
||||
} gps;
|
||||
|
||||
struct {
|
||||
int action_norm;
|
||||
int action_warn;
|
||||
int action_crit;
|
||||
int action_comb;
|
||||
|
||||
enum oc2gbts_sensor_state state;
|
||||
} state;
|
||||
|
||||
struct {
|
||||
int state;
|
||||
int calib_from_loop;
|
||||
struct osmo_timer_list calib_timeout;
|
||||
} calib;
|
||||
|
||||
struct {
|
||||
int state;
|
||||
int swd_from_loop;
|
||||
unsigned long long int swd_events;
|
||||
unsigned long long int swd_events_cache;
|
||||
unsigned long long int swd_eventmasks;
|
||||
int num_events;
|
||||
struct osmo_timer_list swd_timeout;
|
||||
} swd;
|
||||
|
||||
struct {
|
||||
uint8_t led_idx;
|
||||
uint8_t last_pattern_id;
|
||||
uint8_t active_timer;
|
||||
struct llist_head list;
|
||||
} oc2gbts_leds;
|
||||
|
||||
struct {
|
||||
int is_up;
|
||||
uint32_t last_seqno;
|
||||
struct osmo_timer_list recon_timer;
|
||||
struct ipa_client_conn *bts_conn;
|
||||
uint32_t crit_flags;
|
||||
uint32_t warn_flags;
|
||||
} oc2gbts_ctrl;
|
||||
|
||||
struct oc2gbts_alarms {
|
||||
int temp_high;
|
||||
int temp_max;
|
||||
int supply_low;
|
||||
int supply_min;
|
||||
int vswr_high;
|
||||
int vswr_max;
|
||||
int supply_pwr_high;
|
||||
int supply_pwr_max;
|
||||
int pa_pwr_high;
|
||||
int pa_pwr_max;
|
||||
int gps_fix_lost;
|
||||
struct llist_head list;
|
||||
struct osmo_timer_list preventive_timer;
|
||||
int preventive_duration;
|
||||
int preventive_retry;
|
||||
} alarms;
|
||||
|
||||
};
|
||||
|
||||
enum oc2gbts_mgr_fail_evt_rep_crit_sig {
|
||||
/* Critical alarms */
|
||||
S_MGR_TEMP_SUPPLY_CRIT_MAX_ALARM = (1 << 0),
|
||||
S_MGR_TEMP_SOC_CRIT_MAX_ALARM = (1 << 1),
|
||||
S_MGR_TEMP_FPGA_CRIT_MAX_ALARM = (1 << 2),
|
||||
S_MGR_TEMP_RMS_DET_CRIT_MAX_ALARM = (1 << 3),
|
||||
S_MGR_TEMP_OCXO_CRIT_MAX_ALARM = (1 << 4),
|
||||
S_MGR_TEMP_TRX_CRIT_MAX_ALARM = (1 << 5),
|
||||
S_MGR_TEMP_PA_CRIT_MAX_ALARM = (1 << 6),
|
||||
S_MGR_SUPPLY_CRIT_MAX_ALARM = (1 << 7),
|
||||
S_MGR_SUPPLY_CRIT_MIN_ALARM = (1 << 8),
|
||||
S_MGR_VSWR_CRIT_MAX_ALARM = (1 << 9),
|
||||
S_MGR_PWR_SUPPLY_CRIT_MAX_ALARM = (1 << 10),
|
||||
S_MGR_PWR_PA_CRIT_MAX_ALARM = (1 << 11),
|
||||
_S_MGR_CRIT_ALARM_MAX,
|
||||
};
|
||||
|
||||
enum oc2gbts_mgr_fail_evt_rep_warn_sig {
|
||||
/* Warning alarms */
|
||||
S_MGR_TEMP_SUPPLY_WARN_MIN_ALARM = (1 << 0),
|
||||
S_MGR_TEMP_SUPPLY_WARN_MAX_ALARM = (1 << 1),
|
||||
S_MGR_TEMP_SOC_WARN_MIN_ALARM = (1 << 2),
|
||||
S_MGR_TEMP_SOC_WARN_MAX_ALARM = (1 << 3),
|
||||
S_MGR_TEMP_FPGA_WARN_MIN_ALARM = (1 << 4),
|
||||
S_MGR_TEMP_FPGA_WARN_MAX_ALARM = (1 << 5),
|
||||
S_MGR_TEMP_RMS_DET_WARN_MIN_ALARM = (1 << 6),
|
||||
S_MGR_TEMP_RMS_DET_WARN_MAX_ALARM = (1 << 7),
|
||||
S_MGR_TEMP_OCXO_WARN_MIN_ALARM = (1 << 8),
|
||||
S_MGR_TEMP_OCXO_WARN_MAX_ALARM = (1 << 9),
|
||||
S_MGR_TEMP_TRX_WARN_MIN_ALARM = (1 << 10),
|
||||
S_MGR_TEMP_TRX_WARN_MAX_ALARM = (1 << 11),
|
||||
S_MGR_TEMP_PA_WARN_MIN_ALARM = (1 << 12),
|
||||
S_MGR_TEMP_PA_WARN_MAX_ALARM = (1 << 13),
|
||||
S_MGR_SUPPLY_WARN_MIN_ALARM = (1 << 14),
|
||||
S_MGR_SUPPLY_WARN_MAX_ALARM = (1 << 15),
|
||||
S_MGR_VSWR_WARN_MAX_ALARM = (1 << 16),
|
||||
S_MGR_PWR_SUPPLY_WARN_MAX_ALARM = (1 << 17),
|
||||
S_MGR_PWR_PA_WARN_MAX_ALARM = (1 << 18),
|
||||
S_MGR_GPS_FIX_WARN_ALARM = (1 << 19),
|
||||
_S_MGR_WARN_ALARM_MAX,
|
||||
};
|
||||
|
||||
enum oc2gbts_mgr_failure_event_causes {
|
||||
/* Critical causes */
|
||||
NM_EVT_CAUSE_CRIT_TEMP_SUPPLY_MAX_FAIL = 0x4100,
|
||||
NM_EVT_CAUSE_CRIT_TEMP_FPGA_MAX_FAIL = 0x4101,
|
||||
NM_EVT_CAUSE_CRIT_TEMP_SOC_MAX_FAIL = 0x4102,
|
||||
NM_EVT_CAUSE_CRIT_TEMP_RMS_DET_MAX_FAIL = 0x4103,
|
||||
NM_EVT_CAUSE_CRIT_TEMP_OCXO_MAX_FAIL = 0x4104,
|
||||
NM_EVT_CAUSE_CRIT_TEMP_TRX_MAX_FAIL = 0x4105,
|
||||
NM_EVT_CAUSE_CRIT_TEMP_PA_MAX_FAIL = 0x4106,
|
||||
NM_EVT_CAUSE_CRIT_SUPPLY_MAX_FAIL = 0x4107,
|
||||
NM_EVT_CAUSE_CRIT_SUPPLY_MIN_FAIL = 0x4108,
|
||||
NM_EVT_CAUSE_CRIT_VSWR_MAX_FAIL = 0x4109,
|
||||
NM_EVT_CAUSE_CRIT_PWR_SUPPLY_MAX_FAIL = 0x410A,
|
||||
NM_EVT_CAUSE_CRIT_PWR_PA_MAX_FAIL = 0x410B,
|
||||
/* Warning causes */
|
||||
NM_EVT_CAUSE_WARN_TEMP_SUPPLY_LOW_FAIL = 0x4400,
|
||||
NM_EVT_CAUSE_WARN_TEMP_SUPPLY_HIGH_FAIL = 0x4401,
|
||||
NM_EVT_CAUSE_WARN_TEMP_FPGA_LOW_FAIL = 0x4402,
|
||||
NM_EVT_CAUSE_WARN_TEMP_FPGA_HIGH_FAIL = 0x4403,
|
||||
NM_EVT_CAUSE_WARN_TEMP_SOC_LOW_FAIL = 0x4404,
|
||||
NM_EVT_CAUSE_WARN_TEMP_SOC_HIGH_FAIL = 0x4405,
|
||||
NM_EVT_CAUSE_WARN_TEMP_RMS_DET_LOW_FAIL = 0x4406,
|
||||
NM_EVT_CAUSE_WARN_TEMP_RMS_DET_HIGH_FAIL= 0x4407,
|
||||
NM_EVT_CAUSE_WARN_TEMP_OCXO_LOW_FAIL = 0x4408,
|
||||
NM_EVT_CAUSE_WARN_TEMP_OCXO_HIGH_FAIL = 0x4409,
|
||||
NM_EVT_CAUSE_WARN_TEMP_TRX_LOW_FAIL = 0x440A,
|
||||
NM_EVT_CAUSE_WARN_TEMP_TRX_HIGH_FAIL = 0x440B,
|
||||
NM_EVT_CAUSE_WARN_TEMP_PA_LOW_FAIL = 0x440C,
|
||||
NM_EVT_CAUSE_WARN_TEMP_PA_HIGH_FAIL = 0x440D,
|
||||
NM_EVT_CAUSE_WARN_SUPPLY_LOW_FAIL = 0x440E,
|
||||
NM_EVT_CAUSE_WARN_SUPPLY_HIGH_FAIL = 0x440F,
|
||||
NM_EVT_CAUSE_WARN_VSWR_HIGH_FAIL = 0x4410,
|
||||
NM_EVT_CAUSE_WARN_PWR_SUPPLY_HIGH_FAIL = 0x4411,
|
||||
NM_EVT_CAUSE_WARN_PWR_PA_HIGH_FAIL = 0x4412,
|
||||
NM_EVT_CAUSE_WARN_GPS_FIX_FAIL = 0x4413,
|
||||
};
|
||||
|
||||
/* This defines the list of notification events for systemd service watchdog.
|
||||
all these events must be notified in a certain service defined timeslot
|
||||
or the service (this app) would be restarted (only if related systemd service
|
||||
unit file has WatchdogSec!=0).
|
||||
WARNING: swd events must begin with event 0. Last events must be
|
||||
SWD_LAST (max 64 events in this list).
|
||||
*/
|
||||
enum mgr_swd_events {
|
||||
SWD_MAINLOOP = 0,
|
||||
SWD_CHECK_SENSOR,
|
||||
SWD_UPDATE_HOURS,
|
||||
SWD_CHECK_TEMP_SENSOR,
|
||||
SWD_CHECK_LED_CTRL,
|
||||
SWD_CHECK_CALIB,
|
||||
SWD_CHECK_BTS_CONNECTION,
|
||||
SWD_LAST
|
||||
};
|
||||
|
||||
int oc2gbts_mgr_vty_init(void);
|
||||
int oc2gbts_mgr_parse_config(struct oc2gbts_mgr_instance *mgr);
|
||||
int oc2gbts_mgr_nl_init(void);
|
||||
int oc2gbts_mgr_sensor_init(struct oc2gbts_mgr_instance *mgr);
|
||||
const char *oc2gbts_mgr_sensor_get_state(enum oc2gbts_sensor_state state);
|
||||
|
||||
int oc2gbts_mgr_calib_init(struct oc2gbts_mgr_instance *mgr);
|
||||
int oc2gbts_mgr_control_init(struct oc2gbts_mgr_instance *mgr);
|
||||
int oc2gbts_mgr_calib_run(struct oc2gbts_mgr_instance *mgr);
|
||||
void oc2gbts_mgr_dispatch_alarm(struct oc2gbts_mgr_instance *mgr, const int cause, const char *key, const char *text);
|
||||
void handle_alert_actions(struct oc2gbts_mgr_instance *mgr);
|
||||
void handle_ceased_actions(struct oc2gbts_mgr_instance *mgr);
|
||||
void handle_warn_actions(struct oc2gbts_mgr_instance *mgr);
|
||||
extern void *tall_mgr_ctx;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,750 @@
|
|||
/* OCXO calibration control for OC-2G BTS management daemon */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* sysmobts_mgr_calib.c
|
||||
* (C) 2014,2015 by Holger Hans Peter Freyther
|
||||
* (C) 2014 by Harald Welte for the IPA code from the oml router
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "misc/oc2gbts_mgr.h"
|
||||
#include "misc/oc2gbts_misc.h"
|
||||
#include "misc/oc2gbts_clock.h"
|
||||
#include "misc/oc2gbts_swd.h"
|
||||
#include "misc/oc2gbts_par.h"
|
||||
#include "misc/oc2gbts_led.h"
|
||||
#include "osmo-bts/msg_utils.h"
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/select.h>
|
||||
|
||||
#include <osmocom/ctrl/control_cmd.h>
|
||||
#include <osmocom/ctrl/ports.h>
|
||||
|
||||
#include <osmocom/gsm/ipa.h>
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
|
||||
#include <osmocom/abis/abis.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/abis/ipa.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <errno.h>
|
||||
|
||||
static void calib_adjust(struct oc2gbts_mgr_instance *mgr);
|
||||
static void calib_state_reset(struct oc2gbts_mgr_instance *mgr, int reason);
|
||||
static void calib_loop_run(void *_data);
|
||||
|
||||
static int ocxodac_saved_value = -1;
|
||||
|
||||
enum calib_state {
|
||||
CALIB_INITIAL,
|
||||
CALIB_IN_PROGRESS,
|
||||
CALIB_GPS_WAIT_FOR_FIX,
|
||||
};
|
||||
|
||||
enum calib_result {
|
||||
CALIB_FAIL_START,
|
||||
CALIB_FAIL_GPSFIX,
|
||||
CALIB_FAIL_CLKERR,
|
||||
CALIB_FAIL_OCXODAC,
|
||||
CALIB_SUCCESS,
|
||||
};
|
||||
|
||||
static int oc2gbts_par_get_uptime(void *ctx, int *ret)
|
||||
{
|
||||
char *fpath;
|
||||
FILE *fp;
|
||||
int rc;
|
||||
|
||||
fpath = talloc_asprintf(ctx, "%s", UPTIME_TMP_PATH);
|
||||
if (!fpath)
|
||||
return NULL;
|
||||
|
||||
fp = fopen(fpath, "r");
|
||||
if (!fp)
|
||||
fprintf(stderr, "Failed to open %s due to '%s' error\n", fpath, strerror(errno));
|
||||
|
||||
talloc_free(fpath);
|
||||
|
||||
if (fp == NULL) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
rc = fscanf(fp, "%d", ret);
|
||||
if (rc != 1) {
|
||||
fclose(fp);
|
||||
return -EIO;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oc2gbts_par_set_uptime(void *ctx, int val)
|
||||
{
|
||||
char *fpath;
|
||||
FILE *fp;
|
||||
int rc;
|
||||
|
||||
fpath = talloc_asprintf(ctx, "%s", UPTIME_TMP_PATH);
|
||||
if (!fpath)
|
||||
return NULL;
|
||||
|
||||
fp = fopen(fpath, "w");
|
||||
if (!fp)
|
||||
fprintf(stderr, "Failed to open %s due to '%s' error\n", fpath, strerror(errno));
|
||||
|
||||
talloc_free(fpath);
|
||||
|
||||
if (fp == NULL) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
rc = fprintf(fp, "%d", val);
|
||||
if (rc < 0) {
|
||||
fclose(fp);
|
||||
return -EIO;
|
||||
}
|
||||
fsync(fp);
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mgr_gps_close(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
if (!mgr->gps.gps_open)
|
||||
return;
|
||||
|
||||
osmo_timer_del(&mgr->gps.fix_timeout);
|
||||
|
||||
osmo_fd_unregister(&mgr->gps.gpsfd);
|
||||
gps_close(&mgr->gps.gpsdata);
|
||||
memset(&mgr->gps.gpsdata, 0, sizeof(mgr->gps.gpsdata));
|
||||
mgr->gps.gps_open = 0;
|
||||
}
|
||||
|
||||
static void mgr_gps_checkfix(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
struct gps_data_t *data = &mgr->gps.gpsdata;
|
||||
|
||||
/* No 3D fix yet */
|
||||
if (data->fix.mode < MODE_3D) {
|
||||
LOGP(DCALIB, LOGL_DEBUG, "Fix mode not enough: %d\n",
|
||||
data->fix.mode);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Satellite used checking */
|
||||
if (data->satellites_used < 1) {
|
||||
LOGP(DCALIB, LOGL_DEBUG, "Not enough satellites used: %d\n",
|
||||
data->satellites_used);
|
||||
return;
|
||||
}
|
||||
|
||||
mgr->gps.gps_fix_now = (time_t) data->fix.time;
|
||||
LOGP(DCALIB, LOGL_INFO, "Got a GPS fix, satellites used: %d, timestamp: %ld\n",
|
||||
data->satellites_used, mgr->gps.gps_fix_now);
|
||||
osmo_timer_del(&mgr->gps.fix_timeout);
|
||||
mgr_gps_close(mgr);
|
||||
}
|
||||
|
||||
static int mgr_gps_read(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
int rc;
|
||||
struct oc2gbts_mgr_instance *mgr = fd->data;
|
||||
|
||||
rc = gps_read(&mgr->gps.gpsdata);
|
||||
if (rc == -1) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "gpsd vanished during read.\n");
|
||||
calib_state_reset(mgr, CALIB_FAIL_GPSFIX);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc > 0)
|
||||
mgr_gps_checkfix(mgr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mgr_gps_fix_timeout(void *_data)
|
||||
{
|
||||
struct oc2gbts_mgr_instance *mgr = _data;
|
||||
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to acquire GPS fix.\n");
|
||||
mgr_gps_close(mgr);
|
||||
}
|
||||
|
||||
static void mgr_gps_open(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (mgr->gps.gps_open)
|
||||
return;
|
||||
|
||||
rc = gps_open("localhost", DEFAULT_GPSD_PORT, &mgr->gps.gpsdata);
|
||||
if (rc != 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to connect to GPS %d\n", rc);
|
||||
calib_state_reset(mgr, CALIB_FAIL_GPSFIX);
|
||||
return;
|
||||
}
|
||||
|
||||
mgr->gps.gps_open = 1;
|
||||
gps_stream(&mgr->gps.gpsdata, WATCH_ENABLE, NULL);
|
||||
|
||||
mgr->gps.gpsfd.data = mgr;
|
||||
mgr->gps.gpsfd.cb = mgr_gps_read;
|
||||
mgr->gps.gpsfd.when = BSC_FD_READ | BSC_FD_EXCEPT;
|
||||
mgr->gps.gpsfd.fd = mgr->gps.gpsdata.gps_fd;
|
||||
if (osmo_fd_register(&mgr->gps.gpsfd) < 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to register GPSD fd\n");
|
||||
calib_state_reset(mgr, CALIB_FAIL_GPSFIX);
|
||||
}
|
||||
|
||||
mgr->calib.state = CALIB_GPS_WAIT_FOR_FIX;
|
||||
mgr->gps.fix_timeout.data = mgr;
|
||||
mgr->gps.fix_timeout.cb = mgr_gps_fix_timeout;
|
||||
osmo_timer_schedule(&mgr->gps.fix_timeout, 60, 0);
|
||||
LOGP(DCALIB, LOGL_INFO, "Opened the GPSD connection waiting for fix: %d\n",
|
||||
mgr->gps.gpsfd.fd);
|
||||
}
|
||||
|
||||
/* OC2G CTRL interface related functions */
|
||||
static void send_ctrl_cmd(struct oc2gbts_mgr_instance *mgr, struct msgb *msg)
|
||||
{
|
||||
ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
|
||||
ipa_prepend_header(msg, IPAC_PROTO_OSMO);
|
||||
ipa_client_conn_send(mgr->oc2gbts_ctrl.bts_conn, msg);
|
||||
}
|
||||
|
||||
static void send_set_ctrl_cmd_int(struct oc2gbts_mgr_instance *mgr, const char *key, const int val)
|
||||
{
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
msg = msgb_alloc_headroom(1024, 128, "CTRL SET");
|
||||
ret = snprintf((char *) msg->data, 4096, "SET %u %s %d",
|
||||
mgr->oc2gbts_ctrl.last_seqno++, key, val);
|
||||
msg->l2h = msgb_put(msg, ret);
|
||||
return send_ctrl_cmd(mgr, msg);
|
||||
}
|
||||
|
||||
static void send_set_ctrl_cmd(struct oc2gbts_mgr_instance *mgr, const char *key, const int val, const char *text)
|
||||
{
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
msg = msgb_alloc_headroom(1024, 128, "CTRL SET");
|
||||
ret = snprintf((char *) msg->data, 4096, "SET %u %s %d, %s",
|
||||
mgr->oc2gbts_ctrl.last_seqno++, key, val, text);
|
||||
msg->l2h = msgb_put(msg, ret);
|
||||
return send_ctrl_cmd(mgr, msg);
|
||||
}
|
||||
|
||||
static void calib_start(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = oc2gbts_clock_err_open();
|
||||
if (rc != 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to open clock error module %d\n", rc);
|
||||
calib_state_reset(mgr, CALIB_FAIL_CLKERR);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = oc2gbts_clock_dac_open();
|
||||
if (rc != 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to open OCXO dac module %d\n", rc);
|
||||
calib_state_reset(mgr, CALIB_FAIL_OCXODAC);
|
||||
return;
|
||||
}
|
||||
|
||||
calib_adjust(mgr);
|
||||
}
|
||||
static int get_uptime(int *uptime)
|
||||
{
|
||||
struct sysinfo s_info;
|
||||
int rc;
|
||||
rc = sysinfo(&s_info);
|
||||
if(!rc)
|
||||
*uptime = s_info.uptime /(60 * 60);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void calib_adjust(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
int rc;
|
||||
int fault;
|
||||
int error_ppt;
|
||||
int accuracy_ppq;
|
||||
int interval_sec;
|
||||
int dac_value;
|
||||
int new_dac_value;
|
||||
int dac_correction;
|
||||
int now = 0;
|
||||
|
||||
/* Get GPS time via GPSD */
|
||||
mgr_gps_open(mgr);
|
||||
|
||||
rc = oc2gbts_clock_err_get(&fault, &error_ppt,
|
||||
&accuracy_ppq, &interval_sec);
|
||||
if (rc < 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Failed to get clock error measurement %d\n", rc);
|
||||
calib_state_reset(mgr, CALIB_FAIL_CLKERR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* get current up time */
|
||||
rc = get_uptime(&now);
|
||||
if (rc < 0)
|
||||
LOGP(DTEMP, LOGL_ERROR, "Unable to read up time hours: %d (%s)\n", rc, strerror(errno));
|
||||
|
||||
/* read last up time */
|
||||
rc = oc2gbts_par_get_uptime(tall_mgr_ctx, &mgr->gps.last_update);
|
||||
if (rc < 0)
|
||||
LOGP(DCALIB, LOGL_NOTICE, "Last GPS 3D fix can not read (%d). Last GPS 3D fix sets to zero\n", rc);
|
||||
|
||||
if (fault) {
|
||||
LOGP(DCALIB, LOGL_NOTICE, "GPS has no fix, warn_flags=0x%08x, last=%d, now=%d\n",
|
||||
mgr->oc2gbts_ctrl.warn_flags, mgr->gps.last_update, now);
|
||||
if (now >= mgr->gps.last_update + mgr->gps.gps_fix_limit.thresh_warn_max * 24) {
|
||||
if (!(mgr->oc2gbts_ctrl.warn_flags & S_MGR_GPS_FIX_WARN_ALARM)) {
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_GPS_FIX_WARN_ALARM;
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_GPS_FIX_FAIL, "oc2g-oml-alert", "GPS 3D fix has been lost");
|
||||
|
||||
LOGP(DCALIB, LOGL_NOTICE, "GPS has no fix since the last verification, warn_flags=0x%08x, last=%d, now=%d\n",
|
||||
mgr->oc2gbts_ctrl.warn_flags, mgr->gps.last_update, now);
|
||||
|
||||
/* schedule LED pattern for GPS fix lost */
|
||||
mgr->alarms.gps_fix_lost = 1;
|
||||
/* update LED pattern */
|
||||
select_led_pattern(mgr);
|
||||
}
|
||||
} else {
|
||||
/* read from last GPS 3D fix timestamp */
|
||||
rc = oc2gbts_par_get_gps_fix(tall_mgr_ctx, &mgr->gps.last_gps_fix);
|
||||
if (rc < 0)
|
||||
LOGP(DCALIB, LOGL_NOTICE, "Last GPS 3D fix timestamp can not read (%d)\n", rc);
|
||||
|
||||
if (difftime(mgr->gps.gps_fix_now, mgr->gps.last_gps_fix) > mgr->gps.gps_fix_limit.thresh_warn_max * 24 * 60 * 60) {
|
||||
if (!(mgr->oc2gbts_ctrl.warn_flags & S_MGR_GPS_FIX_WARN_ALARM)) {
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_GPS_FIX_WARN_ALARM;
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_GPS_FIX_FAIL, "oc2g-oml-alert", "GPS 3D fix has been lost");
|
||||
|
||||
LOGP(DCALIB, LOGL_NOTICE, "GPS has no fix since the last known GPS fix, warn_flags=0x%08x, gps_last=%ld, gps_now=%ld\n",
|
||||
mgr->oc2gbts_ctrl.warn_flags, mgr->gps.last_gps_fix, mgr->gps.gps_fix_now);
|
||||
|
||||
/* schedule LED pattern for GPS fix lost */
|
||||
mgr->alarms.gps_fix_lost = 1;
|
||||
/* update LED pattern */
|
||||
select_led_pattern(mgr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc = oc2gbts_clock_err_reset();
|
||||
if (rc < 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Failed to reset clock error module %d\n", rc);
|
||||
calib_state_reset(mgr, CALIB_FAIL_CLKERR);
|
||||
return;
|
||||
}
|
||||
|
||||
calib_state_reset(mgr, CALIB_FAIL_GPSFIX);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!interval_sec) {
|
||||
LOGP(DCALIB, LOGL_INFO, "Skipping this iteration, no integration time\n");
|
||||
calib_state_reset(mgr, CALIB_SUCCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We got GPS 3D fix */
|
||||
LOGP(DCALIB, LOGL_DEBUG, "Got GPS 3D fix warn_flags=0x%08x, uptime_last=%d, uptime_now=%d, gps_last=%ld, gps_now=%ld\n",
|
||||
mgr->oc2gbts_ctrl.warn_flags, mgr->gps.last_update, now, mgr->gps.last_gps_fix, mgr->gps.gps_fix_now);
|
||||
|
||||
if (mgr->oc2gbts_ctrl.warn_flags & S_MGR_GPS_FIX_WARN_ALARM) {
|
||||
/* Store GPS fix as soon as we send ceased alarm */
|
||||
LOGP(DCALIB, LOGL_NOTICE, "Store GPS fix as soon as we send ceased alarm last=%ld, now=%ld\n",
|
||||
mgr->gps.last_gps_fix , mgr->gps.gps_fix_now);
|
||||
rc = oc2gbts_par_set_gps_fix(tall_mgr_ctx, mgr->gps.gps_fix_now);
|
||||
if (rc < 0)
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to store GPS 3D fix to storage %d\n", rc);
|
||||
|
||||
/* Store last up time */
|
||||
rc = oc2gbts_par_set_uptime(tall_mgr_ctx, now);
|
||||
if (rc < 0)
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to store uptime to storage %d\n", rc);
|
||||
|
||||
mgr->gps.last_update = now;
|
||||
|
||||
/* schedule LED pattern for GPS fix resume */
|
||||
mgr->alarms.gps_fix_lost = 0;
|
||||
/* update LED pattern */
|
||||
select_led_pattern(mgr);
|
||||
/* send ceased alarm if possible */
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_GPS_FIX_FAIL, "oc2g-oml-ceased", "GPS 3D fix has been lost");
|
||||
mgr->oc2gbts_ctrl.warn_flags &= ~S_MGR_GPS_FIX_WARN_ALARM;
|
||||
|
||||
}
|
||||
/* Store GPS fix at every hour */
|
||||
if (now > mgr->gps.last_update) {
|
||||
/* Store GPS fix every 60 minutes */
|
||||
LOGP(DCALIB, LOGL_INFO, "Store GPS fix every hour last=%ld, now=%ld\n",
|
||||
mgr->gps.last_gps_fix , mgr->gps.gps_fix_now);
|
||||
rc = oc2gbts_par_set_gps_fix(tall_mgr_ctx, mgr->gps.gps_fix_now);
|
||||
if (rc < 0)
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to store GPS 3D fix to storage %d\n", rc);
|
||||
|
||||
/* Store last up time every 60 minutes */
|
||||
rc = oc2gbts_par_set_uptime(tall_mgr_ctx, now);
|
||||
if (rc < 0)
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to store uptime to storage %d\n", rc);
|
||||
|
||||
/* update last uptime */
|
||||
mgr->gps.last_update = now;
|
||||
}
|
||||
|
||||
rc = oc2gbts_clock_dac_get(&dac_value);
|
||||
if (rc < 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Failed to get OCXO dac value %d\n", rc);
|
||||
calib_state_reset(mgr, CALIB_FAIL_OCXODAC);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set OCXO initial dac value */
|
||||
if (ocxodac_saved_value < 0)
|
||||
ocxodac_saved_value = dac_value;
|
||||
|
||||
LOGP(DCALIB, LOGL_INFO,
|
||||
"Calibration ERR(%f PPB) ACC(%f PPB) INT(%d) DAC(%d)\n",
|
||||
error_ppt / 1000., accuracy_ppq / 1000000., interval_sec, dac_value);
|
||||
|
||||
/* 1 unit of correction equal about 0.5 - 1 PPB correction */
|
||||
dac_correction = (int)(-error_ppt * 0.0015);
|
||||
new_dac_value = dac_value + dac_correction;
|
||||
|
||||
if (new_dac_value > 4095)
|
||||
new_dac_value = 4095;
|
||||
else if (new_dac_value < 0)
|
||||
new_dac_value = 0;
|
||||
|
||||
/* We have a fix, make sure the measured error is
|
||||
meaningful (10 times the accuracy) */
|
||||
if ((new_dac_value != dac_value) && ((100l * abs(error_ppt)) > accuracy_ppq)) {
|
||||
|
||||
LOGP(DCALIB, LOGL_INFO,
|
||||
"Going to apply %d as new clock setting.\n",
|
||||
new_dac_value);
|
||||
|
||||
rc = oc2gbts_clock_dac_set(new_dac_value);
|
||||
if (rc < 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Failed to set OCXO dac value %d\n", rc);
|
||||
calib_state_reset(mgr, CALIB_FAIL_OCXODAC);
|
||||
return;
|
||||
}
|
||||
rc = oc2gbts_clock_err_reset();
|
||||
if (rc < 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Failed to reset clock error module %d\n", rc);
|
||||
calib_state_reset(mgr, CALIB_FAIL_CLKERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* New conditions to store DAC value:
|
||||
* - Resolution accuracy less or equal than 0.01PPB (or 10000 PPQ)
|
||||
* - Error less or equal than 2PPB (or 2000PPT)
|
||||
* - Solution different than the last one */
|
||||
else if (accuracy_ppq <= 10000) {
|
||||
if((dac_value != ocxodac_saved_value) && (abs(error_ppt) < 2000)) {
|
||||
LOGP(DCALIB, LOGL_INFO, "Saving OCXO DAC value to memory... val = %d\n", dac_value);
|
||||
rc = oc2gbts_clock_dac_save();
|
||||
if (rc < 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Failed to save OCXO dac value %d\n", rc);
|
||||
calib_state_reset(mgr, CALIB_FAIL_OCXODAC);
|
||||
} else {
|
||||
ocxodac_saved_value = dac_value;
|
||||
}
|
||||
}
|
||||
|
||||
rc = oc2gbts_clock_err_reset();
|
||||
if (rc < 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Failed to reset clock error module %d\n", rc);
|
||||
calib_state_reset(mgr, CALIB_FAIL_CLKERR);
|
||||
}
|
||||
}
|
||||
|
||||
calib_state_reset(mgr, CALIB_SUCCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
static void calib_close(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
oc2gbts_clock_err_close();
|
||||
oc2gbts_clock_dac_close();
|
||||
}
|
||||
|
||||
static void calib_state_reset(struct oc2gbts_mgr_instance *mgr, int outcome)
|
||||
{
|
||||
if (mgr->calib.calib_from_loop) {
|
||||
/*
|
||||
* In case of success calibrate in two hours again
|
||||
* and in case of a failure in some minutes.
|
||||
*
|
||||
* TODO NTQ: Select timeout based on last error and accuracy
|
||||
*/
|
||||
int timeout = 60;
|
||||
//int timeout = 2 * 60 * 60;
|
||||
//if (outcome != CALIB_SUCESS) }
|
||||
// timeout = 5 * 60;
|
||||
//}
|
||||
|
||||
mgr->calib.calib_timeout.data = mgr;
|
||||
mgr->calib.calib_timeout.cb = calib_loop_run;
|
||||
osmo_timer_schedule(&mgr->calib.calib_timeout, timeout, 0);
|
||||
/* TODO: do we want to notify if we got a calibration error, like no gps fix? */
|
||||
oc2gbts_swd_event(mgr, SWD_CHECK_CALIB);
|
||||
}
|
||||
|
||||
mgr->calib.state = CALIB_INITIAL;
|
||||
calib_close(mgr);
|
||||
}
|
||||
|
||||
static int calib_run(struct oc2gbts_mgr_instance *mgr, int from_loop)
|
||||
{
|
||||
if (mgr->calib.state != CALIB_INITIAL) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "Calib is already in progress.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Validates if we have a bts connection */
|
||||
if (mgr->oc2gbts_ctrl.is_up) {
|
||||
LOGP(DCALIB, LOGL_DEBUG, "Bts connection is up.\n");
|
||||
oc2gbts_swd_event(mgr, SWD_CHECK_BTS_CONNECTION);
|
||||
}
|
||||
|
||||
mgr->calib.calib_from_loop = from_loop;
|
||||
|
||||
/* From now on everything will be handled from the failure */
|
||||
mgr->calib.state = CALIB_IN_PROGRESS;
|
||||
calib_start(mgr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void calib_loop_run(void *_data)
|
||||
{
|
||||
int rc;
|
||||
struct oc2gbts_mgr_instance *mgr = _data;
|
||||
|
||||
LOGP(DCALIB, LOGL_INFO, "Going to calibrate the system.\n");
|
||||
rc = calib_run(mgr, 1);
|
||||
if (rc != 0) {
|
||||
calib_state_reset(mgr, CALIB_FAIL_START);
|
||||
}
|
||||
}
|
||||
|
||||
int oc2gbts_mgr_calib_run(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
return calib_run(mgr, 0);
|
||||
}
|
||||
|
||||
static void schedule_bts_connect(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
DEBUGP(DLCTRL, "Scheduling BTS connect\n");
|
||||
osmo_timer_schedule(&mgr->oc2gbts_ctrl.recon_timer, 1, 0);
|
||||
}
|
||||
|
||||
/* link to BSC has gone up or down */
|
||||
static void bts_updown_cb(struct ipa_client_conn *link, int up)
|
||||
{
|
||||
struct oc2gbts_mgr_instance *mgr = link->data;
|
||||
|
||||
LOGP(DLCTRL, LOGL_INFO, "BTS connection %s\n", up ? "up" : "down");
|
||||
|
||||
if (up) {
|
||||
mgr->oc2gbts_ctrl.is_up = 1;
|
||||
mgr->oc2gbts_ctrl.last_seqno = 0;
|
||||
/* handle any pending alarm */
|
||||
handle_alert_actions(mgr);
|
||||
handle_warn_actions(mgr);
|
||||
} else {
|
||||
mgr->oc2gbts_ctrl.is_up = 0;
|
||||
schedule_bts_connect(mgr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* BTS re-connect timer call-back */
|
||||
static void bts_recon_timer_cb(void *data)
|
||||
{
|
||||
int rc;
|
||||
struct oc2gbts_mgr_instance *mgr = data;
|
||||
|
||||
/* update LED pattern */
|
||||
select_led_pattern(mgr);
|
||||
|
||||
/* The connection failures are to be expected during boot */
|
||||
mgr->oc2gbts_ctrl.bts_conn->ofd->when |= BSC_FD_WRITE;
|
||||
rc = ipa_client_conn_open(mgr->oc2gbts_ctrl.bts_conn);
|
||||
if (rc < 0) {
|
||||
LOGP(DLCTRL, LOGL_NOTICE, "Failed to connect to BTS.\n");
|
||||
schedule_bts_connect(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
static void oc2gbts_handle_ctrl(struct oc2gbts_mgr_instance *mgr, struct msgb *msg)
|
||||
{
|
||||
struct ctrl_cmd *cmd = ctrl_cmd_parse(tall_mgr_ctx, msg);
|
||||
int cause = atoi(cmd->reply);
|
||||
|
||||
if (!cmd) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to parse command/response\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd->type) {
|
||||
case CTRL_TYPE_GET_REPLY:
|
||||
LOGP(DCALIB, LOGL_INFO, "Got GET_REPLY from BTS cause=0x%x\n", cause);
|
||||
break;
|
||||
case CTRL_TYPE_SET_REPLY:
|
||||
LOGP(DCALIB, LOGL_INFO, "Got SET_REPLY from BTS cause=0x%x\n", cause);
|
||||
break;
|
||||
default:
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Unhandled CTRL response: %d. Resetting state\n",
|
||||
cmd->type);
|
||||
break;
|
||||
}
|
||||
|
||||
talloc_free(cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
static int bts_read_cb(struct ipa_client_conn *link, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
struct ipaccess_head *hh = (struct ipaccess_head *) msgb_l1(msg);
|
||||
struct ipaccess_head_ext *hh_ext;
|
||||
|
||||
LOGP(DLCTRL, LOGL_DEBUG, "Received data from BTS: %s\n",
|
||||
osmo_hexdump(msgb_data(msg), msgb_length(msg)));
|
||||
|
||||
/* regular message handling */
|
||||
rc = msg_verify_ipa_structure(msg);
|
||||
if (rc < 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Invalid IPA message from BTS (rc=%d)\n", rc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (hh->proto) {
|
||||
case IPAC_PROTO_OSMO:
|
||||
hh_ext = (struct ipaccess_head_ext *) hh->data;
|
||||
switch (hh_ext->proto) {
|
||||
case IPAC_PROTO_EXT_CTRL:
|
||||
oc2gbts_handle_ctrl(link->data, msg);
|
||||
break;
|
||||
default:
|
||||
LOGP(DCALIB, LOGL_NOTICE,
|
||||
"Unhandled osmo ID %u from BTS\n", hh_ext->proto);
|
||||
};
|
||||
msgb_free(msg);
|
||||
break;
|
||||
default:
|
||||
LOGP(DCALIB, LOGL_NOTICE,
|
||||
"Unhandled stream ID %u from BTS\n", hh->proto);
|
||||
msgb_free(msg);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int oc2gbts_mgr_calib_init(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* initialize last uptime */
|
||||
mgr->gps.last_update = 0;
|
||||
rc = oc2gbts_par_set_uptime(tall_mgr_ctx, mgr->gps.last_update);
|
||||
if (rc < 0)
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to store uptime to storage %d\n", rc);
|
||||
|
||||
/* get last GPS 3D fix timestamp */
|
||||
mgr->gps.last_gps_fix = 0;
|
||||
rc = oc2gbts_par_get_gps_fix(tall_mgr_ctx, &mgr->gps.last_gps_fix);
|
||||
if (rc < 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to get last GPS 3D fix timestamp from storage. Create it anyway %d\n", rc);
|
||||
rc = oc2gbts_par_set_gps_fix(tall_mgr_ctx, mgr->gps.last_gps_fix);
|
||||
if (rc < 0)
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to store initial GPS fix to storage %d\n", rc);
|
||||
}
|
||||
|
||||
mgr->calib.state = CALIB_INITIAL;
|
||||
mgr->calib.calib_timeout.data = mgr;
|
||||
mgr->calib.calib_timeout.cb = calib_loop_run;
|
||||
osmo_timer_schedule(&mgr->calib.calib_timeout, 0, 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int oc2gbts_mgr_control_init(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
mgr->oc2gbts_ctrl.bts_conn = ipa_client_conn_create(tall_mgr_ctx, NULL, 0,
|
||||
"127.0.0.1", OSMO_CTRL_PORT_BTS,
|
||||
bts_updown_cb, bts_read_cb,
|
||||
NULL, mgr);
|
||||
if (!mgr->oc2gbts_ctrl.bts_conn) {
|
||||
LOGP(DLCTRL, LOGL_ERROR, "Failed to create IPA connection to BTS\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mgr->oc2gbts_ctrl.recon_timer.cb = bts_recon_timer_cb;
|
||||
mgr->oc2gbts_ctrl.recon_timer.data = mgr;
|
||||
schedule_bts_connect(mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void oc2gbts_mgr_dispatch_alarm(struct oc2gbts_mgr_instance *mgr, const int cause, const char *key, const char *text)
|
||||
{
|
||||
/* Make sure the control link is ready before sending alarm */
|
||||
if (mgr->oc2gbts_ctrl.bts_conn->state != IPA_CLIENT_LINK_STATE_CONNECTED) {
|
||||
LOGP(DLCTRL, LOGL_NOTICE, "MGR losts connection to BTS.\n");
|
||||
LOGP(DLCTRL, LOGL_NOTICE, "MGR drops an alert cause=0x%x, text=%s to BTS\n", cause, text);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DLCTRL, LOGL_DEBUG, "MGR sends an alert cause=0x%x, text=%s to BTS\n", cause, text);
|
||||
send_set_ctrl_cmd(mgr, key, cause, text);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
/* NetworkListen for NuRAN OC-2G BTS management daemon */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* sysmobts_mgr_nl.c
|
||||
* (C) 2014 by Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "misc/oc2gbts_mgr.h"
|
||||
#include "misc/oc2gbts_misc.h"
|
||||
#include "misc/oc2gbts_nl.h"
|
||||
#include "misc/oc2gbts_par.h"
|
||||
#include "misc/oc2gbts_bid.h"
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/select.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define ETH0_ADDR_SYSFS "/var/oc2g/net/eth0/address"
|
||||
|
||||
static struct osmo_fd nl_fd;
|
||||
|
||||
/*
|
||||
* The TLV structure in IPA messages in UDP packages is a bit
|
||||
* weird. First the header appears to have an extra NULL byte
|
||||
* and second the L16 of the L16TV needs to include +1 for the
|
||||
* tag. The default msgb/tlv and libosmo-abis routines do not
|
||||
* provide this.
|
||||
*/
|
||||
|
||||
static void ipaccess_prepend_header_quirk(struct msgb *msg, int proto)
|
||||
{
|
||||
struct ipaccess_head *hh;
|
||||
|
||||
/* prepend the ip.access header */
|
||||
hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh) + 1);
|
||||
hh->len = htons(msg->len - sizeof(*hh) - 1);
|
||||
hh->proto = proto;
|
||||
}
|
||||
|
||||
static void quirk_l16tv_put(struct msgb *msg, uint16_t len, uint8_t tag,
|
||||
const uint8_t *val)
|
||||
{
|
||||
uint8_t *buf = msgb_put(msg, len + 2 + 1);
|
||||
|
||||
*buf++ = (len + 1) >> 8;
|
||||
*buf++ = (len + 1) & 0xff;
|
||||
*buf++ = tag;
|
||||
memcpy(buf, val, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't look at the content of the request yet and lie
|
||||
* about most of the responses.
|
||||
*/
|
||||
static void respond_to(struct sockaddr_in *src, struct osmo_fd *fd,
|
||||
uint8_t *data, size_t len)
|
||||
{
|
||||
static int fetched_info = 0;
|
||||
static char mac_str[20] = {0, };
|
||||
static char model_name[64] = {0, };
|
||||
static char ser_str[20] = {0, };
|
||||
|
||||
struct sockaddr_in loc_addr;
|
||||
int rc;
|
||||
char loc_ip[INET_ADDRSTRLEN];
|
||||
struct msgb *msg = msgb_alloc_headroom(512, 128, "ipa get response");
|
||||
if (!msg) {
|
||||
LOGP(DFIND, LOGL_ERROR, "Failed to allocate msgb\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fetched_info) {
|
||||
int fd_eth;
|
||||
int serno;
|
||||
int model;
|
||||
char rev_maj, rev_min;
|
||||
|
||||
/* fetch the MAC */
|
||||
fd_eth = open(ETH0_ADDR_SYSFS, O_RDONLY);
|
||||
if (fd_eth >= 0) {
|
||||
read(fd_eth, mac_str, sizeof(mac_str)-1);
|
||||
mac_str[sizeof(mac_str)-1] = '\0';
|
||||
close(fd_eth);
|
||||
}
|
||||
|
||||
/* fetch the serial number */
|
||||
oc2gbts_par_get_int(OC2GBTS_PAR_SERNR, &serno);
|
||||
snprintf(ser_str, sizeof(ser_str), "%d", serno);
|
||||
|
||||
/* fetch the model and trx number */
|
||||
snprintf(model_name, sizeof(model_name), "OC-2G BTS");
|
||||
|
||||
oc2gbts_rev_get(&rev_maj, &rev_min);
|
||||
snprintf(model_name, sizeof(model_name), "%s Rev %c.%c",
|
||||
model_name, rev_maj, rev_min);
|
||||
|
||||
model = oc2gbts_model_get();
|
||||
if (model >= 0) {
|
||||
snprintf(model_name, sizeof(model_name), "%s (%05X)",
|
||||
model_name, model);
|
||||
}
|
||||
fetched_info = 1;
|
||||
}
|
||||
|
||||
if (source_for_dest(&src->sin_addr, &loc_addr.sin_addr) != 0) {
|
||||
LOGP(DFIND, LOGL_ERROR, "Failed to determine local source\n");
|
||||
return;
|
||||
}
|
||||
|
||||
msgb_put_u8(msg, IPAC_MSGT_ID_RESP);
|
||||
|
||||
/* append MAC addr */
|
||||
quirk_l16tv_put(msg, strlen(mac_str) + 1, IPAC_IDTAG_MACADDR, (uint8_t *) mac_str);
|
||||
|
||||
/* append ip address */
|
||||
inet_ntop(AF_INET, &loc_addr.sin_addr, loc_ip, sizeof(loc_ip));
|
||||
quirk_l16tv_put(msg, strlen(loc_ip) + 1, IPAC_IDTAG_IPADDR, (uint8_t *) loc_ip);
|
||||
|
||||
/* append the serial number */
|
||||
quirk_l16tv_put(msg, strlen(ser_str) + 1, IPAC_IDTAG_SERNR, (uint8_t *) ser_str);
|
||||
|
||||
/* abuse some flags */
|
||||
quirk_l16tv_put(msg, strlen(model_name) + 1, IPAC_IDTAG_UNIT, (uint8_t *) model_name);
|
||||
|
||||
/* ip.access nanoBTS would reply to port==3006 */
|
||||
ipaccess_prepend_header_quirk(msg, IPAC_PROTO_IPACCESS);
|
||||
rc = sendto(fd->fd, msg->data, msg->len, 0, (struct sockaddr *)src, sizeof(*src));
|
||||
if (rc != msg->len)
|
||||
LOGP(DFIND, LOGL_ERROR,
|
||||
"Failed to send with rc(%d) errno(%d)\n", rc, errno);
|
||||
}
|
||||
|
||||
static int ipaccess_bcast(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
uint8_t data[2048];
|
||||
char src[INET_ADDRSTRLEN];
|
||||
struct sockaddr_in addr = {};
|
||||
socklen_t len = sizeof(addr);
|
||||
int rc;
|
||||
|
||||
rc = recvfrom(fd->fd, data, sizeof(data), 0,
|
||||
(struct sockaddr *) &addr, &len);
|
||||
if (rc <= 0) {
|
||||
LOGP(DFIND, LOGL_ERROR,
|
||||
"Failed to read from socket errno(%d)\n", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DFIND, LOGL_DEBUG,
|
||||
"Received request from: %s size %d\n",
|
||||
inet_ntop(AF_INET, &addr.sin_addr, src, sizeof(src)), rc);
|
||||
|
||||
if (rc < 6)
|
||||
return 0;
|
||||
|
||||
if (data[2] != IPAC_PROTO_IPACCESS || data[4] != IPAC_MSGT_ID_GET)
|
||||
return 0;
|
||||
|
||||
respond_to(&addr, fd, data + 6, rc - 6);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int oc2gbts_mgr_nl_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
nl_fd.cb = ipaccess_bcast;
|
||||
rc = osmo_sock_init_ofd(&nl_fd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||
"0.0.0.0", 3006, OSMO_SOCK_F_BIND);
|
||||
if (rc < 0) {
|
||||
perror("Socket creation");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,980 @@
|
|||
/* Temperature control for NuRAN OC-2G BTS management daemon */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* sysmobts_mgr_temp.c
|
||||
* (C) 2014 by Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
#include "misc/oc2gbts_mgr.h"
|
||||
#include "misc/oc2gbts_misc.h"
|
||||
#include "misc/oc2gbts_temp.h"
|
||||
#include "misc/oc2gbts_power.h"
|
||||
#include "misc/oc2gbts_led.h"
|
||||
#include "misc/oc2gbts_swd.h"
|
||||
#include "misc/oc2gbts_bid.h"
|
||||
#include "limits.h"
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
struct oc2gbts_mgr_instance *s_mgr;
|
||||
static struct osmo_timer_list sensor_ctrl_timer;
|
||||
|
||||
static const struct value_string state_names[] = {
|
||||
{ STATE_NORMAL, "NORMAL" },
|
||||
{ STATE_WARNING_HYST, "WARNING (HYST)" },
|
||||
{ STATE_WARNING, "WARNING" },
|
||||
{ STATE_CRITICAL, "CRITICAL" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/* private function prototype */
|
||||
static void sensor_ctrl_check(struct oc2gbts_mgr_instance *mgr);
|
||||
|
||||
const char *oc2gbts_mgr_sensor_get_state(enum oc2gbts_sensor_state state)
|
||||
{
|
||||
return get_value_string(state_names, state);
|
||||
}
|
||||
|
||||
static int next_state(enum oc2gbts_sensor_state current_state, int critical, int warning)
|
||||
{
|
||||
int next_state = -1;
|
||||
switch (current_state) {
|
||||
case STATE_NORMAL:
|
||||
if (critical)
|
||||
next_state = STATE_CRITICAL;
|
||||
else if (warning)
|
||||
next_state = STATE_WARNING;
|
||||
break;
|
||||
case STATE_WARNING_HYST:
|
||||
if (critical)
|
||||
next_state = STATE_CRITICAL;
|
||||
else if (warning)
|
||||
next_state = STATE_WARNING;
|
||||
else
|
||||
next_state = STATE_NORMAL;
|
||||
break;
|
||||
case STATE_WARNING:
|
||||
if (critical)
|
||||
next_state = STATE_CRITICAL;
|
||||
else if (!warning)
|
||||
next_state = STATE_WARNING_HYST;
|
||||
break;
|
||||
case STATE_CRITICAL:
|
||||
if (!critical && !warning)
|
||||
next_state = STATE_WARNING;
|
||||
break;
|
||||
};
|
||||
|
||||
return next_state;
|
||||
}
|
||||
|
||||
static void handle_normal_actions(int actions)
|
||||
{
|
||||
/* switch on the PA */
|
||||
if (actions & SENSOR_ACT_NORM_PA_ON) {
|
||||
if (oc2gbts_power_set(OC2GBTS_POWER_PA, 1) != 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR,
|
||||
"Failed to switch on the PA\n");
|
||||
} else {
|
||||
LOGP(DTEMP, LOGL_INFO,
|
||||
"Switched on the PA as normal action.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (actions & SENSOR_ACT_NORM_BTS_SRV_ON) {
|
||||
LOGP(DTEMP, LOGL_INFO,
|
||||
"Going to switch on the BTS service\n");
|
||||
/*
|
||||
* TODO: use/create something like nspawn that serializes
|
||||
* and used SIGCHLD/waitpid to pick up the dead processes
|
||||
* without invoking shell.
|
||||
*/
|
||||
system("/bin/systemctl start osmo-bts.service");
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_actions(int actions)
|
||||
{
|
||||
/* switch off the PA */
|
||||
if (actions & SENSOR_ACT_PA_OFF) {
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA)) {
|
||||
if (oc2gbts_power_set(OC2GBTS_POWER_PA, 0) != 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR,
|
||||
"Failed to switch off the PA. Stop BTS?\n");
|
||||
} else {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Switched off the PA due temperature.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actions & SENSOR_ACT_BTS_SRV_OFF) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Going to switch off the BTS service\n");
|
||||
/*
|
||||
* TODO: use/create something like nspawn that serializes
|
||||
* and used SIGCHLD/waitpid to pick up the dead processes
|
||||
* without invoking shell.
|
||||
*/
|
||||
system("/bin/systemctl stop osmo-bts.service");
|
||||
}
|
||||
}
|
||||
|
||||
void handle_ceased_actions(struct oc2gbts_mgr_instance *mgr)
|
||||
{ int i;
|
||||
uint32_t cause;
|
||||
|
||||
if (!mgr->oc2gbts_ctrl.is_up)
|
||||
return;
|
||||
|
||||
LOGP(DTEMP, LOGL_DEBUG, "handle_ceased_actions in state %s, warn_flags=0x%x, crit_flags=0x%x\n",
|
||||
oc2gbts_mgr_sensor_get_state(mgr->state.state),
|
||||
mgr->oc2gbts_ctrl.warn_flags,
|
||||
mgr->oc2gbts_ctrl.crit_flags);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
cause = 1 << i;
|
||||
/* clear warning flag without sending ceased alarm */
|
||||
if (mgr->oc2gbts_ctrl.warn_flags & cause)
|
||||
mgr->oc2gbts_ctrl.warn_flags &= ~cause;
|
||||
|
||||
/* clear warning flag with sending ceased alarm */
|
||||
if (mgr->oc2gbts_ctrl.crit_flags & cause) {
|
||||
/* clear associated flag */
|
||||
mgr->oc2gbts_ctrl.crit_flags &= ~cause;
|
||||
/* dispatch ceased alarm */
|
||||
switch (cause) {
|
||||
case S_MGR_TEMP_SUPPLY_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_TEMP_SUPPLY_MAX_FAIL, "oc2g-oml-ceased", "Main power supply temperature is too high");
|
||||
break;
|
||||
case S_MGR_TEMP_SOC_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_TEMP_SOC_MAX_FAIL, "oc2g-oml-ceased", "SoC temperature is too high");
|
||||
break;
|
||||
case S_MGR_TEMP_FPGA_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_TEMP_FPGA_MAX_FAIL, "oc2g-oml-ceased", "FPGA temperature is too high");
|
||||
break;
|
||||
case S_MGR_TEMP_RMS_DET_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_TEMP_RMS_DET_MAX_FAIL, "oc2g-oml-ceased", "RMS detector temperature is too high");
|
||||
break;
|
||||
case S_MGR_TEMP_OCXO_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_TEMP_OCXO_MAX_FAIL, "oc2g-oml-ceased", "OCXO temperature is too high");
|
||||
break;
|
||||
case S_MGR_TEMP_TRX_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_TEMP_TRX_MAX_FAIL, "oc2g-oml-ceased", "TRX temperature is too high");
|
||||
break;
|
||||
case S_MGR_TEMP_PA_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_TEMP_PA_MAX_FAIL, "oc2g-oml-ceased", "PA temperature is too high");
|
||||
break;
|
||||
case S_MGR_SUPPLY_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_SUPPLY_MAX_FAIL, "oc2g-oml-ceased", "Power supply voltage is too high");
|
||||
break;
|
||||
case S_MGR_SUPPLY_CRIT_MIN_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_SUPPLY_MIN_FAIL, "oc2g-oml-ceased", "Power supply voltage is too low");
|
||||
break;
|
||||
case S_MGR_VSWR_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_VSWR_MAX_FAIL, "oc2g-oml-ceased", "VSWR is too high");
|
||||
break;
|
||||
case S_MGR_PWR_SUPPLY_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_PWR_SUPPLY_MAX_FAIL, "oc2g-oml-ceased", "Power supply consumption is too high");
|
||||
break;
|
||||
case S_MGR_PWR_PA_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_PWR_PA_MAX_FAIL, "oc2g-oml-ceased", "PA power consumption is too high");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void handle_alert_actions(struct oc2gbts_mgr_instance *mgr)
|
||||
{ int i;
|
||||
uint32_t cause;
|
||||
|
||||
if (!mgr->oc2gbts_ctrl.is_up)
|
||||
return;
|
||||
|
||||
LOGP(DTEMP, LOGL_DEBUG, "handle_alert_actions in state %s, crit_flags=0x%x\n",
|
||||
oc2gbts_mgr_sensor_get_state(mgr->state.state),
|
||||
mgr->oc2gbts_ctrl.crit_flags);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
cause = 1 << i;
|
||||
if (mgr->oc2gbts_ctrl.crit_flags & cause) {
|
||||
switch(cause) {
|
||||
case S_MGR_TEMP_SUPPLY_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_TEMP_SUPPLY_MAX_FAIL, "oc2g-oml-alert", "Main power supply temperature is too high");
|
||||
break;
|
||||
case S_MGR_TEMP_SOC_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_TEMP_SOC_MAX_FAIL, "oc2g-oml-alert", "SoC temperature is too high");
|
||||
break;
|
||||
case S_MGR_TEMP_FPGA_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_TEMP_FPGA_MAX_FAIL, "oc2g-oml-alert", "FPGA temperature is too high");
|
||||
break;
|
||||
case S_MGR_TEMP_RMS_DET_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_TEMP_RMS_DET_MAX_FAIL, "oc2g-oml-alert", "RMS detector temperature is too high");
|
||||
break;
|
||||
case S_MGR_TEMP_OCXO_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_TEMP_OCXO_MAX_FAIL, "oc2g-oml-alert", "OCXO temperature is too high");
|
||||
break;
|
||||
case S_MGR_TEMP_TRX_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_TEMP_TRX_MAX_FAIL, "oc2g-oml-alert", "TRX temperature is too high");
|
||||
break;
|
||||
case S_MGR_TEMP_PA_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_TEMP_PA_MAX_FAIL, "oc2g-oml-alert", "PA temperature is too high");
|
||||
break;
|
||||
case S_MGR_SUPPLY_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_SUPPLY_MAX_FAIL, "oc2g-oml-alert", "Power supply voltage is too high");
|
||||
break;
|
||||
case S_MGR_SUPPLY_CRIT_MIN_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_SUPPLY_MIN_FAIL, "oc2g-oml-alert", "Power supply voltage is too low");
|
||||
break;
|
||||
case S_MGR_VSWR_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_VSWR_MAX_FAIL, "oc2g-oml-alert", "VSWR is too high");
|
||||
break;
|
||||
case S_MGR_PWR_SUPPLY_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_PWR_SUPPLY_MAX_FAIL, "oc2g-oml-alert", "Power supply consumption is too high");
|
||||
break;
|
||||
case S_MGR_PWR_PA_CRIT_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_CRIT_PWR_PA_MAX_FAIL, "oc2g-oml-alert", "PA power consumption is too high");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void handle_warn_actions(struct oc2gbts_mgr_instance *mgr)
|
||||
{ int i;
|
||||
uint32_t cause;
|
||||
|
||||
if (!mgr->oc2gbts_ctrl.is_up)
|
||||
return;
|
||||
|
||||
LOGP(DTEMP, LOGL_DEBUG, "handle_warn_actions in state %s, warn_flags=0x%x\n",
|
||||
oc2gbts_mgr_sensor_get_state(mgr->state.state),
|
||||
mgr->oc2gbts_ctrl.warn_flags);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
cause = 1 << i;
|
||||
if (mgr->oc2gbts_ctrl.warn_flags & cause) {
|
||||
switch(cause) {
|
||||
case S_MGR_TEMP_SUPPLY_WARN_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_TEMP_SUPPLY_HIGH_FAIL, "oc2g-oml-alert", "Main power supply temperature is high");
|
||||
break;
|
||||
case S_MGR_TEMP_SUPPLY_WARN_MIN_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_TEMP_SUPPLY_LOW_FAIL, "oc2g-oml-alert", "Main power supply temperature is low");
|
||||
break;
|
||||
case S_MGR_TEMP_SOC_WARN_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_TEMP_SOC_HIGH_FAIL, "oc2g-oml-alert", "SoC temperature is high");
|
||||
break;
|
||||
case S_MGR_TEMP_SOC_WARN_MIN_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_TEMP_SOC_LOW_FAIL, "oc2g-oml-alert", "SoC temperature is low");
|
||||
break;
|
||||
case S_MGR_TEMP_FPGA_WARN_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_TEMP_FPGA_HIGH_FAIL, "oc2g-oml-alert", "FPGA temperature is high");
|
||||
break;
|
||||
case S_MGR_TEMP_FPGA_WARN_MIN_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_TEMP_FPGA_LOW_FAIL, "oc2g-oml-alert", "FPGA temperature is low");
|
||||
break;
|
||||
case S_MGR_TEMP_RMS_DET_WARN_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_TEMP_RMS_DET_HIGH_FAIL, "oc2g-oml-alert", "RMS detector temperature is high");
|
||||
break;
|
||||
case S_MGR_TEMP_RMS_DET_WARN_MIN_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_TEMP_RMS_DET_LOW_FAIL, "oc2g-oml-alert", "RMS detector temperature is low");
|
||||
break;
|
||||
case S_MGR_TEMP_OCXO_WARN_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_TEMP_OCXO_HIGH_FAIL, "oc2g-oml-alert", "OCXO temperature is high");
|
||||
break;
|
||||
case S_MGR_TEMP_OCXO_WARN_MIN_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_TEMP_OCXO_LOW_FAIL, "oc2g-oml-alert", "OCXO temperature is low");
|
||||
break;
|
||||
case S_MGR_TEMP_TRX_WARN_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_TEMP_TRX_HIGH_FAIL, "oc2g-oml-alert", "TRX temperature is high");
|
||||
break;
|
||||
case S_MGR_TEMP_TRX_WARN_MIN_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_TEMP_TRX_LOW_FAIL, "oc2g-oml-alert", "TRX temperature is low");
|
||||
break;
|
||||
case S_MGR_TEMP_PA_WARN_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_TEMP_PA_HIGH_FAIL, "oc2g-oml-alert", "PA temperature is high");
|
||||
break;
|
||||
case S_MGR_TEMP_PA_WARN_MIN_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_TEMP_PA_LOW_FAIL, "oc2g-oml-alert", "PA temperature is low");
|
||||
break;
|
||||
case S_MGR_SUPPLY_WARN_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_SUPPLY_HIGH_FAIL, "oc2g-oml-alert", "Power supply voltage is high");
|
||||
break;
|
||||
case S_MGR_SUPPLY_WARN_MIN_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_SUPPLY_LOW_FAIL, "oc2g-oml-alert", "Power supply voltage is low");
|
||||
break;
|
||||
case S_MGR_VSWR_WARN_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_VSWR_HIGH_FAIL, "oc2g-oml-alert", "VSWR is high");
|
||||
break;
|
||||
case S_MGR_PWR_SUPPLY_WARN_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_PWR_SUPPLY_HIGH_FAIL, "oc2g-oml-alert", "Power supply consumption is high");
|
||||
break;
|
||||
case S_MGR_PWR_PA_WARN_MAX_ALARM:
|
||||
oc2gbts_mgr_dispatch_alarm(mgr, NM_EVT_CAUSE_WARN_PWR_PA_HIGH_FAIL, "oc2g-oml-alert", "PA power consumption is high");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go back to normal! Depending on the configuration execute the normal
|
||||
* actions that could (start to) undo everything we did in the other
|
||||
* states. What is still missing is the power increase/decrease depending
|
||||
* on the state. E.g. starting from WARNING_HYST we might want to slowly
|
||||
* ramp up the output power again.
|
||||
*/
|
||||
static void execute_normal_act(struct oc2gbts_mgr_instance *manager)
|
||||
{
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System is back to normal state.\n");
|
||||
handle_ceased_actions(manager);
|
||||
handle_normal_actions(manager->state.action_norm);
|
||||
}
|
||||
|
||||
static void execute_warning_act(struct oc2gbts_mgr_instance *manager)
|
||||
{
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning state.\n");
|
||||
handle_warn_actions(manager);
|
||||
handle_actions(manager->state.action_warn);
|
||||
}
|
||||
|
||||
/* Preventive timer call-back */
|
||||
static void preventive_timer_cb(void *_data)
|
||||
{
|
||||
struct oc2gbts_mgr_instance *mgr = _data;
|
||||
|
||||
/* Delete current preventive timer if possible */
|
||||
osmo_timer_del(&mgr->alarms.preventive_timer);
|
||||
|
||||
LOGP(DTEMP, LOGL_DEBUG, "Preventive timer expired in %d sec, retry=%d\n",
|
||||
mgr->alarms.preventive_duration,
|
||||
mgr->alarms.preventive_retry);
|
||||
|
||||
/* Turn on PA and clear action flag */
|
||||
if (mgr->state.action_comb & SENSOR_ACT_PA_OFF) {
|
||||
mgr->state.action_comb &= ~SENSOR_ACT_PA_OFF;
|
||||
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA)) {
|
||||
if (oc2gbts_power_set(OC2GBTS_POWER_PA, 1))
|
||||
LOGP(DTEMP, LOGL_ERROR, "Failed to switch on the PA\n");
|
||||
else
|
||||
LOGP(DTEMP, LOGL_DEBUG, "Re-enable PA after preventive timer expired in %d sec\n",
|
||||
mgr->alarms.preventive_duration);
|
||||
}
|
||||
}
|
||||
|
||||
/* restart check sensor timer */
|
||||
osmo_timer_del(&sensor_ctrl_timer);
|
||||
osmo_timer_schedule(&sensor_ctrl_timer, OC2GBTS_SENSOR_TIMER_DURATION, 0);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
static void execute_preventive_act(struct oc2gbts_mgr_instance *manager)
|
||||
{
|
||||
struct oc2gbts_preventive_list *prevent_list, *prevent_list2;
|
||||
|
||||
/* update LED pattern */
|
||||
select_led_pattern(manager);
|
||||
|
||||
/* do nothing if the preventive action list is empty */
|
||||
if (llist_empty(&manager->alarms.list))
|
||||
return;
|
||||
|
||||
llist_for_each_entry_safe(prevent_list, prevent_list2, &manager->alarms.list, list) {
|
||||
/* Delete the timer in list and perform action*/
|
||||
if (prevent_list) {
|
||||
/* Delete current preventive timer if possible */
|
||||
osmo_timer_del(&manager->alarms.preventive_timer);
|
||||
|
||||
/* Start/restart preventive timer */
|
||||
if (prevent_list->param.sleep_sec) {
|
||||
manager->alarms.preventive_timer.cb = preventive_timer_cb;
|
||||
manager->alarms.preventive_timer.data = manager;
|
||||
osmo_timer_schedule(&manager->alarms.preventive_timer, prevent_list->param.sleep_sec, 0);
|
||||
|
||||
LOGP(DTEMP, LOGL_DEBUG,"Preventive timer scheduled for %d sec, preventive flags=0x%x\n",
|
||||
prevent_list->param.sleep_sec,
|
||||
prevent_list->action_flag);
|
||||
}
|
||||
/* Update active flags */
|
||||
manager->state.action_comb |= prevent_list->action_flag;
|
||||
|
||||
/* Turn off PA */
|
||||
if (manager->state.action_comb & SENSOR_ACT_PA_OFF) {
|
||||
if (oc2gbts_power_set(OC2GBTS_POWER_PA, 0))
|
||||
LOGP(DTEMP, LOGL_ERROR, "Failed to switch off the PA\n");
|
||||
}
|
||||
|
||||
/* Delete this preventive entry */
|
||||
llist_del(&prevent_list->list);
|
||||
talloc_free(prevent_list);
|
||||
LOGP(DTEMP, LOGL_DEBUG,"Deleted preventive entry from list, entries left=%d\n",
|
||||
llist_count(&manager->alarms.list));
|
||||
|
||||
/* stay in last state is preventive active has exceed maximum number of retries */
|
||||
if (manager->alarms.preventive_retry > OC2GBTS_PREVENT_RETRY)
|
||||
LOGP(DTEMP, LOGL_NOTICE, "Maximum number of preventive active exceed\n");
|
||||
else
|
||||
/* increase retry counter */
|
||||
manager->alarms.preventive_retry++;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void execute_critical_act(struct oc2gbts_mgr_instance *manager)
|
||||
{
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical warning.\n");
|
||||
handle_alert_actions(manager);
|
||||
handle_actions(manager->state.action_crit);
|
||||
|
||||
}
|
||||
|
||||
static void oc2gbts_mgr_sensor_handle(struct oc2gbts_mgr_instance *manager,
|
||||
int critical, int warning)
|
||||
{
|
||||
int new_state = next_state(manager->state.state, critical, warning);
|
||||
|
||||
/* run preventive action if it is possible */
|
||||
execute_preventive_act(manager);
|
||||
|
||||
/* Nothing changed */
|
||||
if (new_state < 0)
|
||||
return;
|
||||
LOGP(DTEMP, LOGL_INFO, "Moving from state %s to %s.\n",
|
||||
get_value_string(state_names, manager->state.state),
|
||||
get_value_string(state_names, new_state));
|
||||
manager->state.state = new_state;
|
||||
switch (manager->state.state) {
|
||||
case STATE_NORMAL:
|
||||
execute_normal_act(manager);
|
||||
/* reset alarms */
|
||||
manager->alarms.temp_high = 0;
|
||||
manager->alarms.temp_max = 0;
|
||||
manager->alarms.vswr_high = 0;
|
||||
manager->alarms.vswr_max = 0;
|
||||
manager->alarms.supply_low = 0;
|
||||
manager->alarms.supply_min = 0;
|
||||
manager->alarms.supply_pwr_high = 0;
|
||||
manager->alarms.supply_pwr_max = 0;
|
||||
manager->alarms.pa_pwr_max = 0;
|
||||
manager->alarms.pa_pwr_high = 0;
|
||||
manager->state.action_comb = 0;
|
||||
manager->alarms.preventive_retry = 0;
|
||||
/* update LED pattern */
|
||||
select_led_pattern(manager);
|
||||
break;
|
||||
case STATE_WARNING_HYST:
|
||||
/* do nothing? Maybe start to increase transmit power? */
|
||||
break;
|
||||
case STATE_WARNING:
|
||||
execute_warning_act(manager);
|
||||
/* update LED pattern */
|
||||
select_led_pattern(manager);
|
||||
break;
|
||||
case STATE_CRITICAL:
|
||||
execute_critical_act(manager);
|
||||
/* update LED pattern */
|
||||
select_led_pattern(manager);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static void schedule_preventive_action(struct oc2gbts_mgr_instance *mgr, int action, int duration)
|
||||
{
|
||||
struct oc2gbts_preventive_list *prevent_list;
|
||||
|
||||
/* add to pending list */
|
||||
prevent_list = talloc_zero(tall_mgr_ctx, struct oc2gbts_preventive_list);
|
||||
if (prevent_list) {
|
||||
prevent_list->action_flag = action;
|
||||
prevent_list->param.sleep_sec = duration;
|
||||
prevent_list->param.sleep_usec = 0;
|
||||
llist_add_tail(&prevent_list->list, &mgr->alarms.list);
|
||||
LOGP(DTEMP, LOGL_DEBUG,"Added preventive action to list, duration=%d sec, total entries=%d\n",
|
||||
prevent_list->param.sleep_sec,
|
||||
llist_count(&mgr->alarms.list));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void sensor_ctrl_check(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
int rc;
|
||||
int temp, volt, vswr, power = 0;
|
||||
int warn_thresh_passed = 0;
|
||||
int crit_thresh_passed = 0;
|
||||
int action = 0;
|
||||
|
||||
LOGP(DTEMP, LOGL_INFO, "Going to check the temperature.\n");
|
||||
|
||||
/* Read the current supply temperature */
|
||||
rc = oc2gbts_temp_get(OC2GBTS_TEMP_SUPPLY, &temp);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Failed to read the supply temperature. rc=%d\n", rc);
|
||||
warn_thresh_passed = crit_thresh_passed = 1;
|
||||
} else {
|
||||
temp = temp / 1000;
|
||||
if (temp > mgr->temp.supply_temp_limit.thresh_warn_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because supply temperature is over %d\n", mgr->temp.supply_temp_limit.thresh_warn_max);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->alarms.temp_high = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_TEMP_SUPPLY_WARN_MAX_ALARM;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
if (temp < mgr->temp.supply_temp_limit.thresh_warn_min){
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because supply temperature is under %d\n", mgr->temp.supply_temp_limit.thresh_warn_min);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_TEMP_SUPPLY_WARN_MIN_ALARM;
|
||||
}
|
||||
if (temp > mgr->temp.supply_temp_limit.thresh_crit_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical because supply temperature is over %d\n", mgr->temp.supply_temp_limit.thresh_crit_max);
|
||||
crit_thresh_passed = 1;
|
||||
mgr->alarms.temp_max = 1;
|
||||
mgr->oc2gbts_ctrl.crit_flags |= S_MGR_TEMP_SUPPLY_CRIT_MAX_ALARM;
|
||||
mgr->oc2gbts_ctrl.warn_flags &= ~S_MGR_TEMP_SUPPLY_WARN_MAX_ALARM;
|
||||
action = SENSOR_ACT_PA_OFF;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
LOGP(DTEMP, LOGL_INFO, "Supply temperature is: %d\n", temp);
|
||||
}
|
||||
|
||||
/* Read the current SoC temperature */
|
||||
rc = oc2gbts_temp_get(OC2GBTS_TEMP_SOC, &temp);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Failed to read the SoC temperature. rc=%d\n", rc);
|
||||
warn_thresh_passed = crit_thresh_passed = 1;
|
||||
} else {
|
||||
temp = temp / 1000;
|
||||
if (temp > mgr->temp.soc_temp_limit.thresh_warn_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because SoC temperature is over %d\n", mgr->temp.soc_temp_limit.thresh_warn_max);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->alarms.temp_high = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_TEMP_SOC_WARN_MAX_ALARM;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
if (temp < mgr->temp.soc_temp_limit.thresh_warn_min){
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because SoC temperature is under %d\n", mgr->temp.soc_temp_limit.thresh_warn_min);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_TEMP_SOC_WARN_MIN_ALARM;
|
||||
}
|
||||
if (temp > mgr->temp.soc_temp_limit.thresh_crit_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical because SoC temperature is over %d\n", mgr->temp.soc_temp_limit.thresh_crit_max);
|
||||
crit_thresh_passed = 1;
|
||||
mgr->alarms.temp_max = 1;
|
||||
mgr->oc2gbts_ctrl.crit_flags |= S_MGR_TEMP_SOC_CRIT_MAX_ALARM;
|
||||
mgr->oc2gbts_ctrl.warn_flags &= ~S_MGR_TEMP_SOC_WARN_MAX_ALARM;
|
||||
action = SENSOR_ACT_PA_OFF;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
LOGP(DTEMP, LOGL_INFO, "SoC temperature is: %d\n", temp);
|
||||
}
|
||||
|
||||
/* Read the current fpga temperature */
|
||||
rc = oc2gbts_temp_get(OC2GBTS_TEMP_FPGA, &temp);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Failed to read the fpga temperature. rc=%d\n", rc);
|
||||
warn_thresh_passed = crit_thresh_passed = 1;
|
||||
} else {
|
||||
temp = temp / 1000;
|
||||
if (temp > mgr->temp.fpga_temp_limit.thresh_warn_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because fpga temperature is over %d\n", mgr->temp.fpga_temp_limit.thresh_warn_max);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->alarms.temp_high = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_TEMP_FPGA_WARN_MAX_ALARM;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
if (temp < mgr->temp.fpga_temp_limit.thresh_warn_min) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because fpga temperature is under %d\n", mgr->temp.fpga_temp_limit.thresh_warn_min);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_TEMP_FPGA_WARN_MIN_ALARM;
|
||||
}
|
||||
if (temp > mgr->temp.fpga_temp_limit.thresh_crit_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical because fpga temperature is over %d\n", mgr->temp.fpga_temp_limit.thresh_crit_max);
|
||||
crit_thresh_passed = 1;
|
||||
mgr->alarms.temp_max = 1;
|
||||
mgr->oc2gbts_ctrl.crit_flags |= S_MGR_TEMP_FPGA_CRIT_MAX_ALARM;
|
||||
mgr->oc2gbts_ctrl.warn_flags &= ~S_MGR_TEMP_FPGA_WARN_MAX_ALARM;
|
||||
action = SENSOR_ACT_PA_OFF;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
LOGP(DTEMP, LOGL_INFO, "FPGA temperature is: %d\n", temp);
|
||||
}
|
||||
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_RMS_FWD) || oc2gbts_option_get(OC2GBTS_OPTION_RMS_REFL)) {
|
||||
/* Read the current RMS detector temperature */
|
||||
rc = oc2gbts_temp_get(OC2GBTS_TEMP_RMSDET, &temp);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Failed to read the RMS detector temperature. rc=%d\n", rc);
|
||||
warn_thresh_passed = crit_thresh_passed = 1;
|
||||
} else {
|
||||
temp = temp / 1000;
|
||||
if (temp > mgr->temp.rmsdet_temp_limit.thresh_warn_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because RMS detector temperature is over %d\n", mgr->temp.rmsdet_temp_limit.thresh_warn_max);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->alarms.temp_high = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_TEMP_RMS_DET_WARN_MAX_ALARM;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
if (temp < mgr->temp.rmsdet_temp_limit.thresh_warn_min) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because RMS detector temperature is under %d\n", mgr->temp.rmsdet_temp_limit.thresh_warn_min);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_TEMP_RMS_DET_WARN_MIN_ALARM;
|
||||
}
|
||||
if (temp > mgr->temp.rmsdet_temp_limit.thresh_crit_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical because RMS detector temperature is over %d\n", mgr->temp.rmsdet_temp_limit.thresh_crit_max);
|
||||
crit_thresh_passed = 1;
|
||||
mgr->alarms.temp_max = 1;
|
||||
mgr->oc2gbts_ctrl.crit_flags |= S_MGR_TEMP_RMS_DET_CRIT_MAX_ALARM;
|
||||
mgr->oc2gbts_ctrl.warn_flags &= ~S_MGR_TEMP_RMS_DET_WARN_MAX_ALARM;
|
||||
action = SENSOR_ACT_PA_OFF;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
LOGP(DTEMP, LOGL_INFO, "RMS detector temperature is: %d\n", temp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the current OCXO temperature */
|
||||
rc = oc2gbts_temp_get(OC2GBTS_TEMP_OCXO, &temp);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Failed to read the OCXO temperature. rc=%d\n", rc);
|
||||
warn_thresh_passed = crit_thresh_passed = 1;
|
||||
} else {
|
||||
temp = temp / 1000;
|
||||
if (temp > mgr->temp.ocxo_temp_limit.thresh_warn_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because OCXO temperature is over %d\n", mgr->temp.ocxo_temp_limit.thresh_warn_max);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->alarms.temp_high = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_TEMP_OCXO_WARN_MAX_ALARM;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
if (temp < mgr->temp.ocxo_temp_limit.thresh_warn_min) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because OCXO temperature is under %d\n", mgr->temp.ocxo_temp_limit.thresh_warn_min);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_TEMP_OCXO_WARN_MIN_ALARM;
|
||||
}
|
||||
if (temp > mgr->temp.ocxo_temp_limit.thresh_crit_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical because OCXO temperature is over %d\n", mgr->temp.ocxo_temp_limit.thresh_crit_max);
|
||||
crit_thresh_passed = 1;
|
||||
mgr->alarms.temp_max = 1;
|
||||
mgr->oc2gbts_ctrl.crit_flags |= S_MGR_TEMP_OCXO_CRIT_MAX_ALARM;
|
||||
mgr->oc2gbts_ctrl.warn_flags &= ~S_MGR_TEMP_OCXO_WARN_MAX_ALARM;
|
||||
action = SENSOR_ACT_PA_OFF;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
LOGP(DTEMP, LOGL_INFO, "OCXO temperature is: %d\n", temp);
|
||||
}
|
||||
|
||||
/* Read the current TX temperature */
|
||||
rc = oc2gbts_temp_get(OC2GBTS_TEMP_TX, &temp);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Failed to read the TX temperature. rc=%d\n", rc);
|
||||
warn_thresh_passed = crit_thresh_passed = 1;
|
||||
} else {
|
||||
temp = temp / 1000;
|
||||
if (temp > mgr->temp.tx_temp_limit.thresh_warn_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because TX temperature is over %d\n", mgr->temp.tx_temp_limit.thresh_warn_max);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->alarms.temp_high = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_TEMP_TRX_WARN_MAX_ALARM;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
if (temp < mgr->temp.tx_temp_limit.thresh_warn_min) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because TX temperature is under %d\n", mgr->temp.tx_temp_limit.thresh_warn_min);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_TEMP_TRX_WARN_MIN_ALARM;
|
||||
}
|
||||
if (temp > mgr->temp.tx_temp_limit.thresh_crit_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical because TX temperature is over %d\n", mgr->temp.tx_temp_limit.thresh_crit_max);
|
||||
crit_thresh_passed = 1;
|
||||
mgr->alarms.temp_max = 1;
|
||||
mgr->oc2gbts_ctrl.crit_flags |= S_MGR_TEMP_TRX_CRIT_MAX_ALARM;
|
||||
mgr->oc2gbts_ctrl.warn_flags &= ~S_MGR_TEMP_TRX_WARN_MAX_ALARM;
|
||||
action = SENSOR_ACT_PA_OFF;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
LOGP(DTEMP, LOGL_INFO, "TX temperature is: %d\n", temp);
|
||||
}
|
||||
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA_TEMP)) {
|
||||
/* Read the current PA temperature */
|
||||
rc = oc2gbts_temp_get(OC2GBTS_TEMP_PA, &temp);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Failed to read the PA temperature. rc=%d\n", rc);
|
||||
warn_thresh_passed = crit_thresh_passed = 1;
|
||||
} else {
|
||||
temp = temp / 1000;
|
||||
if (temp > mgr->temp.pa_temp_limit.thresh_warn_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because PA temperature because is over %d\n", mgr->temp.pa_temp_limit.thresh_warn_max);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->alarms.temp_high = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_TEMP_PA_WARN_MAX_ALARM;
|
||||
action = SENSOR_ACT_PA_OFF;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
if (temp < mgr->temp.pa_temp_limit.thresh_warn_min) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because PA temperature because is under %d\n", mgr->temp.pa_temp_limit.thresh_warn_min);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_TEMP_PA_WARN_MIN_ALARM;
|
||||
}
|
||||
if (temp > mgr->temp.pa_temp_limit.thresh_crit_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical because PA temperature because is over %d\n", mgr->temp.pa_temp_limit.thresh_crit_max);
|
||||
crit_thresh_passed = 1;
|
||||
mgr->alarms.temp_max = 1;
|
||||
mgr->oc2gbts_ctrl.crit_flags |= S_MGR_TEMP_PA_CRIT_MAX_ALARM;
|
||||
mgr->oc2gbts_ctrl.warn_flags &= ~S_MGR_TEMP_PA_WARN_MAX_ALARM;
|
||||
action = SENSOR_ACT_PA_OFF;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
LOGP(DTEMP, LOGL_INFO, "PA temperature is: %d\n", temp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the current main supply voltage */
|
||||
if (oc2gbts_power_get(OC2GBTS_POWER_SUPPLY)) {
|
||||
rc = oc2gbts_power_sensor_get(OC2GBTS_POWER_SUPPLY, OC2GBTS_POWER_VOLTAGE, &volt);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Failed to read the main supply voltage. rc=%d\n", rc);
|
||||
warn_thresh_passed = crit_thresh_passed = 1;
|
||||
} else {
|
||||
if (volt > mgr->volt.supply_volt_limit.thresh_warn_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because supply voltage is over %d\n", mgr->volt.supply_volt_limit.thresh_warn_max);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_SUPPLY_WARN_MAX_ALARM;
|
||||
}
|
||||
if (volt < mgr->volt.supply_volt_limit.thresh_warn_min) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because supply voltage is under %d\n", mgr->volt.supply_volt_limit.thresh_warn_min);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->alarms.supply_low = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_SUPPLY_WARN_MIN_ALARM;
|
||||
}
|
||||
if (volt > mgr->volt.supply_volt_limit.thresh_crit_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical because supply voltage is over %d\n", mgr->volt.supply_volt_limit.thresh_crit_max);
|
||||
crit_thresh_passed = 1;
|
||||
mgr->oc2gbts_ctrl.crit_flags |= S_MGR_SUPPLY_CRIT_MAX_ALARM;
|
||||
mgr->oc2gbts_ctrl.warn_flags &= ~S_MGR_SUPPLY_WARN_MAX_ALARM;
|
||||
}
|
||||
|
||||
if (volt < mgr->volt.supply_volt_limit.thresh_crit_min) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical because supply voltage is under %d\n", mgr->volt.supply_volt_limit.thresh_crit_min);
|
||||
crit_thresh_passed = 1;
|
||||
mgr->alarms.supply_min = 1;
|
||||
mgr->oc2gbts_ctrl.crit_flags |= S_MGR_SUPPLY_CRIT_MIN_ALARM;
|
||||
mgr->oc2gbts_ctrl.warn_flags &= ~S_MGR_SUPPLY_WARN_MIN_ALARM;
|
||||
action = SENSOR_ACT_PA_OFF;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_NONE);
|
||||
}
|
||||
LOGP(DTEMP, LOGL_INFO, "Main supply voltage is: %d\n", volt);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the main supply power consumption */
|
||||
if (oc2gbts_power_get(OC2GBTS_POWER_SUPPLY)) {
|
||||
rc = oc2gbts_power_sensor_get(OC2GBTS_POWER_SUPPLY, OC2GBTS_POWER_POWER, &power);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Failed to read the power supply current. rc=%d\n", rc);
|
||||
warn_thresh_passed = crit_thresh_passed = 1;
|
||||
} else {
|
||||
power /= 1000000;
|
||||
if (power > mgr->pwr.supply_pwr_limit.thresh_warn_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because main supply power consumption is over %d\n", mgr->pwr.supply_pwr_limit.thresh_warn_max);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->alarms.supply_pwr_high = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_PWR_SUPPLY_WARN_MAX_ALARM;
|
||||
}
|
||||
if (power > mgr->pwr.supply_pwr_limit.thresh_crit_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical because main supply power consumption is over %d\n", mgr->pwr.supply_pwr_limit.thresh_crit_max);
|
||||
crit_thresh_passed = 1;
|
||||
|
||||
mgr->oc2gbts_ctrl.crit_flags |= S_MGR_PWR_SUPPLY_CRIT_MAX_ALARM;
|
||||
mgr->oc2gbts_ctrl.warn_flags &= ~S_MGR_PWR_SUPPLY_WARN_MAX_ALARM;
|
||||
|
||||
if (oc2gbts_power_get(OC2GBTS_POWER_PA)) {
|
||||
mgr->alarms.supply_pwr_max = 1;
|
||||
/* schedule to turn off PA */
|
||||
action = SENSOR_ACT_PA_OFF;
|
||||
/* repeat same alarm to BSC */
|
||||
handle_alert_actions(mgr);
|
||||
}
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_SHORT_DURATION);
|
||||
}
|
||||
LOGP(DTEMP, LOGL_INFO, "Main supply current power consumption is: %d\n", power);
|
||||
}
|
||||
} else {
|
||||
/* keep last state */
|
||||
if (mgr->oc2gbts_ctrl.crit_flags & S_MGR_PWR_SUPPLY_CRIT_MAX_ALARM) {
|
||||
warn_thresh_passed = 1;
|
||||
crit_thresh_passed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA)) {
|
||||
/* Read the current PA power consumption */
|
||||
if (oc2gbts_power_get(OC2GBTS_POWER_PA)) {
|
||||
rc = oc2gbts_power_sensor_get(OC2GBTS_POWER_PA, OC2GBTS_POWER_POWER, &power);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Failed to read the PA power. rc=%d\n", rc);
|
||||
warn_thresh_passed = crit_thresh_passed = 1;
|
||||
} else {
|
||||
power /= 1000000;
|
||||
if (power > mgr->pwr.pa_pwr_limit.thresh_warn_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because PA power consumption is over %d\n", mgr->pwr.pa_pwr_limit.thresh_warn_max);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->alarms.pa_pwr_high = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_PWR_PA_WARN_MAX_ALARM;
|
||||
}
|
||||
if (power > mgr->pwr.pa_pwr_limit.thresh_crit_max) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical because PA power consumption is over %d\n", mgr->pwr.pa_pwr_limit.thresh_crit_max);
|
||||
crit_thresh_passed = 1;
|
||||
mgr->alarms.pa_pwr_max = 1;
|
||||
mgr->oc2gbts_ctrl.crit_flags |= S_MGR_PWR_PA_CRIT_MAX_ALARM;
|
||||
mgr->oc2gbts_ctrl.warn_flags &= ~S_MGR_PWR_PA_WARN_MAX_ALARM;
|
||||
action = SENSOR_ACT_PA_OFF;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_SHORT_DURATION);
|
||||
}
|
||||
LOGP(DTEMP, LOGL_INFO, "PA power consumption is: %d\n", power);
|
||||
}
|
||||
} else {
|
||||
/* keep last state */
|
||||
if (mgr->oc2gbts_ctrl.crit_flags & S_MGR_PWR_PA_CRIT_MAX_ALARM) {
|
||||
warn_thresh_passed = 1;
|
||||
crit_thresh_passed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA) &&
|
||||
oc2gbts_option_get(OC2GBTS_OPTION_RMS_FWD) &&
|
||||
oc2gbts_option_get(OC2GBTS_OPTION_RMS_REFL)) {
|
||||
/* Read the current VSWR of powered ON PA*/
|
||||
if (oc2gbts_power_get(OC2GBTS_POWER_PA)) {
|
||||
rc = oc2gbts_vswr_get(OC2GBTS_VSWR, &vswr);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Failed to read the VSWR. rc=%d\n", rc);
|
||||
warn_thresh_passed = crit_thresh_passed = 1;
|
||||
} else {
|
||||
if ((vswr > mgr->vswr.vswr_limit.thresh_warn_max) && (mgr->vswr.last_vswr > mgr->vswr.vswr_limit.thresh_warn_max)) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached warning because VSWR is over %d\n", mgr->vswr.vswr_limit.thresh_warn_max);
|
||||
warn_thresh_passed = 1;
|
||||
mgr->alarms.vswr_high = 1;
|
||||
mgr->oc2gbts_ctrl.warn_flags |= S_MGR_VSWR_WARN_MAX_ALARM;
|
||||
}
|
||||
if ((vswr > mgr->vswr.vswr_limit.thresh_crit_max) && (mgr->vswr.last_vswr > mgr->vswr.vswr_limit.thresh_crit_max)) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical because VSWR is over %d\n", mgr->vswr.vswr_limit.thresh_crit_max);
|
||||
crit_thresh_passed = 1;
|
||||
mgr->alarms.vswr_max = 1;
|
||||
mgr->oc2gbts_ctrl.crit_flags |= S_MGR_VSWR_CRIT_MAX_ALARM;
|
||||
mgr->oc2gbts_ctrl.warn_flags &= ~S_MGR_VSWR_WARN_MAX_ALARM;
|
||||
action = SENSOR_ACT_PA_OFF;
|
||||
/* add to pending list */
|
||||
schedule_preventive_action(mgr, action, OC2GBTS_PREVENT_TIMER_DURATION);
|
||||
}
|
||||
LOGP(DTEMP, LOGL_INFO, "VSWR is: current = %d, last = %d\n", vswr, mgr->vswr.last_vswr);
|
||||
|
||||
/* update last VSWR */
|
||||
mgr->vswr.last_vswr = vswr;
|
||||
}
|
||||
} else {
|
||||
/* keep last state */
|
||||
if (mgr->oc2gbts_ctrl.crit_flags & S_MGR_VSWR_CRIT_MAX_ALARM) {
|
||||
warn_thresh_passed = 1;
|
||||
crit_thresh_passed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select_led_pattern(mgr);
|
||||
oc2gbts_mgr_sensor_handle(mgr, crit_thresh_passed, warn_thresh_passed);
|
||||
}
|
||||
|
||||
static void sensor_ctrl_check_cb(void *_data)
|
||||
{
|
||||
struct oc2gbts_mgr_instance *mgr = _data;
|
||||
sensor_ctrl_check(mgr);
|
||||
/* Check every minute? XXX make it configurable! */
|
||||
osmo_timer_schedule(&sensor_ctrl_timer, OC2GBTS_SENSOR_TIMER_DURATION, 0);
|
||||
LOGP(DTEMP, LOGL_DEBUG,"Check sensors timer expired\n");
|
||||
/* TODO: do we want to notify if some sensors could not be read? */
|
||||
oc2gbts_swd_event(mgr, SWD_CHECK_TEMP_SENSOR);
|
||||
}
|
||||
|
||||
int oc2gbts_mgr_sensor_init(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/* always enable PA GPIO for OC-2G */
|
||||
if (!oc2gbts_power_get(OC2GBTS_POWER_PA)) {
|
||||
rc = oc2gbts_power_set(OC2GBTS_POWER_PA, 1);
|
||||
if (!rc)
|
||||
LOGP(DTEMP, LOGL_ERROR, "Failed to set GPIO for internal PA\n");
|
||||
}
|
||||
|
||||
s_mgr = mgr;
|
||||
sensor_ctrl_timer.cb = sensor_ctrl_check_cb;
|
||||
sensor_ctrl_timer.data = s_mgr;
|
||||
sensor_ctrl_check_cb(s_mgr);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -0,0 +1,984 @@
|
|||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* sysmobts_mgr_vty.c
|
||||
* (C) 2014 by oc2gcom - s.f.m.c. GmbH
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Alvaro Neira Ayuso <anayuso@oc2gcom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
|
||||
#include "oc2gbts_misc.h"
|
||||
#include "oc2gbts_mgr.h"
|
||||
#include "oc2gbts_temp.h"
|
||||
#include "oc2gbts_power.h"
|
||||
#include "oc2gbts_bid.h"
|
||||
#include "oc2gbts_led.h"
|
||||
#include "btsconfig.h"
|
||||
|
||||
static struct oc2gbts_mgr_instance *s_mgr;
|
||||
|
||||
static const char copyright[] =
|
||||
"(C) 2012 by Harald Welte <laforge@gnumonks.org>\r\n"
|
||||
"(C) 2014 by Holger Hans Peter Freyther\r\n"
|
||||
"(C) 2015 by Yves Godin <support@nuranwireless.com>\r\n"
|
||||
"License AGPLv3+: GNU AGPL version 2 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
|
||||
"This is free software: you are free to change and redistribute it.\r\n"
|
||||
"There is NO WARRANTY, to the extent permitted by law.\r\n";
|
||||
|
||||
static int go_to_parent(struct vty *vty)
|
||||
{
|
||||
switch (vty->node) {
|
||||
case MGR_NODE:
|
||||
vty->node = CONFIG_NODE;
|
||||
break;
|
||||
case ACT_NORM_NODE:
|
||||
case ACT_WARN_NODE:
|
||||
case ACT_CRIT_NODE:
|
||||
case LIMIT_SUPPLY_TEMP_NODE:
|
||||
case LIMIT_SOC_NODE:
|
||||
case LIMIT_FPGA_NODE:
|
||||
case LIMIT_RMSDET_NODE:
|
||||
case LIMIT_OCXO_NODE:
|
||||
case LIMIT_TX_TEMP_NODE:
|
||||
case LIMIT_PA_TEMP_NODE:
|
||||
case LIMIT_SUPPLY_VOLT_NODE:
|
||||
case LIMIT_VSWR_NODE:
|
||||
case LIMIT_SUPPLY_PWR_NODE:
|
||||
case LIMIT_PA_PWR_NODE:
|
||||
vty->node = MGR_NODE;
|
||||
break;
|
||||
default:
|
||||
vty->node = CONFIG_NODE;
|
||||
}
|
||||
return vty->node;
|
||||
}
|
||||
|
||||
static int is_config_node(struct vty *vty, int node)
|
||||
{
|
||||
switch (node) {
|
||||
case MGR_NODE:
|
||||
case ACT_NORM_NODE:
|
||||
case ACT_WARN_NODE:
|
||||
case ACT_CRIT_NODE:
|
||||
case LIMIT_SUPPLY_TEMP_NODE:
|
||||
case LIMIT_SOC_NODE:
|
||||
case LIMIT_FPGA_NODE:
|
||||
case LIMIT_RMSDET_NODE:
|
||||
case LIMIT_OCXO_NODE:
|
||||
case LIMIT_TX_TEMP_NODE:
|
||||
case LIMIT_PA_TEMP_NODE:
|
||||
case LIMIT_SUPPLY_VOLT_NODE:
|
||||
case LIMIT_VSWR_NODE:
|
||||
case LIMIT_SUPPLY_PWR_NODE:
|
||||
case LIMIT_PA_PWR_NODE:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct vty_app_info vty_info = {
|
||||
.name = "oc2gbts-mgr",
|
||||
.version = PACKAGE_VERSION,
|
||||
.go_parent_cb = go_to_parent,
|
||||
.is_config_node = is_config_node,
|
||||
.copyright = copyright,
|
||||
};
|
||||
|
||||
|
||||
#define MGR_STR "Configure oc2gbts-mgr\n"
|
||||
|
||||
static struct cmd_node mgr_node = {
|
||||
MGR_NODE,
|
||||
"%s(oc2gbts-mgr)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node act_norm_node = {
|
||||
ACT_NORM_NODE,
|
||||
"%s(actions-normal)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node act_warn_node = {
|
||||
ACT_WARN_NODE,
|
||||
"%s(actions-warn)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node act_crit_node = {
|
||||
ACT_CRIT_NODE,
|
||||
"%s(actions-critical)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node limit_supply_temp_node = {
|
||||
LIMIT_SUPPLY_TEMP_NODE,
|
||||
"%s(limit-supply-temp)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node limit_soc_node = {
|
||||
LIMIT_SOC_NODE,
|
||||
"%s(limit-soc)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node limit_fpga_node = {
|
||||
LIMIT_FPGA_NODE,
|
||||
"%s(limit-fpga)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node limit_rmsdet_node = {
|
||||
LIMIT_RMSDET_NODE,
|
||||
"%s(limit-rmsdet)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node limit_ocxo_node = {
|
||||
LIMIT_OCXO_NODE,
|
||||
"%s(limit-ocxo)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node limit_tx_temp_node = {
|
||||
LIMIT_TX_TEMP_NODE,
|
||||
"%s(limit-tx-temp)# ",
|
||||
1,
|
||||
};
|
||||
static struct cmd_node limit_pa_temp_node = {
|
||||
LIMIT_PA_TEMP_NODE,
|
||||
"%s(limit-pa-temp)# ",
|
||||
1,
|
||||
};
|
||||
static struct cmd_node limit_supply_volt_node = {
|
||||
LIMIT_SUPPLY_VOLT_NODE,
|
||||
"%s(limit-supply-volt)# ",
|
||||
1,
|
||||
};
|
||||
static struct cmd_node limit_vswr_node = {
|
||||
LIMIT_VSWR_NODE,
|
||||
"%s(limit-vswr)# ",
|
||||
1,
|
||||
};
|
||||
static struct cmd_node limit_supply_pwr_node = {
|
||||
LIMIT_SUPPLY_PWR_NODE,
|
||||
"%s(limit-supply-pwr)# ",
|
||||
1,
|
||||
};
|
||||
static struct cmd_node limit_pa_pwr_node = {
|
||||
LIMIT_PA_PWR_NODE,
|
||||
"%s(limit-pa-pwr)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node limit_gps_fix_node = {
|
||||
LIMIT_GPS_FIX_NODE,
|
||||
"%s(limit-gps-fix)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
DEFUN(cfg_mgr, cfg_mgr_cmd,
|
||||
"oc2gbts-mgr",
|
||||
MGR_STR)
|
||||
{
|
||||
vty->node = MGR_NODE;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void write_volt_limit(struct vty *vty, const char *name,
|
||||
struct oc2gbts_volt_limit *limit)
|
||||
{
|
||||
vty_out(vty, " %s%s", name, VTY_NEWLINE);
|
||||
vty_out(vty, " threshold warning min %d%s",
|
||||
limit->thresh_warn_min, VTY_NEWLINE);
|
||||
vty_out(vty, " threshold critical min %d%s",
|
||||
limit->thresh_crit_min, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void write_vswr_limit(struct vty *vty, const char *name,
|
||||
struct oc2gbts_vswr_limit *limit)
|
||||
{
|
||||
vty_out(vty, " %s%s", name, VTY_NEWLINE);
|
||||
vty_out(vty, " threshold warning max %d%s",
|
||||
limit->thresh_warn_max, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void write_pwr_limit(struct vty *vty, const char *name,
|
||||
struct oc2gbts_pwr_limit *limit)
|
||||
{
|
||||
vty_out(vty, " %s%s", name, VTY_NEWLINE);
|
||||
vty_out(vty, " threshold warning max %d%s",
|
||||
limit->thresh_warn_max, VTY_NEWLINE);
|
||||
vty_out(vty, " threshold critical max %d%s",
|
||||
limit->thresh_crit_max, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void write_norm_action(struct vty *vty, const char *name, int actions)
|
||||
{
|
||||
vty_out(vty, " %s%s", name, VTY_NEWLINE);
|
||||
vty_out(vty, " %spa-on%s",
|
||||
(actions & SENSOR_ACT_NORM_PA_ON) ? "" : "no ", VTY_NEWLINE);
|
||||
vty_out(vty, " %sbts-service-on%s",
|
||||
(actions & SENSOR_ACT_NORM_BTS_SRV_ON) ? "" : "no ", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void write_action(struct vty *vty, const char *name, int actions)
|
||||
{
|
||||
vty_out(vty, " %s%s", name, VTY_NEWLINE);
|
||||
vty_out(vty, " %spa-off%s",
|
||||
(actions & SENSOR_ACT_PA_OFF) ? "" : "no ", VTY_NEWLINE);
|
||||
vty_out(vty, " %sbts-service-off%s",
|
||||
(actions & SENSOR_ACT_BTS_SRV_OFF) ? "" : "no ", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static int config_write_mgr(struct vty *vty)
|
||||
{
|
||||
vty_out(vty, "oc2gbts-mgr%s", VTY_NEWLINE);
|
||||
|
||||
write_volt_limit(vty, "limits supply_volt", &s_mgr->volt.supply_volt_limit);
|
||||
write_pwr_limit(vty, "limits supply_pwr", &s_mgr->pwr.supply_pwr_limit);
|
||||
write_vswr_limit(vty, "limits vswr", &s_mgr->vswr.vswr_limit);
|
||||
|
||||
write_norm_action(vty, "actions normal", s_mgr->state.action_norm);
|
||||
write_action(vty, "actions warn", s_mgr->state.action_warn);
|
||||
write_action(vty, "actions critical", s_mgr->state.action_crit);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int config_write_dummy(struct vty *vty)
|
||||
{
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define CFG_LIMIT_TEMP(name, expl, switch_to, variable) \
|
||||
DEFUN(cfg_limit_##name, cfg_limit_##name##_cmd, \
|
||||
"limits " #name, \
|
||||
"Configure Limits\n" expl) \
|
||||
{ \
|
||||
vty->node = switch_to; \
|
||||
vty->index = &s_mgr->temp.variable; \
|
||||
return CMD_SUCCESS; \
|
||||
}
|
||||
|
||||
CFG_LIMIT_TEMP(supply_temp, "SUPPLY TEMP\n", LIMIT_SUPPLY_TEMP_NODE, supply_temp_limit)
|
||||
CFG_LIMIT_TEMP(soc_temp, "SOC TEMP\n", LIMIT_SOC_NODE, soc_temp_limit)
|
||||
CFG_LIMIT_TEMP(fpga_temp, "FPGA TEMP\n", LIMIT_FPGA_NODE, fpga_temp_limit)
|
||||
CFG_LIMIT_TEMP(rmsdet_temp, "RMSDET TEMP\n", LIMIT_RMSDET_NODE, rmsdet_temp_limit)
|
||||
CFG_LIMIT_TEMP(ocxo_temp, "OCXO TEMP\n", LIMIT_OCXO_NODE, ocxo_temp_limit)
|
||||
CFG_LIMIT_TEMP(tx_temp, "TX TEMP\n", LIMIT_TX_TEMP_NODE, tx_temp_limit)
|
||||
CFG_LIMIT_TEMP(pa_temp, "PA TEMP\n", LIMIT_PA_TEMP_NODE, pa_temp_limit)
|
||||
#undef CFG_LIMIT_TEMP
|
||||
|
||||
#define CFG_LIMIT_VOLT(name, expl, switch_to, variable) \
|
||||
DEFUN(cfg_limit_##name, cfg_limit_##name##_cmd, \
|
||||
"limits " #name, \
|
||||
"Configure Limits\n" expl) \
|
||||
{ \
|
||||
vty->node = switch_to; \
|
||||
vty->index = &s_mgr->volt.variable; \
|
||||
return CMD_SUCCESS; \
|
||||
}
|
||||
|
||||
CFG_LIMIT_VOLT(supply_volt, "SUPPLY VOLT\n", LIMIT_SUPPLY_VOLT_NODE, supply_volt_limit)
|
||||
#undef CFG_LIMIT_VOLT
|
||||
|
||||
#define CFG_LIMIT_VSWR(name, expl, switch_to, variable) \
|
||||
DEFUN(cfg_limit_##name, cfg_limit_##name##_cmd, \
|
||||
"limits " #name, \
|
||||
"Configure Limits\n" expl) \
|
||||
{ \
|
||||
vty->node = switch_to; \
|
||||
vty->index = &s_mgr->vswr.variable; \
|
||||
return CMD_SUCCESS; \
|
||||
}
|
||||
|
||||
CFG_LIMIT_VSWR(vswr, "VSWR\n", LIMIT_VSWR_NODE, vswr_limit)
|
||||
#undef CFG_LIMIT_VSWR
|
||||
|
||||
#define CFG_LIMIT_PWR(name, expl, switch_to, variable) \
|
||||
DEFUN(cfg_limit_##name, cfg_limit_##name##_cmd, \
|
||||
"limits " #name, \
|
||||
"Configure Limits\n" expl) \
|
||||
{ \
|
||||
vty->node = switch_to; \
|
||||
vty->index = &s_mgr->pwr.variable; \
|
||||
return CMD_SUCCESS; \
|
||||
}
|
||||
|
||||
CFG_LIMIT_PWR(supply_pwr, "SUPPLY PWR\n", LIMIT_SUPPLY_PWR_NODE, supply_pwr_limit)
|
||||
CFG_LIMIT_PWR(pa_pwr, "PA PWR\n", LIMIT_PA_PWR_NODE, pa_pwr_limit)
|
||||
#undef CFG_LIMIT_PWR
|
||||
|
||||
#define CFG_LIMIT_GPS_FIX(name, expl, switch_to, variable) \
|
||||
DEFUN(cfg_limit_##name, cfg_limit_##name##_cmd, \
|
||||
"limits " #name, \
|
||||
"Configure Limits\n" expl) \
|
||||
{ \
|
||||
vty->node = switch_to; \
|
||||
vty->index = &s_mgr->gps.variable; \
|
||||
return CMD_SUCCESS; \
|
||||
}
|
||||
|
||||
CFG_LIMIT_GPS_FIX(gps_fix, "GPS FIX\n", LIMIT_GPS_FIX_NODE, gps_fix_limit)
|
||||
#undef CFG_LIMIT_GPS_FIX
|
||||
|
||||
DEFUN(cfg_limit_volt_warn_min, cfg_thresh_volt_warn_min_cmd,
|
||||
"threshold warning min <0-48000>",
|
||||
"Threshold to reach\n" "Warning level\n" "Range\n")
|
||||
{
|
||||
struct oc2gbts_volt_limit *limit = vty->index;
|
||||
limit->thresh_warn_min = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_limit_volt_crit_min, cfg_thresh_volt_crit_min_cmd,
|
||||
"threshold critical min <0-48000>",
|
||||
"Threshold to reach\n" "Critical level\n" "Range\n")
|
||||
{
|
||||
struct oc2gbts_volt_limit *limit = vty->index;
|
||||
limit->thresh_crit_min = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_limit_vswr_warn_max, cfg_thresh_vswr_warn_max_cmd,
|
||||
"threshold warning max <1000-200000>",
|
||||
"Threshold to reach\n" "Warning level\n" "Range\n")
|
||||
{
|
||||
struct oc2gbts_vswr_limit *limit = vty->index;
|
||||
limit->thresh_warn_max = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_limit_vswr_crit_max, cfg_thresh_vswr_crit_max_cmd,
|
||||
"threshold critical max <1000-200000>",
|
||||
"Threshold to reach\n" "Warning level\n" "Range\n")
|
||||
{
|
||||
struct oc2gbts_vswr_limit *limit = vty->index;
|
||||
limit->thresh_crit_max = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_limit_pwr_warn_max, cfg_thresh_pwr_warn_max_cmd,
|
||||
"threshold warning max <0-200>",
|
||||
"Threshold to reach\n" "Warning level\n" "Range\n")
|
||||
{
|
||||
struct oc2gbts_pwr_limit *limit = vty->index;
|
||||
limit->thresh_warn_max = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_limit_pwr_crit_max, cfg_thresh_pwr_crit_max_cmd,
|
||||
"threshold critical max <0-200>",
|
||||
"Threshold to reach\n" "Warning level\n" "Range\n")
|
||||
{
|
||||
struct oc2gbts_pwr_limit *limit = vty->index;
|
||||
limit->thresh_crit_max = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define CFG_ACTION(name, expl, switch_to, variable) \
|
||||
DEFUN(cfg_action_##name, cfg_action_##name##_cmd, \
|
||||
"actions " #name, \
|
||||
"Configure Actions\n" expl) \
|
||||
{ \
|
||||
vty->node = switch_to; \
|
||||
vty->index = &s_mgr->state.variable; \
|
||||
return CMD_SUCCESS; \
|
||||
}
|
||||
CFG_ACTION(normal, "Normal Actions\n", ACT_NORM_NODE, action_norm)
|
||||
CFG_ACTION(warn, "Warning Actions\n", ACT_WARN_NODE, action_warn)
|
||||
CFG_ACTION(critical, "Critical Actions\n", ACT_CRIT_NODE, action_crit)
|
||||
#undef CFG_ACTION
|
||||
|
||||
DEFUN(cfg_action_pa_on, cfg_action_pa_on_cmd,
|
||||
"pa-on",
|
||||
"Switch the Power Amplifier on\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action |= SENSOR_ACT_NORM_PA_ON;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_action_pa_on, cfg_no_action_pa_on_cmd,
|
||||
"no pa-on",
|
||||
NO_STR "Switch the Power Amplifier on\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action &= ~SENSOR_ACT_NORM_PA_ON;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_action_bts_srv_on, cfg_action_bts_srv_on_cmd,
|
||||
"bts-service-on",
|
||||
"Start the systemd oc2gbts.service\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action |= SENSOR_ACT_NORM_BTS_SRV_ON;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_action_bts_srv_on, cfg_no_action_bts_srv_on_cmd,
|
||||
"no bts-service-on",
|
||||
NO_STR "Start the systemd oc2gbts.service\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action &= ~SENSOR_ACT_NORM_BTS_SRV_ON;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_action_pa_off, cfg_action_pa_off_cmd,
|
||||
"pa-off",
|
||||
"Switch the Power Amplifier off\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action |= SENSOR_ACT_PA_OFF;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_action_pa_off, cfg_no_action_pa_off_cmd,
|
||||
"no pa-off",
|
||||
NO_STR "Do not switch off the Power Amplifier\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action &= ~SENSOR_ACT_PA_OFF;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_action_bts_srv_off, cfg_action_bts_srv_off_cmd,
|
||||
"bts-service-off",
|
||||
"Stop the systemd oc2gbts.service\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action |= SENSOR_ACT_BTS_SRV_OFF;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_action_bts_srv_off, cfg_no_action_bts_srv_off_cmd,
|
||||
"no bts-service-off",
|
||||
NO_STR "Stop the systemd oc2gbts.service\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action &= ~SENSOR_ACT_BTS_SRV_OFF;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_mgr, show_mgr_cmd, "show manager",
|
||||
SHOW_STR "Display information about the manager")
|
||||
{
|
||||
int temp, volt, current, power, vswr;
|
||||
vty_out(vty, "Warning alarm flags: 0x%08x%s",
|
||||
s_mgr->oc2gbts_ctrl.warn_flags, VTY_NEWLINE);
|
||||
vty_out(vty, "Critical alarm flags: 0x%08x%s",
|
||||
s_mgr->oc2gbts_ctrl.crit_flags, VTY_NEWLINE);
|
||||
vty_out(vty, "Preventive action retried: %d%s",
|
||||
s_mgr->alarms.preventive_retry, VTY_NEWLINE);
|
||||
vty_out(vty, "Temperature control state: %s%s",
|
||||
oc2gbts_mgr_sensor_get_state(s_mgr->state.state), VTY_NEWLINE);
|
||||
vty_out(vty, "Current Temperatures%s", VTY_NEWLINE);
|
||||
oc2gbts_temp_get(OC2GBTS_TEMP_SUPPLY, &temp);
|
||||
vty_out(vty, " Main Supply : %4.2f Celcius%s",
|
||||
temp/ 1000.0f,
|
||||
VTY_NEWLINE);
|
||||
oc2gbts_temp_get(OC2GBTS_TEMP_SOC, &temp);
|
||||
vty_out(vty, " SoC : %4.2f Celcius%s",
|
||||
temp / 1000.0f,
|
||||
VTY_NEWLINE);
|
||||
oc2gbts_temp_get(OC2GBTS_TEMP_FPGA, &temp);
|
||||
vty_out(vty, " FPGA : %4.2f Celcius%s",
|
||||
temp / 1000.0f,
|
||||
VTY_NEWLINE);
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_RMS_FWD) ||
|
||||
oc2gbts_option_get(OC2GBTS_OPTION_RMS_REFL)) {
|
||||
oc2gbts_temp_get(OC2GBTS_TEMP_RMSDET, &temp);
|
||||
vty_out(vty, " RMSDet : %4.2f Celcius%s",
|
||||
temp / 1000.0f,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
oc2gbts_temp_get(OC2GBTS_TEMP_OCXO, &temp);
|
||||
vty_out(vty, " OCXO : %4.2f Celcius%s",
|
||||
temp / 1000.0f,
|
||||
VTY_NEWLINE);
|
||||
oc2gbts_temp_get(OC2GBTS_TEMP_TX, &temp);
|
||||
vty_out(vty, " TX : %4.2f Celcius%s",
|
||||
temp / 1000.0f,
|
||||
VTY_NEWLINE);
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA_TEMP)) {
|
||||
oc2gbts_temp_get(OC2GBTS_TEMP_PA, &temp);
|
||||
vty_out(vty, " Power Amp : %4.2f Celcius%s",
|
||||
temp / 1000.0f,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
vty_out(vty, "Power Status%s", VTY_NEWLINE);
|
||||
oc2gbts_power_sensor_get(OC2GBTS_POWER_SUPPLY,
|
||||
OC2GBTS_POWER_VOLTAGE, &volt);
|
||||
oc2gbts_power_sensor_get(OC2GBTS_POWER_SUPPLY,
|
||||
OC2GBTS_POWER_CURRENT, ¤t);
|
||||
oc2gbts_power_sensor_get(OC2GBTS_POWER_SUPPLY,
|
||||
OC2GBTS_POWER_POWER, &power);
|
||||
vty_out(vty, " Main Supply : ON [%6.2f Vdc, %4.2f A, %6.2f W]%s",
|
||||
volt /1000.0f,
|
||||
current /1000.0f,
|
||||
power /1000000.0f,
|
||||
VTY_NEWLINE);
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA)) {
|
||||
oc2gbts_power_sensor_get(OC2GBTS_POWER_PA,
|
||||
OC2GBTS_POWER_VOLTAGE, &volt);
|
||||
oc2gbts_power_sensor_get(OC2GBTS_POWER_PA,
|
||||
OC2GBTS_POWER_CURRENT, ¤t);
|
||||
oc2gbts_power_sensor_get(OC2GBTS_POWER_PA,
|
||||
OC2GBTS_POWER_POWER, &power);
|
||||
vty_out(vty, " Power Amp : %s [%6.2f Vdc, %4.2f A, %6.2f W]%s",
|
||||
oc2gbts_power_get(OC2GBTS_POWER_PA) ? "ON " : "OFF",
|
||||
volt /1000.0f,
|
||||
current /1000.0f,
|
||||
power /1000000.0f,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA) &&
|
||||
oc2gbts_option_get(OC2GBTS_OPTION_RMS_FWD) &&
|
||||
oc2gbts_option_get(OC2GBTS_OPTION_RMS_REFL)) {
|
||||
vty_out(vty, "VSWR Status%s", VTY_NEWLINE);
|
||||
oc2gbts_vswr_get(OC2GBTS_VSWR, &vswr);
|
||||
vty_out(vty, " VSWR : %f %s",
|
||||
vswr / 1000.0f,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_thresh, show_thresh_cmd, "show thresholds",
|
||||
SHOW_STR "Display information about the thresholds")
|
||||
{
|
||||
vty_out(vty, "Temperature limits (Celsius)%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Main supply%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Critical max : %d%s",s_mgr->temp.supply_temp_limit.thresh_crit_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning max : %d%s",s_mgr->temp.supply_temp_limit.thresh_warn_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning min : %d%s",s_mgr->temp.supply_temp_limit.thresh_warn_min, VTY_NEWLINE);
|
||||
vty_out(vty, " SoC%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Critical max : %d%s",s_mgr->temp.soc_temp_limit.thresh_crit_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning max : %d%s",s_mgr->temp.soc_temp_limit.thresh_warn_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning min : %d%s",s_mgr->temp.soc_temp_limit.thresh_warn_min, VTY_NEWLINE);
|
||||
vty_out(vty, " FPGA%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Critical max : %d%s",s_mgr->temp.fpga_temp_limit.thresh_crit_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning max : %d%s",s_mgr->temp.fpga_temp_limit.thresh_warn_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning min : %d%s",s_mgr->temp.fpga_temp_limit.thresh_warn_min, VTY_NEWLINE);
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_RMS_FWD) ||
|
||||
oc2gbts_option_get(OC2GBTS_OPTION_RMS_REFL)) {
|
||||
vty_out(vty, " RMSDet%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Critical max : %d%s",s_mgr->temp.rmsdet_temp_limit.thresh_crit_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning max : %d%s",s_mgr->temp.rmsdet_temp_limit.thresh_warn_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning min : %d%s",s_mgr->temp.rmsdet_temp_limit.thresh_warn_min, VTY_NEWLINE);
|
||||
}
|
||||
vty_out(vty, " OCXO%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Critical max : %d%s",s_mgr->temp.ocxo_temp_limit.thresh_crit_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning max : %d%s",s_mgr->temp.ocxo_temp_limit.thresh_warn_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning min : %d%s",s_mgr->temp.ocxo_temp_limit.thresh_warn_min, VTY_NEWLINE);
|
||||
vty_out(vty, " TX%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Critical max : %d%s",s_mgr->temp.tx_temp_limit.thresh_crit_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning max : %d%s",s_mgr->temp.tx_temp_limit.thresh_warn_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning min : %d%s",s_mgr->temp.tx_temp_limit.thresh_warn_min, VTY_NEWLINE);
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA_TEMP)) {
|
||||
vty_out(vty, " PA%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Critical max : %d%s",s_mgr->temp.pa_temp_limit.thresh_crit_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning max : %d%s",s_mgr->temp.pa_temp_limit.thresh_warn_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning min : %d%s",s_mgr->temp.pa_temp_limit.thresh_warn_min, VTY_NEWLINE);
|
||||
}
|
||||
vty_out(vty, "Power limits%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Main supply (mV)%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Critical max : %d%s",s_mgr->volt.supply_volt_limit.thresh_crit_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning max : %d%s",s_mgr->volt.supply_volt_limit.thresh_warn_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning min : %d%s",s_mgr->volt.supply_volt_limit.thresh_warn_min, VTY_NEWLINE);
|
||||
vty_out(vty, " Critical min : %d%s",s_mgr->volt.supply_volt_limit.thresh_crit_min, VTY_NEWLINE);
|
||||
vty_out(vty, " Main supply power (W)%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Critical max : %d%s",s_mgr->pwr.supply_pwr_limit.thresh_crit_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning max : %d%s",s_mgr->pwr.supply_pwr_limit.thresh_warn_max, VTY_NEWLINE);
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA)) {
|
||||
vty_out(vty, " PA power (W)%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Critical max : %d%s",s_mgr->pwr.pa_pwr_limit.thresh_crit_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning max : %d%s",s_mgr->pwr.pa_pwr_limit.thresh_warn_max, VTY_NEWLINE);
|
||||
}
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA) &&
|
||||
oc2gbts_option_get(OC2GBTS_OPTION_RMS_FWD) &&
|
||||
oc2gbts_option_get(OC2GBTS_OPTION_RMS_REFL)) {
|
||||
vty_out(vty, "VSWR limits%s", VTY_NEWLINE);
|
||||
vty_out(vty, " TX%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Critical max : %d%s",s_mgr->vswr.vswr_limit.thresh_crit_max, VTY_NEWLINE);
|
||||
vty_out(vty, " Warning max : %d%s",s_mgr->vswr.vswr_limit.thresh_warn_max, VTY_NEWLINE);
|
||||
}
|
||||
vty_out(vty, "Days since last GPS 3D fix%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Warning max : %d%s",s_mgr->gps.gps_fix_limit.thresh_warn_max, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(calibrate_clock, calibrate_clock_cmd,
|
||||
"calibrate clock",
|
||||
"Calibration commands\n"
|
||||
"Calibrate clock against GPS PPS\n")
|
||||
{
|
||||
if (oc2gbts_mgr_calib_run(s_mgr) < 0) {
|
||||
vty_out(vty, "%%Failed to start calibration.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(set_led_pattern, set_led_pattern_cmd,
|
||||
"set led pattern <0-255>",
|
||||
"Set LED pattern\n"
|
||||
"Set LED pattern for debugging purpose only. This pattern will be overridden after 60 seconds by LED pattern of actual system state\n")
|
||||
{
|
||||
int pattern_id = atoi(argv[0]);
|
||||
|
||||
if ((pattern_id < 0) || (pattern_id > BLINK_PATTERN_MAX_ITEM)) {
|
||||
vty_out(vty, "%%Invalid LED pattern ID. It must be in range of %d..%d %s", 0, BLINK_PATTERN_MAX_ITEM - 1, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
led_set(s_mgr, pattern_id);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(force_mgr_state, force_mgr_state_cmd,
|
||||
"force manager state <0-255>",
|
||||
"Force BTS manager state\n"
|
||||
"Force BTS manager state for debugging purpose only\n")
|
||||
{
|
||||
int state = atoi(argv[0]);
|
||||
|
||||
if ((state < 0) || (state > STATE_CRITICAL)) {
|
||||
vty_out(vty, "%%Invalid BTS manager state. It must be in range of %d..%d %s", 0, STATE_CRITICAL, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
s_mgr->state.state = state;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define LIMIT_TEMP(name, limit, expl, variable, criticity, min_max) \
|
||||
DEFUN(limit_temp_##name##_##variable, limit_temp_##name##_##variable##_cmd, \
|
||||
"limit temp " #name " " #criticity " " #min_max " <-200-200>", \
|
||||
"Limit to reach\n" expl) \
|
||||
{ \
|
||||
s_mgr->temp.limit.variable = atoi(argv[0]); \
|
||||
return CMD_SUCCESS; \
|
||||
}
|
||||
|
||||
LIMIT_TEMP(supply, supply_temp_limit, "SUPPLY TEMP\n", thresh_warn_max, warning, max)
|
||||
LIMIT_TEMP(supply, supply_temp_limit, "SUPPLY TEMP\n", thresh_crit_max, critical, max)
|
||||
LIMIT_TEMP(supply, supply_temp_limit, "SUPPLY TEMP\n", thresh_warn_min, warning, min)
|
||||
LIMIT_TEMP(soc, supply_temp_limit, "SOC TEMP\n", thresh_warn_max, warning, max)
|
||||
LIMIT_TEMP(soc, supply_temp_limit, "SOC TEMP\n", thresh_crit_max, critical, max)
|
||||
LIMIT_TEMP(soc, supply_temp_limit, "SOC TEMP\n", thresh_warn_min, warning, min)
|
||||
LIMIT_TEMP(fpga, fpga_temp_limit, "FPGA TEMP\n", thresh_warn_max, warning, max)
|
||||
LIMIT_TEMP(fpga, fpga_temp_limit, "FPGA TEMP\n", thresh_crit_max, critical, max)
|
||||
LIMIT_TEMP(fpga, fpga_temp_limit, "FPGA TEMP\n", thresh_warn_min, warning, min)
|
||||
LIMIT_TEMP(rmsdet, rmsdet_temp_limit, "RMSDET TEMP\n", thresh_warn_max, warning, max)
|
||||
LIMIT_TEMP(rmsdet, rmsdet_temp_limit, "RMSDET TEMP\n", thresh_crit_max, critical, max)
|
||||
LIMIT_TEMP(rmsdet, rmsdet_temp_limit, "RMSDET TEMP\n", thresh_warn_min, warning, min)
|
||||
LIMIT_TEMP(ocxo, ocxo_temp_limit, "OCXO TEMP\n", thresh_warn_max, warning, max)
|
||||
LIMIT_TEMP(ocxo, ocxo_temp_limit, "OCXO TEMP\n", thresh_crit_max, critical, max)
|
||||
LIMIT_TEMP(ocxo, ocxo_temp_limit, "OCXO TEMP\n", thresh_warn_min, warning, min)
|
||||
LIMIT_TEMP(tx, tx_temp_limit, "TX TEMP\n", thresh_warn_max, warning, max)
|
||||
LIMIT_TEMP(tx, tx_temp_limit, "TX TEMP\n", thresh_crit_max, critical, max)
|
||||
LIMIT_TEMP(tx, tx_temp_limit, "TX TEMP\n", thresh_warn_min, warning, min)
|
||||
LIMIT_TEMP(pa, pa_temp_limit, "PA TEMP\n", thresh_warn_max, warning, max)
|
||||
LIMIT_TEMP(pa, pa_temp_limit, "PA TEMP\n", thresh_crit_max, critical, max)
|
||||
LIMIT_TEMP(pa, pa_temp_limit, "PA TEMP\n", thresh_warn_min, warning, min)
|
||||
#undef LIMIT_TEMP
|
||||
|
||||
#define LIMIT_VOLT(name, limit, expl, variable, criticity, min_max) \
|
||||
DEFUN(limit_volt_##name##_##variable, limit_volt_##name##_##variable##_cmd, \
|
||||
"limit " #name " " #criticity " " #min_max " <0-48000>", \
|
||||
"Limit to reach\n" expl) \
|
||||
{ \
|
||||
s_mgr->volt.limit.variable = atoi(argv[0]); \
|
||||
return CMD_SUCCESS; \
|
||||
}
|
||||
|
||||
LIMIT_VOLT(supply, supply_volt_limit, "SUPPLY VOLT\n", thresh_warn_max, warning, max)
|
||||
LIMIT_VOLT(supply, supply_volt_limit, "SUPPLY VOLT\n", thresh_crit_max, critical, max)
|
||||
LIMIT_VOLT(supply, supply_volt_limit, "SUPPLY VOLT\n", thresh_warn_min, warning, min)
|
||||
LIMIT_VOLT(supply, supply_volt_limit, "SUPPLY VOLT\n", thresh_crit_min, critical, min)
|
||||
#undef LIMIT_VOLT
|
||||
|
||||
#define LIMIT_PWR(name, limit, expl, variable, criticity, min_max) \
|
||||
DEFUN(limit_pwr_##name##_##variable, limit_pwr_##name##_##variable##_cmd, \
|
||||
"limit power " #name " " #criticity " " #min_max " <0-200>", \
|
||||
"Limit to reach\n" expl) \
|
||||
{ \
|
||||
s_mgr->pwr.limit.variable = atoi(argv[0]); \
|
||||
return CMD_SUCCESS; \
|
||||
}
|
||||
|
||||
LIMIT_PWR(supply, supply_pwr_limit, "SUPPLY PWR\n", thresh_warn_max, warning, max)
|
||||
LIMIT_PWR(supply, supply_pwr_limit, "SUPPLY PWR\n", thresh_crit_max, critical, max)
|
||||
LIMIT_PWR(pa, pa_pwr_limit, "PA PWR\n", thresh_warn_max, warning, max)
|
||||
LIMIT_PWR(pa, pa_pwr_limit, "PA PWR\n", thresh_crit_max, critical, max)
|
||||
#undef LIMIT_PWR
|
||||
|
||||
#define LIMIT_VSWR(limit, expl, variable, criticity, min_max) \
|
||||
DEFUN(limit_vswr_##variable, limit_vswr_##variable##_cmd, \
|
||||
"limit vswr " #criticity " " #min_max " <1000-200000>", \
|
||||
"Limit to reach\n" expl) \
|
||||
{ \
|
||||
s_mgr->vswr.limit.variable = atoi(argv[0]); \
|
||||
return CMD_SUCCESS; \
|
||||
}
|
||||
|
||||
LIMIT_VSWR(vswr_limit, "VSWR\n", thresh_warn_max, warning, max)
|
||||
LIMIT_VSWR(vswr_limit, "VSWR\n", thresh_crit_max, critical, max)
|
||||
#undef LIMIT_VSWR
|
||||
|
||||
#define LIMIT_GPSFIX(limit, expl, variable, criticity, min_max) \
|
||||
DEFUN(limit_gpsfix_##variable, limit_gpsfix_##variable##_cmd, \
|
||||
"limit gpsfix " #criticity " " #min_max " <0-365>", \
|
||||
"Limit to reach\n" expl) \
|
||||
{ \
|
||||
s_mgr->gps.limit.variable = atoi(argv[0]); \
|
||||
return CMD_SUCCESS; \
|
||||
}
|
||||
|
||||
LIMIT_GPSFIX(gps_fix_limit, "GPS FIX\n", thresh_warn_max, warning, max)
|
||||
#undef LIMIT_GPSFIX
|
||||
|
||||
static void register_limit(int limit, uint32_t unit)
|
||||
{
|
||||
switch (unit) {
|
||||
case MGR_LIMIT_TYPE_VOLT:
|
||||
install_element(limit, &cfg_thresh_volt_warn_min_cmd);
|
||||
install_element(limit, &cfg_thresh_volt_crit_min_cmd);
|
||||
break;
|
||||
case MGR_LIMIT_TYPE_VSWR:
|
||||
install_element(limit, &cfg_thresh_vswr_warn_max_cmd);
|
||||
install_element(limit, &cfg_thresh_vswr_crit_max_cmd);
|
||||
break;
|
||||
case MGR_LIMIT_TYPE_PWR:
|
||||
install_element(limit, &cfg_thresh_pwr_warn_max_cmd);
|
||||
install_element(limit, &cfg_thresh_pwr_crit_max_cmd);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void register_normal_action(int act)
|
||||
{
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA)) {
|
||||
install_element(act, &cfg_action_pa_on_cmd);
|
||||
install_element(act, &cfg_no_action_pa_on_cmd);
|
||||
}
|
||||
install_element(act, &cfg_action_bts_srv_on_cmd);
|
||||
install_element(act, &cfg_no_action_bts_srv_on_cmd);
|
||||
}
|
||||
|
||||
static void register_action(int act)
|
||||
{
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA)) {
|
||||
install_element(act, &cfg_action_pa_off_cmd);
|
||||
install_element(act, &cfg_no_action_pa_off_cmd);
|
||||
}
|
||||
install_element(act, &cfg_action_bts_srv_off_cmd);
|
||||
install_element(act, &cfg_no_action_bts_srv_off_cmd);
|
||||
}
|
||||
|
||||
static void register_hidden_commands()
|
||||
{
|
||||
install_element(ENABLE_NODE, &limit_temp_supply_thresh_warn_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_supply_thresh_crit_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_supply_thresh_warn_min_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_soc_thresh_warn_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_soc_thresh_crit_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_soc_thresh_warn_min_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_fpga_thresh_warn_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_fpga_thresh_crit_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_fpga_thresh_warn_min_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_rmsdet_thresh_warn_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_rmsdet_thresh_crit_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_rmsdet_thresh_warn_min_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_ocxo_thresh_warn_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_ocxo_thresh_crit_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_ocxo_thresh_warn_min_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_tx_thresh_warn_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_tx_thresh_crit_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_tx_thresh_warn_min_cmd);
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA_TEMP)) {
|
||||
install_element(ENABLE_NODE, &limit_temp_pa_thresh_warn_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_pa_thresh_crit_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_temp_pa_thresh_warn_min_cmd);
|
||||
}
|
||||
|
||||
install_element(ENABLE_NODE, &limit_volt_supply_thresh_warn_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_volt_supply_thresh_crit_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_volt_supply_thresh_warn_min_cmd);
|
||||
install_element(ENABLE_NODE, &limit_volt_supply_thresh_crit_min_cmd);
|
||||
|
||||
install_element(ENABLE_NODE, &limit_pwr_supply_thresh_warn_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_pwr_supply_thresh_crit_max_cmd);
|
||||
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA)) {
|
||||
install_element(ENABLE_NODE, &limit_pwr_pa_thresh_warn_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_pwr_pa_thresh_crit_max_cmd);
|
||||
}
|
||||
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA) &&
|
||||
oc2gbts_option_get(OC2GBTS_OPTION_RMS_FWD) &&
|
||||
oc2gbts_option_get(OC2GBTS_OPTION_RMS_REFL)) {
|
||||
install_element(ENABLE_NODE, &limit_vswr_thresh_warn_max_cmd);
|
||||
install_element(ENABLE_NODE, &limit_vswr_thresh_crit_max_cmd);
|
||||
}
|
||||
|
||||
install_element(ENABLE_NODE, &limit_gpsfix_thresh_warn_max_cmd);
|
||||
}
|
||||
|
||||
int oc2gbts_mgr_vty_init(void)
|
||||
{
|
||||
vty_init(&vty_info);
|
||||
|
||||
install_element_ve(&show_mgr_cmd);
|
||||
install_element_ve(&show_thresh_cmd);
|
||||
|
||||
install_element(ENABLE_NODE, &calibrate_clock_cmd);
|
||||
|
||||
install_node(&mgr_node, config_write_mgr);
|
||||
install_element(CONFIG_NODE, &cfg_mgr_cmd);
|
||||
vty_install_default(MGR_NODE);
|
||||
|
||||
/* install the limit nodes */
|
||||
install_node(&limit_supply_temp_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_supply_temp_cmd);
|
||||
vty_install_default(LIMIT_SUPPLY_TEMP_NODE);
|
||||
|
||||
install_node(&limit_soc_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_soc_temp_cmd);
|
||||
vty_install_default(LIMIT_SOC_NODE);
|
||||
|
||||
install_node(&limit_fpga_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_fpga_temp_cmd);
|
||||
vty_install_default(LIMIT_FPGA_NODE);
|
||||
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_RMS_FWD) ||
|
||||
oc2gbts_option_get(OC2GBTS_OPTION_RMS_REFL)) {
|
||||
install_node(&limit_rmsdet_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_rmsdet_temp_cmd);
|
||||
vty_install_default(LIMIT_RMSDET_NODE);
|
||||
}
|
||||
|
||||
install_node(&limit_ocxo_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_ocxo_temp_cmd);
|
||||
vty_install_default(LIMIT_OCXO_NODE);
|
||||
|
||||
install_node(&limit_tx_temp_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_tx_temp_cmd);
|
||||
vty_install_default(LIMIT_TX_TEMP_NODE);
|
||||
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA_TEMP)) {
|
||||
install_node(&limit_pa_temp_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_pa_temp_cmd);
|
||||
vty_install_default(LIMIT_PA_TEMP_NODE);
|
||||
}
|
||||
|
||||
install_node(&limit_supply_volt_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_supply_volt_cmd);
|
||||
register_limit(LIMIT_SUPPLY_VOLT_NODE, MGR_LIMIT_TYPE_VOLT);
|
||||
vty_install_default(LIMIT_SUPPLY_VOLT_NODE);
|
||||
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA) &&
|
||||
oc2gbts_option_get(OC2GBTS_OPTION_RMS_FWD) &&
|
||||
oc2gbts_option_get(OC2GBTS_OPTION_RMS_REFL)) {
|
||||
install_node(&limit_vswr_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_vswr_cmd);
|
||||
register_limit(LIMIT_VSWR_NODE, MGR_LIMIT_TYPE_VSWR);
|
||||
vty_install_default(LIMIT_VSWR_NODE);
|
||||
}
|
||||
|
||||
install_node(&limit_supply_pwr_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_supply_pwr_cmd);
|
||||
register_limit(LIMIT_SUPPLY_PWR_NODE, MGR_LIMIT_TYPE_PWR);
|
||||
vty_install_default(LIMIT_SUPPLY_PWR_NODE);
|
||||
|
||||
if (oc2gbts_option_get(OC2GBTS_OPTION_PA)) {
|
||||
install_node(&limit_pa_pwr_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_pa_pwr_cmd);
|
||||
vty_install_default(LIMIT_PA_PWR_NODE);
|
||||
}
|
||||
|
||||
install_node(&limit_gps_fix_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_gps_fix_cmd);
|
||||
vty_install_default(LIMIT_GPS_FIX_NODE);
|
||||
|
||||
/* install the normal node */
|
||||
install_node(&act_norm_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_action_normal_cmd);
|
||||
register_normal_action(ACT_NORM_NODE);
|
||||
|
||||
/* install the warning and critical node */
|
||||
install_node(&act_warn_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_action_warn_cmd);
|
||||
register_action(ACT_WARN_NODE);
|
||||
vty_install_default(ACT_WARN_NODE);
|
||||
|
||||
install_node(&act_crit_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_action_critical_cmd);
|
||||
register_action(ACT_CRIT_NODE);
|
||||
vty_install_default(ACT_CRIT_NODE);
|
||||
|
||||
/* install LED pattern command for debugging purpose */
|
||||
install_element_ve(&set_led_pattern_cmd);
|
||||
install_element_ve(&force_mgr_state_cmd);
|
||||
|
||||
register_hidden_commands();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int oc2gbts_mgr_parse_config(struct oc2gbts_mgr_instance *manager)
|
||||
{
|
||||
int rc;
|
||||
|
||||
s_mgr = manager;
|
||||
rc = vty_read_config_file(s_mgr->config_file, NULL);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to parse the config file: '%s'\n",
|
||||
s_mgr->config_file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,381 @@
|
|||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* sysmobts_misc.c
|
||||
* (C) 2012 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
|
||||
#include "oc2gbts_mgr.h"
|
||||
#include "btsconfig.h"
|
||||
#include "oc2gbts_misc.h"
|
||||
#include "oc2gbts_par.h"
|
||||
#include "oc2gbts_temp.h"
|
||||
#include "oc2gbts_power.h"
|
||||
#include "oc2gbts_bid.h"
|
||||
|
||||
/*********************************************************************
|
||||
* Temperature handling
|
||||
*********************************************************************/
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
int has_max;
|
||||
enum oc2gbts_temp_sensor sensor;
|
||||
enum oc2gbts_par ee_par;
|
||||
} temp_data[] = {
|
||||
{
|
||||
.name = "supply_temp",
|
||||
.has_max = 1,
|
||||
.sensor = OC2GBTS_TEMP_SUPPLY,
|
||||
.ee_par = OC2GBTS_PAR_TEMP_SUPPLY_MAX,
|
||||
}, {
|
||||
.name = "soc_temp",
|
||||
.has_max = 0,
|
||||
.sensor = OC2GBTS_TEMP_SOC,
|
||||
.ee_par = OC2GBTS_PAR_TEMP_SOC_MAX,
|
||||
}, {
|
||||
.name = "fpga_temp",
|
||||
.has_max = 0,
|
||||
.sensor = OC2GBTS_TEMP_FPGA,
|
||||
.ee_par = OC2GBTS_PAR_TEMP_FPGA_MAX,
|
||||
|
||||
}, {
|
||||
.name = "rmsdet_temp",
|
||||
.has_max = 1,
|
||||
.sensor = OC2GBTS_TEMP_RMSDET,
|
||||
.ee_par = OC2GBTS_PAR_TEMP_RMSDET_MAX,
|
||||
}, {
|
||||
.name = "ocxo_temp",
|
||||
.has_max = 1,
|
||||
.sensor = OC2GBTS_TEMP_OCXO,
|
||||
.ee_par = OC2GBTS_PAR_TEMP_OCXO_MAX,
|
||||
}, {
|
||||
.name = "tx_temp",
|
||||
.has_max = 0,
|
||||
.sensor = OC2GBTS_TEMP_TX,
|
||||
.ee_par = OC2GBTS_PAR_TEMP_TX_MAX,
|
||||
}, {
|
||||
.name = "pa_temp",
|
||||
.has_max = 1,
|
||||
.sensor = OC2GBTS_TEMP_PA,
|
||||
.ee_par = OC2GBTS_PAR_TEMP_PA_MAX,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
int has_max;
|
||||
enum oc2gbts_power_source sensor_source;
|
||||
enum oc2gbts_power_type sensor_type;
|
||||
enum oc2gbts_par ee_par;
|
||||
} power_data[] = {
|
||||
{
|
||||
.name = "supply_volt",
|
||||
.has_max = 1,
|
||||
.sensor_source = OC2GBTS_POWER_SUPPLY,
|
||||
.sensor_type = OC2GBTS_POWER_VOLTAGE,
|
||||
.ee_par = OC2GBTS_PAR_VOLT_SUPPLY_MAX,
|
||||
}, {
|
||||
.name = "supply_pwr",
|
||||
.has_max = 1,
|
||||
.sensor_source = OC2GBTS_POWER_SUPPLY,
|
||||
.sensor_type = OC2GBTS_POWER_POWER,
|
||||
.ee_par = OC2GBTS_PAR_PWR_SUPPLY_MAX,
|
||||
}, {
|
||||
.name = "pa_pwr",
|
||||
.has_max = 1,
|
||||
.sensor_source = OC2GBTS_POWER_PA,
|
||||
.sensor_type = OC2GBTS_POWER_POWER,
|
||||
.ee_par = OC2GBTS_PAR_PWR_PA_MAX,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
int has_max;
|
||||
enum oc2gbts_vswr_sensor sensor;
|
||||
enum oc2gbts_par ee_par;
|
||||
} vswr_data[] = {
|
||||
{
|
||||
.name = "vswr",
|
||||
.has_max = 0,
|
||||
.sensor = OC2GBTS_VSWR,
|
||||
.ee_par = OC2GBTS_PAR_VSWR_MAX,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct value_string power_unit_strs[] = {
|
||||
{ OC2GBTS_POWER_POWER, "W" },
|
||||
{ OC2GBTS_POWER_VOLTAGE, "V" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
void oc2gbts_check_temp(int no_rom_write)
|
||||
{
|
||||
int temp_old[ARRAY_SIZE(temp_data)];
|
||||
int temp_cur[ARRAY_SIZE(temp_data)];
|
||||
int i, rc;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(temp_data); i++) {
|
||||
int ret = -99;
|
||||
|
||||
if (temp_data[i].sensor == OC2GBTS_TEMP_PA &&
|
||||
!oc2gbts_option_get(OC2GBTS_OPTION_PA_TEMP))
|
||||
continue;
|
||||
|
||||
rc = oc2gbts_par_get_int(temp_data[i].ee_par, &ret);
|
||||
temp_old[i] = ret * 1000;
|
||||
|
||||
oc2gbts_temp_get(temp_data[i].sensor, &temp_cur[i]);
|
||||
if (temp_cur[i] < 0 && temp_cur[i] > -1000) {
|
||||
LOGP(DTEMP, LOGL_ERROR, "Error reading temperature (%d)\n", temp_data[i].sensor);
|
||||
continue;
|
||||
}
|
||||
|
||||
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_cur[i] > temp_old[i]) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "New maximum %s "
|
||||
"temperature: %d.%d C\n", temp_data[i].name,
|
||||
temp_cur[i]/1000, temp_old[i]%1000);
|
||||
|
||||
if (!no_rom_write) {
|
||||
rc = oc2gbts_par_set_int(temp_data[i].ee_par,
|
||||
temp_cur[i]/1000);
|
||||
if (rc < 0)
|
||||
LOGP(DTEMP, LOGL_ERROR, "error writing new %s "
|
||||
"max temp %d (%s)\n", temp_data[i].name,
|
||||
rc, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void oc2gbts_check_power(int no_rom_write)
|
||||
{
|
||||
int power_old[ARRAY_SIZE(power_data)];
|
||||
int power_cur[ARRAY_SIZE(power_data)];
|
||||
int i, rc;
|
||||
int div_ratio;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(power_data); i++) {
|
||||
int ret = 0;
|
||||
|
||||
if (power_data[i].sensor_source == OC2GBTS_POWER_PA &&
|
||||
!oc2gbts_option_get(OC2GBTS_OPTION_PA))
|
||||
continue;
|
||||
|
||||
rc = oc2gbts_par_get_int(power_data[i].ee_par, &ret);
|
||||
switch(power_data[i].sensor_type) {
|
||||
case OC2GBTS_POWER_VOLTAGE:
|
||||
div_ratio = 1000;
|
||||
break;
|
||||
case OC2GBTS_POWER_POWER:
|
||||
div_ratio = 1000000;
|
||||
break;
|
||||
default:
|
||||
div_ratio = 1000;
|
||||
}
|
||||
power_old[i] = ret * div_ratio;
|
||||
|
||||
oc2gbts_power_sensor_get(power_data[i].sensor_source, power_data[i].sensor_type, &power_cur[i]);
|
||||
if (power_cur[i] < 0 && power_cur[i] > -1000) {
|
||||
LOGP(DTEMP, LOGL_ERROR, "Error reading power (%d) (%d)\n", power_data[i].sensor_source, power_data[i].sensor_type);
|
||||
continue;
|
||||
}
|
||||
LOGP(DTEMP, LOGL_DEBUG, "Current %s power: %d.%d %s\n",
|
||||
power_data[i].name, power_cur[i]/div_ratio, power_cur[i]%div_ratio,
|
||||
get_value_string(power_unit_strs, power_data[i].sensor_type));
|
||||
|
||||
if (power_cur[i] > power_old[i]) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "New maximum %s "
|
||||
"power: %d.%d %s\n", power_data[i].name,
|
||||
power_cur[i]/div_ratio, power_cur[i]%div_ratio,
|
||||
get_value_string(power_unit_strs, power_data[i].sensor_type));
|
||||
|
||||
if (!no_rom_write) {
|
||||
rc = oc2gbts_par_set_int(power_data[i].ee_par,
|
||||
power_cur[i]/div_ratio);
|
||||
if (rc < 0)
|
||||
LOGP(DTEMP, LOGL_ERROR, "error writing new %s "
|
||||
"max power %d (%s)\n", power_data[i].name,
|
||||
rc, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void oc2gbts_check_vswr(int no_rom_write)
|
||||
{
|
||||
int vswr_old[ARRAY_SIZE(vswr_data)];
|
||||
int vswr_cur[ARRAY_SIZE(vswr_data)];
|
||||
int i, rc;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vswr_data); i++) {
|
||||
int ret = 0;
|
||||
|
||||
if (vswr_data[i].sensor == OC2GBTS_VSWR &&
|
||||
(!oc2gbts_option_get(OC2GBTS_OPTION_RMS_FWD) ||
|
||||
!oc2gbts_option_get(OC2GBTS_OPTION_RMS_REFL)))
|
||||
continue;
|
||||
|
||||
rc = oc2gbts_par_get_int(vswr_data[i].ee_par, &ret);
|
||||
vswr_old[i] = ret * 1000;
|
||||
|
||||
oc2gbts_vswr_get(vswr_data[i].sensor, &vswr_cur[i]);
|
||||
if (vswr_cur[i] < 0 && vswr_cur[i] > -1000) {
|
||||
LOGP(DTEMP, LOGL_ERROR, "Error reading vswr (%d)\n", vswr_data[i].sensor);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOGP(DTEMP, LOGL_DEBUG, "Current %s vswr: %d.%d\n",
|
||||
vswr_data[i].name, vswr_cur[i]/1000, vswr_cur[i]%1000);
|
||||
|
||||
if (vswr_cur[i] > vswr_old[i]) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "New maximum %s "
|
||||
"vswr: %d.%d C\n", vswr_data[i].name,
|
||||
vswr_cur[i]/1000, vswr_old[i]%1000);
|
||||
|
||||
if (!no_rom_write) {
|
||||
rc = oc2gbts_par_set_int(vswr_data[i].ee_par,
|
||||
vswr_cur[i]/1000);
|
||||
if (rc < 0)
|
||||
LOGP(DTEMP, LOGL_ERROR, "error writing new %s "
|
||||
"max vswr %d (%s)\n", vswr_data[i].name,
|
||||
rc, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Hours handling
|
||||
*********************************************************************/
|
||||
static time_t last_update;
|
||||
|
||||
int oc2gbts_update_hours(int no_rom_write)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
int rc, op_hrs = 0;
|
||||
|
||||
/* first time after start of manager program */
|
||||
if (last_update == 0) {
|
||||
last_update = now;
|
||||
|
||||
rc = oc2gbts_par_get_int(OC2GBTS_PAR_HOURS, &op_hrs);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR, "Unable to read "
|
||||
"operational hours: %d (%s)\n", rc,
|
||||
strerror(errno));
|
||||
/* create a new file anyway */
|
||||
if (!no_rom_write)
|
||||
rc = oc2gbts_par_set_int(OC2GBTS_PAR_HOURS, op_hrs);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
LOGP(DTEMP, LOGL_INFO, "Total hours of Operation: %u\n",
|
||||
op_hrs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (now >= last_update + 3600) {
|
||||
rc = oc2gbts_par_get_int(OC2GBTS_PAR_HOURS, &op_hrs);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR, "Unable to read "
|
||||
"operational hours: %d (%s)\n", rc,
|
||||
strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* number of hours to increase */
|
||||
op_hrs += (now-last_update)/3600;
|
||||
|
||||
LOGP(DTEMP, LOGL_INFO, "Total hours of Operation: %u\n",
|
||||
op_hrs);
|
||||
|
||||
if (!no_rom_write) {
|
||||
rc = oc2gbts_par_set_int(OC2GBTS_PAR_HOURS, op_hrs);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
last_update = now;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Firmware reloading
|
||||
*********************************************************************/
|
||||
|
||||
static const char *fw_sysfs[_NUM_FW] = {
|
||||
[OC2GBTS_FW_DSP] = "/sys/kernel/debug/remoteproc/remoteproc0/recovery",
|
||||
};
|
||||
|
||||
int oc2gbts_firmware_reload(enum oc2gbts_firmware_type type)
|
||||
{
|
||||
int fd;
|
||||
int rc;
|
||||
|
||||
switch (type) {
|
||||
case OC2GBTS_FW_DSP:
|
||||
fd = open(fw_sysfs[type], O_WRONLY);
|
||||
if (fd < 0) {
|
||||
LOGP(DFW, LOGL_ERROR, "unable ot open firmware device %s: %s\n",
|
||||
fw_sysfs[type], strerror(errno));
|
||||
close(fd);
|
||||
return fd;
|
||||
}
|
||||
rc = write(fd, "restart", 8);
|
||||
if (rc < 8) {
|
||||
LOGP(DFW, LOGL_ERROR, "short write during "
|
||||
"fw write to %s\n", fw_sysfs[type]);
|
||||
close(fd);
|
||||
return -EIO;
|
||||
}
|
||||
close(fd);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef _OC2GBTS_MISC_H
|
||||
#define _OC2GBTS_MISC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void oc2gbts_check_temp(int no_rom_write);
|
||||
void oc2gbts_check_power(int no_rom_write);
|
||||
void oc2gbts_check_vswr(int no_rom_write);
|
||||
|
||||
int oc2gbts_update_hours(int no_rom_write);
|
||||
|
||||
enum oc2gbts_firmware_type {
|
||||
OC2GBTS_FW_DSP,
|
||||
_NUM_FW
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,123 @@
|
|||
/* Helper for netlink */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* sysmobts_nl.c
|
||||
* (C) 2014 by Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/ip.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define NLMSG_TAIL(nmsg) \
|
||||
((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
|
||||
|
||||
/**
|
||||
* In case one binds to 0.0.0.0/INADDR_ANY and wants to know which source
|
||||
* address will be used when sending a message this function can be used.
|
||||
* It will ask the routing code of the kernel for the PREFSRC
|
||||
*/
|
||||
int source_for_dest(const struct in_addr *dest, struct in_addr *loc_source)
|
||||
{
|
||||
int fd, rc;
|
||||
struct rtmsg *r;
|
||||
struct rtattr *rta;
|
||||
struct {
|
||||
struct nlmsghdr n;
|
||||
struct rtmsg r;
|
||||
char buf[1024];
|
||||
} req;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE);
|
||||
if (fd < 0) {
|
||||
perror("nl socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Send a rtmsg and ask for a response */
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||
req.n.nlmsg_type = RTM_GETROUTE;
|
||||
req.n.nlmsg_seq = 1;
|
||||
|
||||
/* Prepare the routing request */
|
||||
req.r.rtm_family = AF_INET;
|
||||
|
||||
/* set the dest */
|
||||
rta = NLMSG_TAIL(&req.n);
|
||||
rta->rta_type = RTA_DST;
|
||||
rta->rta_len = RTA_LENGTH(sizeof(*dest));
|
||||
memcpy(RTA_DATA(rta), dest, sizeof(*dest));
|
||||
|
||||
/* update sizes for dest */
|
||||
req.r.rtm_dst_len = sizeof(*dest) * 8;
|
||||
req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_ALIGN(rta->rta_len);
|
||||
|
||||
rc = send(fd, &req, req.n.nlmsg_len, 0);
|
||||
if (rc != req.n.nlmsg_len) {
|
||||
perror("short write");
|
||||
close(fd);
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
/* now receive a response and parse it */
|
||||
rc = recv(fd, &req, sizeof(req), 0);
|
||||
if (rc <= 0) {
|
||||
perror("short read");
|
||||
close(fd);
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (!NLMSG_OK(&req.n, rc) || req.n.nlmsg_type != RTM_NEWROUTE) {
|
||||
close(fd);
|
||||
return -4;
|
||||
}
|
||||
|
||||
r = NLMSG_DATA(&req.n);
|
||||
rc -= NLMSG_LENGTH(sizeof(*r));
|
||||
rta = RTM_RTA(r);
|
||||
while (RTA_OK(rta, rc)) {
|
||||
if (rta->rta_type != RTA_PREFSRC) {
|
||||
rta = RTA_NEXT(rta, rc);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* we are done */
|
||||
memcpy(loc_source, RTA_DATA(rta), RTA_PAYLOAD(rta));
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return -5;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* sysmobts_nl.h
|
||||
* (C) 2014 by Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
struct in_addr;
|
||||
|
||||
int source_for_dest(const struct in_addr *dest, struct in_addr *loc_source);
|
|
@ -0,0 +1,249 @@
|
|||
/* oc2gbts - access to hardware related parameters */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* sysmobts_par.c
|
||||
* (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 <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
#include "oc2gbts_par.h"
|
||||
|
||||
const struct value_string oc2gbts_par_names[_NUM_OC2GBTS_PAR+1] = {
|
||||
{ OC2GBTS_PAR_TEMP_SUPPLY_MAX, "temp-supply-max" },
|
||||
{ OC2GBTS_PAR_TEMP_SOC_MAX, "temp-soc-max" },
|
||||
{ OC2GBTS_PAR_TEMP_FPGA_MAX, "temp-fpga-max" },
|
||||
{ OC2GBTS_PAR_TEMP_RMSDET_MAX, "temp-rmsdet-max" },
|
||||
{ OC2GBTS_PAR_TEMP_OCXO_MAX, "temp-ocxo-max" },
|
||||
{ OC2GBTS_PAR_TEMP_TX_MAX, "temp-tx-max" },
|
||||
{ OC2GBTS_PAR_TEMP_PA_MAX, "temp-pa-max" },
|
||||
{ OC2GBTS_PAR_VOLT_SUPPLY_MAX, "volt-supply-max" },
|
||||
{ OC2GBTS_PAR_PWR_SUPPLY_MAX, "pwr-supply-max" },
|
||||
{ OC2GBTS_PAR_PWR_PA_MAX, "pwr-pa-max" },
|
||||
{ OC2GBTS_PAR_VSWR_MAX, "vswr-max" },
|
||||
{ OC2GBTS_PAR_GPS_FIX, "gps-fix" },
|
||||
{ OC2GBTS_PAR_SERNR, "serial-nr" },
|
||||
{ OC2GBTS_PAR_HOURS, "hours-running" },
|
||||
{ OC2GBTS_PAR_BOOTS, "boot-count" },
|
||||
{ OC2GBTS_PAR_KEY, "key" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
int oc2gbts_par_is_int(enum oc2gbts_par par)
|
||||
{
|
||||
switch (par) {
|
||||
case OC2GBTS_PAR_TEMP_SUPPLY_MAX:
|
||||
case OC2GBTS_PAR_TEMP_SOC_MAX:
|
||||
case OC2GBTS_PAR_TEMP_FPGA_MAX:
|
||||
case OC2GBTS_PAR_TEMP_RMSDET_MAX:
|
||||
case OC2GBTS_PAR_TEMP_OCXO_MAX:
|
||||
case OC2GBTS_PAR_TEMP_TX_MAX:
|
||||
case OC2GBTS_PAR_TEMP_PA_MAX:
|
||||
case OC2GBTS_PAR_VOLT_SUPPLY_MAX:
|
||||
case OC2GBTS_PAR_VSWR_MAX:
|
||||
case OC2GBTS_PAR_SERNR:
|
||||
case OC2GBTS_PAR_HOURS:
|
||||
case OC2GBTS_PAR_BOOTS:
|
||||
case OC2GBTS_PAR_PWR_SUPPLY_MAX:
|
||||
case OC2GBTS_PAR_PWR_PA_MAX:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
FILE *oc2gbts_par_get_path(void *ctx, enum oc2gbts_par par, const char* mode)
|
||||
{
|
||||
char *fpath;
|
||||
FILE *fp;
|
||||
|
||||
if (par >= _NUM_OC2GBTS_PAR)
|
||||
return NULL;
|
||||
|
||||
fpath = talloc_asprintf(ctx, "%s/%s", USER_ROM_PATH, get_value_string(oc2gbts_par_names, par));
|
||||
if (!fpath)
|
||||
return NULL;
|
||||
|
||||
fp = fopen(fpath, mode);
|
||||
if (!fp)
|
||||
fprintf(stderr, "Failed to open %s due to '%s' error\n", fpath, strerror(errno));
|
||||
|
||||
talloc_free(fpath);
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
int oc2gbts_par_get_int(enum oc2gbts_par par, int *ret)
|
||||
{
|
||||
char fpath[PATH_MAX];
|
||||
FILE *fp;
|
||||
int rc;
|
||||
|
||||
if (par >= _NUM_OC2GBTS_PAR)
|
||||
return -ENODEV;
|
||||
|
||||
snprintf(fpath, sizeof(fpath)-1, "%s/%s", USER_ROM_PATH, get_value_string(oc2gbts_par_names, par));
|
||||
fpath[sizeof(fpath)-1] = '\0';
|
||||
|
||||
fp = fopen(fpath, "r");
|
||||
if (fp == NULL) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
rc = fscanf(fp, "%d", ret);
|
||||
if (rc != 1) {
|
||||
fclose(fp);
|
||||
return -EIO;
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int oc2gbts_par_set_int(enum oc2gbts_par par, int val)
|
||||
{
|
||||
char fpath[PATH_MAX];
|
||||
FILE *fp;
|
||||
int rc;
|
||||
|
||||
if (par >= _NUM_OC2GBTS_PAR)
|
||||
return -ENODEV;
|
||||
|
||||
snprintf(fpath, sizeof(fpath)-1, "%s/%s", USER_ROM_PATH, get_value_string(oc2gbts_par_names, par));
|
||||
fpath[sizeof(fpath)-1] = '\0';
|
||||
|
||||
fp = fopen(fpath, "w");
|
||||
if (fp == NULL) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
rc = fprintf(fp, "%d", val);
|
||||
if (rc < 0) {
|
||||
fclose(fp);
|
||||
return -EIO;
|
||||
}
|
||||
fsync(fp);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int oc2gbts_par_get_buf(enum oc2gbts_par par, uint8_t *buf,
|
||||
unsigned int size)
|
||||
{
|
||||
char fpath[PATH_MAX];
|
||||
FILE *fp;
|
||||
int rc;
|
||||
|
||||
if (par >= _NUM_OC2GBTS_PAR)
|
||||
return -ENODEV;
|
||||
|
||||
snprintf(fpath, sizeof(fpath)-1, "%s/%s", USER_ROM_PATH, get_value_string(oc2gbts_par_names, par));
|
||||
fpath[sizeof(fpath)-1] = '\0';
|
||||
|
||||
fp = fopen(fpath, "rb");
|
||||
if (fp == NULL) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
rc = fread(buf, 1, size, fp);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int oc2gbts_par_set_buf(enum oc2gbts_par par, const uint8_t *buf,
|
||||
unsigned int size)
|
||||
{
|
||||
char fpath[PATH_MAX];
|
||||
FILE *fp;
|
||||
int rc;
|
||||
|
||||
if (par >= _NUM_OC2GBTS_PAR)
|
||||
return -ENODEV;
|
||||
|
||||
snprintf(fpath, sizeof(fpath)-1, "%s/%s", USER_ROM_PATH, get_value_string(oc2gbts_par_names, par));
|
||||
fpath[sizeof(fpath)-1] = '\0';
|
||||
|
||||
fp = fopen(fpath, "wb");
|
||||
if (fp == NULL) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
rc = fwrite(buf, 1, size, fp);
|
||||
|
||||
fsync(fp);
|
||||
fclose(fp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int oc2gbts_par_get_gps_fix(void *ctx, time_t *ret)
|
||||
{
|
||||
FILE *fp;
|
||||
int rc;
|
||||
|
||||
fp = oc2gbts_par_get_path(ctx, OC2GBTS_PAR_GPS_FIX, "r");
|
||||
if (fp == NULL) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
rc = fscanf(fp, "%ld", ret);
|
||||
if (rc != 1) {
|
||||
fclose(fp);
|
||||
return -EIO;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int oc2gbts_par_set_gps_fix(void *ctx, time_t val)
|
||||
{
|
||||
FILE *fp;
|
||||
int rc;
|
||||
|
||||
fp = oc2gbts_par_get_path(ctx, OC2GBTS_PAR_GPS_FIX, "w");
|
||||
if (fp == NULL) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
rc = fprintf(fp, "%ld", val);
|
||||
if (rc < 0) {
|
||||
fclose(fp);
|
||||
return -EIO;
|
||||
}
|
||||
fsync(fp);
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef _OC2GBTS_PAR_H
|
||||
#define _OC2GBTS_PAR_H
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#define FACTORY_ROM_PATH "/mnt/rom/factory"
|
||||
#define USER_ROM_PATH "/var/run/oc2gbts-mgr"
|
||||
#define UPTIME_TMP_PATH "/tmp/uptime"
|
||||
|
||||
enum oc2gbts_par {
|
||||
OC2GBTS_PAR_TEMP_SUPPLY_MAX,
|
||||
OC2GBTS_PAR_TEMP_SOC_MAX,
|
||||
OC2GBTS_PAR_TEMP_FPGA_MAX,
|
||||
OC2GBTS_PAR_TEMP_RMSDET_MAX,
|
||||
OC2GBTS_PAR_TEMP_OCXO_MAX,
|
||||
OC2GBTS_PAR_TEMP_TX_MAX,
|
||||
OC2GBTS_PAR_TEMP_PA_MAX,
|
||||
OC2GBTS_PAR_VOLT_SUPPLY_MAX,
|
||||
OC2GBTS_PAR_PWR_SUPPLY_MAX,
|
||||
OC2GBTS_PAR_PWR_PA_MAX,
|
||||
OC2GBTS_PAR_VSWR_MAX,
|
||||
OC2GBTS_PAR_GPS_FIX,
|
||||
OC2GBTS_PAR_SERNR,
|
||||
OC2GBTS_PAR_HOURS,
|
||||
OC2GBTS_PAR_BOOTS,
|
||||
OC2GBTS_PAR_KEY,
|
||||
_NUM_OC2GBTS_PAR
|
||||
};
|
||||
|
||||
extern const struct value_string oc2gbts_par_names[_NUM_OC2GBTS_PAR+1];
|
||||
|
||||
int oc2gbts_par_get_int(enum oc2gbts_par par, int *ret);
|
||||
int oc2gbts_par_set_int(enum oc2gbts_par par, int val);
|
||||
int oc2gbts_par_get_buf(enum oc2gbts_par par, uint8_t *buf,
|
||||
unsigned int size);
|
||||
int oc2gbts_par_set_buf(enum oc2gbts_par par, const uint8_t *buf,
|
||||
unsigned int size);
|
||||
|
||||
int oc2gbts_par_is_int(enum oc2gbts_par par);
|
||||
int oc2gbts_par_get_gps_fix(void *ctx, time_t *ret);
|
||||
int oc2gbts_par_set_gps_fix(void *ctx, time_t val);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,177 @@
|
|||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "oc2gbts_power.h"
|
||||
|
||||
static const char *power_enable_devs[_NUM_POWER_SOURCES] = {
|
||||
[OC2GBTS_POWER_PA] = "/var/oc2g/pa-state/pa0/state",
|
||||
};
|
||||
|
||||
static const char *power_sensor_devs[_NUM_POWER_SOURCES] = {
|
||||
[OC2GBTS_POWER_SUPPLY] = "/var/oc2g/pwr-sense/main-supply/",
|
||||
[OC2GBTS_POWER_PA] = "/var/oc2g/pwr-sense/pa0/",
|
||||
};
|
||||
|
||||
static const char *power_sensor_type_str[_NUM_POWER_TYPES] = {
|
||||
[OC2GBTS_POWER_POWER] = "power",
|
||||
[OC2GBTS_POWER_VOLTAGE] = "voltage",
|
||||
[OC2GBTS_POWER_CURRENT] = "current",
|
||||
};
|
||||
|
||||
int oc2gbts_power_sensor_get(
|
||||
enum oc2gbts_power_source source,
|
||||
enum oc2gbts_power_type type,
|
||||
int *power)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
char pwrstr[10];
|
||||
int fd, rc;
|
||||
|
||||
if (source >= _NUM_POWER_SOURCES)
|
||||
return -EINVAL;
|
||||
|
||||
if (type >= _NUM_POWER_TYPES)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, "%s%s", power_sensor_devs[source], power_sensor_type_str[type]);
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
fd = open(buf, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
rc = read(fd, pwrstr, sizeof(pwrstr));
|
||||
pwrstr[sizeof(pwrstr)-1] = '\0';
|
||||
if (rc < 0) {
|
||||
close(fd);
|
||||
return rc;
|
||||
}
|
||||
if (rc == 0) {
|
||||
close(fd);
|
||||
return -EIO;
|
||||
}
|
||||
close(fd);
|
||||
*power = atoi(pwrstr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int oc2gbts_power_set(
|
||||
enum oc2gbts_power_source source,
|
||||
int en)
|
||||
{
|
||||
int fd;
|
||||
int rc;
|
||||
|
||||
if (source != OC2GBTS_POWER_PA) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fd = open(power_enable_devs[source], O_WRONLY);
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
rc = write(fd, en?"1":"0", 2);
|
||||
close( fd );
|
||||
|
||||
if (rc != 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (en) usleep(50*1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int oc2gbts_power_get(
|
||||
enum oc2gbts_power_source source)
|
||||
{
|
||||
int fd;
|
||||
int rc;
|
||||
int retVal = 0;
|
||||
char enstr[10];
|
||||
|
||||
fd = open(power_enable_devs[source], O_RDONLY);
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
rc = read(fd, enstr, sizeof(enstr));
|
||||
enstr[rc-1] = '\0';
|
||||
|
||||
close(fd);
|
||||
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
if (rc == 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rc = strcmp(enstr, "enabled");
|
||||
if(rc == 0) {
|
||||
retVal = 1;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static const char *vswr_devs[_NUM_VSWR_SENSORS] = {
|
||||
[OC2GBTS_VSWR] = "/var/oc2g/vswr/tx0/vswr",
|
||||
};
|
||||
|
||||
int oc2gbts_vswr_get(enum oc2gbts_vswr_sensor sensor, int *vswr)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
char vswrstr[8];
|
||||
int fd, rc;
|
||||
|
||||
if (sensor < 0 || sensor >= _NUM_VSWR_SENSORS)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, "%s", vswr_devs[sensor]);
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
fd = open(buf, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
rc = read(fd, vswrstr, sizeof(vswrstr));
|
||||
vswrstr[sizeof(vswrstr)-1] = '\0';
|
||||
if (rc < 0) {
|
||||
close(fd);
|
||||
return rc;
|
||||
}
|
||||
if (rc == 0) {
|
||||
close(fd);
|
||||
return -EIO;
|
||||
}
|
||||
close(fd);
|
||||
*vswr = atoi(vswrstr);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef _OC2GBTS_POWER_H
|
||||
#define _OC2GBTS_POWER_H
|
||||
|
||||
enum oc2gbts_power_source {
|
||||
OC2GBTS_POWER_SUPPLY,
|
||||
OC2GBTS_POWER_PA,
|
||||
_NUM_POWER_SOURCES
|
||||
};
|
||||
|
||||
enum oc2gbts_power_type {
|
||||
OC2GBTS_POWER_POWER,
|
||||
OC2GBTS_POWER_VOLTAGE,
|
||||
OC2GBTS_POWER_CURRENT,
|
||||
_NUM_POWER_TYPES
|
||||
};
|
||||
|
||||
int oc2gbts_power_sensor_get(
|
||||
enum oc2gbts_power_source source,
|
||||
enum oc2gbts_power_type type,
|
||||
int *volt);
|
||||
|
||||
int oc2gbts_power_set(
|
||||
enum oc2gbts_power_source source,
|
||||
int en);
|
||||
|
||||
int oc2gbts_power_get(
|
||||
enum oc2gbts_power_source source);
|
||||
|
||||
enum oc2gbts_vswr_sensor {
|
||||
OC2GBTS_VSWR,
|
||||
_NUM_VSWR_SENSORS
|
||||
};
|
||||
|
||||
int oc2gbts_vswr_get(enum oc2gbts_vswr_sensor sensor, int *vswr);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,178 @@
|
|||
/* Systemd service wd notification for OC-2G BTS management daemon */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "misc/oc2gbts_mgr.h"
|
||||
#include "misc/oc2gbts_swd.h"
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
/* Needed for service watchdog notification */
|
||||
#include <systemd/sd-daemon.h>
|
||||
|
||||
/* This is the period used to verify if all events have been registered to be allowed
|
||||
to notify the systemd service watchdog
|
||||
*/
|
||||
#define SWD_PERIOD 30
|
||||
|
||||
static void swd_start(struct oc2gbts_mgr_instance *mgr);
|
||||
static void swd_process(struct oc2gbts_mgr_instance *mgr);
|
||||
static void swd_close(struct oc2gbts_mgr_instance *mgr);
|
||||
static void swd_state_reset(struct oc2gbts_mgr_instance *mgr, int reason);
|
||||
static int swd_run(struct oc2gbts_mgr_instance *mgr, int from_loop);
|
||||
static void swd_loop_run(void *_data);
|
||||
|
||||
enum swd_state {
|
||||
SWD_INITIAL,
|
||||
SWD_IN_PROGRESS,
|
||||
};
|
||||
|
||||
enum swd_result {
|
||||
SWD_FAIL_START,
|
||||
SWD_FAIL_NOTIFY,
|
||||
SWD_SUCCESS,
|
||||
};
|
||||
|
||||
static void swd_start(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
swd_process(mgr);
|
||||
}
|
||||
|
||||
static void swd_process(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
int rc = 0, notify = 0;
|
||||
|
||||
/* Did we get all needed conditions ? */
|
||||
if (mgr->swd.swd_eventmasks == mgr->swd.swd_events) {
|
||||
/* Ping systemd service wd if enabled */
|
||||
rc = sd_notify(0, "WATCHDOG=1");
|
||||
LOGP(DSWD, LOGL_INFO, "Watchdog notification attempt\n");
|
||||
notify = 1;
|
||||
}
|
||||
else {
|
||||
LOGP(DSWD, LOGL_INFO, "Missing watchdog events: e:0x%016llx,m:0x%016llx\n",mgr->swd.swd_events,mgr->swd.swd_eventmasks);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
LOGP(DSWD, LOGL_ERROR,
|
||||
"Failed to notify system service watchdog: %d\n", rc);
|
||||
swd_state_reset(mgr, SWD_FAIL_NOTIFY);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
/* Did we notified the watchdog? */
|
||||
if (notify) {
|
||||
mgr->swd.swd_events = 0;
|
||||
/* Makes sure we really cleared it in case any event was notified at this same moment (it would be lost) */
|
||||
if (mgr->swd.swd_events != 0)
|
||||
mgr->swd.swd_events = 0;
|
||||
}
|
||||
}
|
||||
|
||||
swd_state_reset(mgr, SWD_SUCCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
static void swd_close(struct oc2gbts_mgr_instance *mgr)
|
||||
{
|
||||
}
|
||||
|
||||
static void swd_state_reset(struct oc2gbts_mgr_instance *mgr, int outcome)
|
||||
{
|
||||
if (mgr->swd.swd_from_loop) {
|
||||
mgr->swd.swd_timeout.data = mgr;
|
||||
mgr->swd.swd_timeout.cb = swd_loop_run;
|
||||
osmo_timer_schedule(&mgr->swd.swd_timeout, SWD_PERIOD, 0);
|
||||
}
|
||||
|
||||
mgr->swd.state = SWD_INITIAL;
|
||||
swd_close(mgr);
|
||||
}
|
||||
|
||||
static int swd_run(struct oc2gbts_mgr_instance *mgr, int from_loop)
|
||||
{
|
||||
if (mgr->swd.state != SWD_INITIAL) {
|
||||
LOGP(DSWD, LOGL_ERROR, "Swd is already in progress.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mgr->swd.swd_from_loop = from_loop;
|
||||
|
||||
/* From now on everything will be handled from the failure */
|
||||
mgr->swd.state = SWD_IN_PROGRESS;
|
||||
swd_start(mgr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void swd_loop_run(void *_data)
|
||||
{
|
||||
int rc;
|
||||
struct oc2gbts_mgr_instance *mgr = _data;
|
||||
|
||||
LOGP(DSWD, LOGL_INFO, "Going to check for watchdog notification.\n");
|
||||
rc = swd_run(mgr, 1);
|
||||
if (rc != 0) {
|
||||
swd_state_reset(mgr, SWD_FAIL_START);
|
||||
}
|
||||
}
|
||||
|
||||
/* 'swd_num_events' configures the number of events to be monitored before notifying the
|
||||
systemd service watchdog. It must be in the range of [1,64]. Events are notified
|
||||
through the function 'oc2gbts_swd_event'
|
||||
*/
|
||||
int oc2gbts_swd_init(struct oc2gbts_mgr_instance *mgr, int swd_num_events)
|
||||
{
|
||||
/* Checks for a valid number of events to validate */
|
||||
if (swd_num_events < 1 || swd_num_events > 64)
|
||||
return(-1);
|
||||
|
||||
mgr->swd.state = SWD_INITIAL;
|
||||
mgr->swd.swd_timeout.data = mgr;
|
||||
mgr->swd.swd_timeout.cb = swd_loop_run;
|
||||
osmo_timer_schedule(&mgr->swd.swd_timeout, 0, 0);
|
||||
|
||||
if (swd_num_events == 64){
|
||||
mgr->swd.swd_eventmasks = 0xffffffffffffffffULL;
|
||||
}
|
||||
else {
|
||||
mgr->swd.swd_eventmasks = ((1ULL << swd_num_events) - 1);
|
||||
}
|
||||
mgr->swd.swd_events = 0;
|
||||
mgr->swd.num_events = swd_num_events;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Notifies that the specified event 'swd_event' happened correctly;
|
||||
the value must be in the range of [0,'swd_num_events'[ (see oc2gbts_swd_init).
|
||||
For example, if 'swd_num_events' was 64, 'swd_event' events are numbered 0 to 63.
|
||||
WARNING: if this function can be used from multiple threads at the same time,
|
||||
it must be protected with a kind of mutex to avoid loosing event notification.
|
||||
*/
|
||||
int oc2gbts_swd_event(struct oc2gbts_mgr_instance *mgr, enum mgr_swd_events swd_event)
|
||||
{
|
||||
/* Checks for a valid specified event (smaller than max possible) */
|
||||
if ((int)(swd_event) < 0 || (int)(swd_event) >= mgr->swd.num_events)
|
||||
return(-1);
|
||||
|
||||
mgr->swd.swd_events = mgr->swd.swd_events | ((unsigned long long int)(1) << (int)(swd_event));
|
||||
|
||||
/* !!! Uncomment following line to debug events notification */
|
||||
LOGP(DSWD, LOGL_DEBUG,"Swd event notified: %d\n", (int)(swd_event));
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef _OC2GBTS_SWD_H
|
||||
#define _OC2GBTS_SWD_H
|
||||
|
||||
int oc2gbts_swd_init(struct oc2gbts_mgr_instance *mgr, int swd_num_events);
|
||||
int oc2gbts_swd_event(struct oc2gbts_mgr_instance *mgr, enum mgr_swd_events swd_event);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,71 @@
|
|||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* 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 <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include "oc2gbts_temp.h"
|
||||
|
||||
static const char *temp_devs[_NUM_TEMP_SENSORS] = {
|
||||
[OC2GBTS_TEMP_SUPPLY] = "/var/oc2g/temp/main-supply/temp",
|
||||
[OC2GBTS_TEMP_SOC] = "/var/oc2g/temp/cpu/temp",
|
||||
[OC2GBTS_TEMP_FPGA] = "/var/oc2g/temp/fpga/temp",
|
||||
[OC2GBTS_TEMP_RMSDET] = "/var/oc2g/temp/rmsdet/temp",
|
||||
[OC2GBTS_TEMP_OCXO] = "/var/oc2g/temp/ocxo/temp",
|
||||
[OC2GBTS_TEMP_TX] = "/var/oc2g/temp/tx0/temp",
|
||||
[OC2GBTS_TEMP_PA] = "/var/oc2g/temp/pa0/temp",
|
||||
};
|
||||
|
||||
int oc2gbts_temp_get(enum oc2gbts_temp_sensor sensor, int *temp)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
char tempstr[8];
|
||||
int fd, rc;
|
||||
|
||||
if (sensor < 0 || sensor >= _NUM_TEMP_SENSORS)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, "%s", temp_devs[sensor]);
|
||||
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);
|
||||
*temp = atoi(tempstr);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef _OC2GBTS_TEMP_H
|
||||
#define _OC2GBTS_TEMP_H
|
||||
|
||||
enum oc2gbts_temp_sensor {
|
||||
OC2GBTS_TEMP_SUPPLY,
|
||||
OC2GBTS_TEMP_SOC,
|
||||
OC2GBTS_TEMP_FPGA,
|
||||
OC2GBTS_TEMP_RMSDET,
|
||||
OC2GBTS_TEMP_OCXO,
|
||||
OC2GBTS_TEMP_TX,
|
||||
OC2GBTS_TEMP_PA,
|
||||
_NUM_TEMP_SENSORS
|
||||
};
|
||||
|
||||
enum oc2gbts_temp_type {
|
||||
OC2GBTS_TEMP_INPUT,
|
||||
OC2GBTS_TEMP_LOWEST,
|
||||
OC2GBTS_TEMP_HIGHEST,
|
||||
OC2GBTS_TEMP_FAULT,
|
||||
_NUM_TEMP_TYPES
|
||||
};
|
||||
|
||||
int oc2gbts_temp_get(enum oc2gbts_temp_sensor sensor, int *temp);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,158 @@
|
|||
/* oc2gbts-util - access to hardware related parameters */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* sysmobts_misc.c
|
||||
* (C) 2012-2013 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
|
||||
|
||||
#include "oc2gbts_par.h"
|
||||
|
||||
enum act {
|
||||
ACT_GET,
|
||||
ACT_SET,
|
||||
};
|
||||
|
||||
static enum act action;
|
||||
static char *write_arg;
|
||||
static int void_warranty;
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
const struct value_string *par = oc2gbts_par_names;
|
||||
|
||||
printf("oc2gbts-util [--void-warranty -r | -w value] param_name\n");
|
||||
printf("Possible param names:\n");
|
||||
|
||||
for (; par->str != NULL; par += 1) {
|
||||
if (!oc2gbts_par_is_int(par->value))
|
||||
continue;
|
||||
printf(" %s\n", par->str);
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_idx = 0, c;
|
||||
static const struct option long_options[] = {
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ "read", 0, 0, 'r' },
|
||||
{ "void-warranty", 0, 0, 1000},
|
||||
{ "write", 1, 0, 'w' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "rw:h",
|
||||
long_options, &option_idx);
|
||||
if (c == -1)
|
||||
break;
|
||||
switch (c) {
|
||||
case 'r':
|
||||
action = ACT_GET;
|
||||
break;
|
||||
case 'w':
|
||||
action = ACT_SET;
|
||||
write_arg = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
print_help();
|
||||
return -1;
|
||||
break;
|
||||
case 1000:
|
||||
printf("Will void warranty on write.\n");
|
||||
void_warranty = 1;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *parname;
|
||||
enum oc2gbts_par par;
|
||||
int rc, val;
|
||||
|
||||
rc = parse_options(argc, argv);
|
||||
if (rc < 0)
|
||||
exit(2);
|
||||
|
||||
if (optind >= argc) {
|
||||
fprintf(stderr, "You must specify the parameter name\n");
|
||||
exit(2);
|
||||
}
|
||||
parname = argv[optind];
|
||||
|
||||
rc = get_string_value(oc2gbts_par_names, parname);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "`%s' is not a valid parameter\n", parname);
|
||||
exit(2);
|
||||
} else
|
||||
par = rc;
|
||||
|
||||
switch (action) {
|
||||
case ACT_GET:
|
||||
rc = oc2gbts_par_get_int(par, &val);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error %d\n", rc);
|
||||
goto err;
|
||||
}
|
||||
printf("%d\n", val);
|
||||
break;
|
||||
case ACT_SET:
|
||||
rc = oc2gbts_par_get_int(par, &val);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error %d\n", rc);
|
||||
goto err;
|
||||
}
|
||||
if (val != 0xFFFF && val != 0xFF && val != 0xFFFFFFFF && !void_warranty) {
|
||||
fprintf(stderr, "Parameter is already set!\r\n");
|
||||
goto err;
|
||||
}
|
||||
rc = oc2gbts_par_set_int(par, atoi(write_arg));
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error %d\n", rc);
|
||||
goto err;
|
||||
}
|
||||
printf("Success setting %s=%d\n", parname,
|
||||
atoi(write_arg));
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unsupported action\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
exit(0);
|
||||
|
||||
err:
|
||||
exit(1);
|
||||
}
|
||||
|
|
@ -0,0 +1,361 @@
|
|||
/* NuRAN Wireless OC-2G L1 API related definitions */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
* based on:
|
||||
* sysmobts.c
|
||||
* (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 <nrw/oc2g/oc2g.h>
|
||||
#include <nrw/oc2g/gsml1const.h>
|
||||
#include <nrw/oc2g/gsml1dbg.h>
|
||||
|
||||
#include "oc2gbts.h"
|
||||
|
||||
enum l1prim_type oc2gbts_get_l1prim_type(GsmL1_PrimId_t id)
|
||||
{
|
||||
switch (id) {
|
||||
case GsmL1_PrimId_MphInitReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphCloseReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphConnectReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphDisconnectReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphActivateReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphDeactivateReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphConfigReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphMeasureReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphInitCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphCloseCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphConnectCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphDisconnectCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphActivateCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphDeactivateCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphConfigCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphMeasureCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_PhEmptyFrameReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_PhDataReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphTimeInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_MphSyncInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_PhConnectInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_PhReadyToSendInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_PhDataInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_PhRaInd: return L1P_T_IND;
|
||||
default: return L1P_T_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
const struct value_string oc2gbts_l1prim_names[GsmL1_PrimId_NUM+1] = {
|
||||
{ GsmL1_PrimId_MphInitReq, "MPH-INIT.req" },
|
||||
{ GsmL1_PrimId_MphCloseReq, "MPH-CLOSE.req" },
|
||||
{ GsmL1_PrimId_MphConnectReq, "MPH-CONNECT.req" },
|
||||
{ GsmL1_PrimId_MphDisconnectReq,"MPH-DISCONNECT.req" },
|
||||
{ GsmL1_PrimId_MphActivateReq, "MPH-ACTIVATE.req" },
|
||||
{ GsmL1_PrimId_MphDeactivateReq,"MPH-DEACTIVATE.req" },
|
||||
{ GsmL1_PrimId_MphConfigReq, "MPH-CONFIG.req" },
|
||||
{ GsmL1_PrimId_MphMeasureReq, "MPH-MEASURE.req" },
|
||||
{ GsmL1_PrimId_MphInitCnf, "MPH-INIT.conf" },
|
||||
{ GsmL1_PrimId_MphCloseCnf, "MPH-CLOSE.conf" },
|
||||
{ GsmL1_PrimId_MphConnectCnf, "MPH-CONNECT.conf" },
|
||||
{ GsmL1_PrimId_MphDisconnectCnf,"MPH-DISCONNECT.conf" },
|
||||
{ GsmL1_PrimId_MphActivateCnf, "MPH-ACTIVATE.conf" },
|
||||
{ GsmL1_PrimId_MphDeactivateCnf,"MPH-DEACTIVATE.conf" },
|
||||
{ GsmL1_PrimId_MphConfigCnf, "MPH-CONFIG.conf" },
|
||||
{ GsmL1_PrimId_MphMeasureCnf, "MPH-MEASURE.conf" },
|
||||
{ GsmL1_PrimId_MphTimeInd, "MPH-TIME.ind" },
|
||||
{ GsmL1_PrimId_MphSyncInd, "MPH-SYNC.ind" },
|
||||
{ GsmL1_PrimId_PhEmptyFrameReq, "PH-EMPTY_FRAME.req" },
|
||||
{ GsmL1_PrimId_PhDataReq, "PH-DATA.req" },
|
||||
{ GsmL1_PrimId_PhConnectInd, "PH-CONNECT.ind" },
|
||||
{ GsmL1_PrimId_PhReadyToSendInd,"PH-READY_TO_SEND.ind" },
|
||||
{ GsmL1_PrimId_PhDataInd, "PH-DATA.ind" },
|
||||
{ GsmL1_PrimId_PhRaInd, "PH-RA.ind" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
GsmL1_PrimId_t oc2gbts_get_l1prim_conf(GsmL1_PrimId_t id)
|
||||
{
|
||||
switch (id) {
|
||||
case GsmL1_PrimId_MphInitReq: return GsmL1_PrimId_MphInitCnf;
|
||||
case GsmL1_PrimId_MphCloseReq: return GsmL1_PrimId_MphCloseCnf;
|
||||
case GsmL1_PrimId_MphConnectReq: return GsmL1_PrimId_MphConnectCnf;
|
||||
case GsmL1_PrimId_MphDisconnectReq: return GsmL1_PrimId_MphDisconnectCnf;
|
||||
case GsmL1_PrimId_MphActivateReq: return GsmL1_PrimId_MphActivateCnf;
|
||||
case GsmL1_PrimId_MphDeactivateReq: return GsmL1_PrimId_MphDeactivateCnf;
|
||||
case GsmL1_PrimId_MphConfigReq: return GsmL1_PrimId_MphConfigCnf;
|
||||
case GsmL1_PrimId_MphMeasureReq: return GsmL1_PrimId_MphMeasureCnf;
|
||||
default: return -1; // Weak
|
||||
}
|
||||
}
|
||||
|
||||
enum l1prim_type oc2gbts_get_sysprim_type(Oc2g_PrimId_t id)
|
||||
{
|
||||
switch (id) {
|
||||
case Oc2g_PrimId_SystemInfoReq: return L1P_T_REQ;
|
||||
case Oc2g_PrimId_SystemInfoCnf: return L1P_T_CONF;
|
||||
case Oc2g_PrimId_SystemFailureInd: return L1P_T_IND;
|
||||
case Oc2g_PrimId_ActivateRfReq: return L1P_T_REQ;
|
||||
case Oc2g_PrimId_ActivateRfCnf: return L1P_T_CONF;
|
||||
case Oc2g_PrimId_DeactivateRfReq: return L1P_T_REQ;
|
||||
case Oc2g_PrimId_DeactivateRfCnf: return L1P_T_CONF;
|
||||
case Oc2g_PrimId_SetTraceFlagsReq: return L1P_T_REQ;
|
||||
case Oc2g_PrimId_Layer1ResetReq: return L1P_T_REQ;
|
||||
case Oc2g_PrimId_Layer1ResetCnf: return L1P_T_CONF;
|
||||
case Oc2g_PrimId_SetCalibTblReq: return L1P_T_REQ;
|
||||
case Oc2g_PrimId_SetCalibTblCnf: return L1P_T_CONF;
|
||||
case Oc2g_PrimId_MuteRfReq: return L1P_T_REQ;
|
||||
case Oc2g_PrimId_MuteRfCnf: return L1P_T_CONF;
|
||||
case Oc2g_PrimId_SetRxAttenReq: return L1P_T_REQ;
|
||||
case Oc2g_PrimId_SetRxAttenCnf: return L1P_T_CONF;
|
||||
case Oc2g_PrimId_IsAliveReq: return L1P_T_REQ;
|
||||
case Oc2g_PrimId_IsAliveCnf: return L1P_T_CONF;
|
||||
case Oc2g_PrimId_SetMaxCellSizeReq: return L1P_T_REQ;
|
||||
case Oc2g_PrimId_SetMaxCellSizeCnf: return L1P_T_CONF;
|
||||
case Oc2g_PrimId_SetC0IdleSlotPowerReductionReq: return L1P_T_REQ;
|
||||
case Oc2g_PrimId_SetC0IdleSlotPowerReductionCnf: return L1P_T_CONF;
|
||||
default: return L1P_T_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
const struct value_string oc2gbts_sysprim_names[Oc2g_PrimId_NUM+1] = {
|
||||
{ Oc2g_PrimId_SystemInfoReq, "SYSTEM-INFO.req" },
|
||||
{ Oc2g_PrimId_SystemInfoCnf, "SYSTEM-INFO.conf" },
|
||||
{ Oc2g_PrimId_SystemFailureInd, "SYSTEM-FAILURE.ind" },
|
||||
{ Oc2g_PrimId_ActivateRfReq, "ACTIVATE-RF.req" },
|
||||
{ Oc2g_PrimId_ActivateRfCnf, "ACTIVATE-RF.conf" },
|
||||
{ Oc2g_PrimId_DeactivateRfReq, "DEACTIVATE-RF.req" },
|
||||
{ Oc2g_PrimId_DeactivateRfCnf, "DEACTIVATE-RF.conf" },
|
||||
{ Oc2g_PrimId_SetTraceFlagsReq, "SET-TRACE-FLAGS.req" },
|
||||
{ Oc2g_PrimId_Layer1ResetReq, "LAYER1-RESET.req" },
|
||||
{ Oc2g_PrimId_Layer1ResetCnf, "LAYER1-RESET.conf" },
|
||||
{ Oc2g_PrimId_SetCalibTblReq, "SET-CALIB.req" },
|
||||
{ Oc2g_PrimId_SetCalibTblCnf, "SET-CALIB.cnf" },
|
||||
{ Oc2g_PrimId_MuteRfReq, "MUTE-RF.req" },
|
||||
{ Oc2g_PrimId_MuteRfCnf, "MUTE-RF.cnf" },
|
||||
{ Oc2g_PrimId_SetRxAttenReq, "SET-RX-ATTEN.req" },
|
||||
{ Oc2g_PrimId_SetRxAttenCnf, "SET-RX-ATTEN-CNF.cnf" },
|
||||
{ Oc2g_PrimId_IsAliveReq, "IS-ALIVE.req" },
|
||||
{ Oc2g_PrimId_IsAliveCnf, "IS-ALIVE-CNF.cnf" },
|
||||
{ Oc2g_PrimId_SetMaxCellSizeReq, "SET-MAX-CELL-SIZE.req" },
|
||||
{ Oc2g_PrimId_SetMaxCellSizeCnf, "SET-MAX-CELL-SIZE.cnf" },
|
||||
{ Oc2g_PrimId_SetC0IdleSlotPowerReductionReq, "SET-C0-IDLE-PWR-RED.req" },
|
||||
{ Oc2g_PrimId_SetC0IdleSlotPowerReductionCnf, "SET-C0-IDLE-PWR-RED.cnf" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
Oc2g_PrimId_t oc2gbts_get_sysprim_conf(Oc2g_PrimId_t id)
|
||||
{
|
||||
switch (id) {
|
||||
case Oc2g_PrimId_SystemInfoReq: return Oc2g_PrimId_SystemInfoCnf;
|
||||
case Oc2g_PrimId_ActivateRfReq: return Oc2g_PrimId_ActivateRfCnf;
|
||||
case Oc2g_PrimId_DeactivateRfReq: return Oc2g_PrimId_DeactivateRfCnf;
|
||||
case Oc2g_PrimId_Layer1ResetReq: return Oc2g_PrimId_Layer1ResetCnf;
|
||||
case Oc2g_PrimId_SetCalibTblReq: return Oc2g_PrimId_SetCalibTblCnf;
|
||||
case Oc2g_PrimId_MuteRfReq: return Oc2g_PrimId_MuteRfCnf;
|
||||
case Oc2g_PrimId_SetRxAttenReq: return Oc2g_PrimId_SetRxAttenCnf;
|
||||
case Oc2g_PrimId_IsAliveReq: return Oc2g_PrimId_IsAliveCnf;
|
||||
case Oc2g_PrimId_SetMaxCellSizeReq: return Oc2g_PrimId_SetMaxCellSizeCnf;
|
||||
case Oc2g_PrimId_SetC0IdleSlotPowerReductionReq: return Oc2g_PrimId_SetC0IdleSlotPowerReductionCnf;
|
||||
default: return -1; // Weak
|
||||
}
|
||||
}
|
||||
|
||||
const struct value_string oc2gbts_l1sapi_names[GsmL1_Sapi_NUM+1] = {
|
||||
{ GsmL1_Sapi_Idle, "IDLE" },
|
||||
{ GsmL1_Sapi_Fcch, "FCCH" },
|
||||
{ GsmL1_Sapi_Sch, "SCH" },
|
||||
{ GsmL1_Sapi_Sacch, "SACCH" },
|
||||
{ GsmL1_Sapi_Sdcch, "SDCCH" },
|
||||
{ GsmL1_Sapi_Bcch, "BCCH" },
|
||||
{ GsmL1_Sapi_Pch, "PCH" },
|
||||
{ GsmL1_Sapi_Agch, "AGCH" },
|
||||
{ GsmL1_Sapi_Cbch, "CBCH" },
|
||||
{ GsmL1_Sapi_Rach, "RACH" },
|
||||
{ GsmL1_Sapi_TchF, "TCH/F" },
|
||||
{ GsmL1_Sapi_FacchF, "FACCH/F" },
|
||||
{ GsmL1_Sapi_TchH, "TCH/H" },
|
||||
{ GsmL1_Sapi_FacchH, "FACCH/H" },
|
||||
{ GsmL1_Sapi_Nch, "NCH" },
|
||||
{ GsmL1_Sapi_Pdtch, "PDTCH" },
|
||||
{ GsmL1_Sapi_Pacch, "PACCH" },
|
||||
{ GsmL1_Sapi_Pbcch, "PBCCH" },
|
||||
{ GsmL1_Sapi_Pagch, "PAGCH" },
|
||||
{ GsmL1_Sapi_Ppch, "PPCH" },
|
||||
{ GsmL1_Sapi_Pnch, "PNCH" },
|
||||
{ GsmL1_Sapi_Ptcch, "PTCCH" },
|
||||
{ GsmL1_Sapi_Prach, "PRACH" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string oc2gbts_l1status_names[GSML1_STATUS_NUM+1] = {
|
||||
{ GsmL1_Status_Success, "Success" },
|
||||
{ GsmL1_Status_Generic, "Generic error" },
|
||||
{ GsmL1_Status_NoMemory, "Not enough memory" },
|
||||
{ GsmL1_Status_Timeout, "Timeout" },
|
||||
{ GsmL1_Status_InvalidParam, "Invalid parameter" },
|
||||
{ GsmL1_Status_Busy, "Resource busy" },
|
||||
{ GsmL1_Status_NoRessource, "No more resources" },
|
||||
{ GsmL1_Status_Uninitialized, "Trying to use uninitialized resource" },
|
||||
{ GsmL1_Status_NullInterface, "Trying to call a NULL interface" },
|
||||
{ GsmL1_Status_NullFctnPtr, "Trying to call a NULL function ptr" },
|
||||
{ GsmL1_Status_BadCrc, "Bad CRC" },
|
||||
{ GsmL1_Status_BadUsf, "Bad USF" },
|
||||
{ GsmL1_Status_InvalidCPS, "Invalid CPS field" },
|
||||
{ GsmL1_Status_UnexpectedBurst, "Unexpected burst" },
|
||||
{ GsmL1_Status_UnavailCodec, "AMR codec is unavailable" },
|
||||
{ GsmL1_Status_CriticalError, "Critical error" },
|
||||
{ GsmL1_Status_OverheatError, "Overheat error" },
|
||||
{ GsmL1_Status_DeviceError, "Device error" },
|
||||
{ GsmL1_Status_FacchError, "FACCH / TCH order error" },
|
||||
{ GsmL1_Status_AlreadyDeactivated, "Lchan already deactivated" },
|
||||
{ GsmL1_Status_TxBurstFifoOvrn, "FIFO overrun" },
|
||||
{ GsmL1_Status_TxBurstFifoUndr, "FIFO underrun" },
|
||||
{ GsmL1_Status_NotSynchronized, "Not synchronized" },
|
||||
{ GsmL1_Status_Unsupported, "Unsupported feature" },
|
||||
{ GsmL1_Status_ClockError, "System clock error" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string oc2gbts_tracef_names[29] = {
|
||||
{ DBG_DEBUG, "DEBUG" },
|
||||
{ DBG_L1WARNING, "L1_WARNING" },
|
||||
{ DBG_ERROR, "ERROR" },
|
||||
{ DBG_L1RXMSG, "L1_RX_MSG" },
|
||||
{ DBG_L1RXMSGBYTE, "L1_RX_MSG_BYTE" },
|
||||
{ DBG_L1TXMSG, "L1_TX_MSG" },
|
||||
{ DBG_L1TXMSGBYTE, "L1_TX_MSG_BYTE" },
|
||||
{ DBG_MPHCNF, "MPH_CNF" },
|
||||
{ DBG_MPHIND, "MPH_IND" },
|
||||
{ DBG_MPHREQ, "MPH_REQ" },
|
||||
{ DBG_PHIND, "PH_IND" },
|
||||
{ DBG_PHREQ, "PH_REQ" },
|
||||
{ DBG_PHYRF, "PHY_RF" },
|
||||
{ DBG_PHYRFMSGBYTE, "PHY_MSG_BYTE" },
|
||||
{ DBG_MODE, "MODE" },
|
||||
{ DBG_TDMAINFO, "TDMA_INFO" },
|
||||
{ DBG_BADCRC, "BAD_CRC" },
|
||||
{ DBG_PHINDBYTE, "PH_IND_BYTE" },
|
||||
{ DBG_PHREQBYTE, "PH_REQ_BYTE" },
|
||||
{ DBG_DEVICEMSG, "DEVICE_MSG" },
|
||||
{ DBG_RACHINFO, "RACH_INFO" },
|
||||
{ DBG_LOGCHINFO, "LOG_CH_INFO" },
|
||||
{ DBG_MEMORY, "MEMORY" },
|
||||
{ DBG_PROFILING, "PROFILING" },
|
||||
{ DBG_TESTCOMMENT, "TEST_COMMENT" },
|
||||
{ DBG_TEST, "TEST" },
|
||||
{ DBG_STATUS, "STATUS" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string oc2gbts_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 oc2gbts_tch_pl_names[] = {
|
||||
{ GsmL1_TchPlType_NA, "N/A" },
|
||||
{ GsmL1_TchPlType_Fr, "FR" },
|
||||
{ GsmL1_TchPlType_Hr, "HR" },
|
||||
{ GsmL1_TchPlType_Efr, "EFR" },
|
||||
{ GsmL1_TchPlType_Amr, "AMR(IF2)" },
|
||||
{ GsmL1_TchPlType_Amr_SidBad, "AMR(SID BAD)" },
|
||||
{ GsmL1_TchPlType_Amr_Onset, "AMR(ONSET)" },
|
||||
{ GsmL1_TchPlType_Amr_Ratscch, "AMR(RATSCCH)" },
|
||||
{ GsmL1_TchPlType_Amr_SidUpdateInH, "AMR(SID_UPDATE INH)" },
|
||||
{ GsmL1_TchPlType_Amr_SidFirstP1, "AMR(SID_FIRST P1)" },
|
||||
{ GsmL1_TchPlType_Amr_SidFirstP2, "AMR(SID_FIRST P2)" },
|
||||
{ GsmL1_TchPlType_Amr_SidFirstInH, "AMR(SID_FIRST INH)" },
|
||||
{ GsmL1_TchPlType_Amr_RatscchMarker, "AMR(RATSCCH MARK)" },
|
||||
{ GsmL1_TchPlType_Amr_RatscchData, "AMR(RATSCCH DATA)" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string oc2gbts_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 oc2gbts_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
|
||||
};
|
||||
|
||||
const struct value_string oc2gbts_rsl_ho_causes[] = {
|
||||
{ IPAC_HO_RQD_CAUSE_L_RXLEV_UL_H, "L_RXLEV_UL_H" },
|
||||
{ IPAC_HO_RQD_CAUSE_L_RXLEV_DL_H, "L_RXLEV_DL_H" },
|
||||
{ IPAC_HO_RQD_CAUSE_L_RXQUAL_UL_H, "L_RXQUAL_UL_H" },
|
||||
{ IPAC_HO_RQD_CAUSE_L_RXQUAL_DL_H, "L_RXQUAL_DL_H" },
|
||||
{ IPAC_HO_RQD_CAUSE_RXLEV_UL_IH, "RXLEV_UL_IH" },
|
||||
{ IPAC_HO_RQD_CAUSE_RXLEV_DL_IH, "RXLEV_DL_IH" },
|
||||
{ IPAC_HO_RQD_CAUSE_MAX_MS_RANGE, "MAX_MS_RANGE" },
|
||||
{ IPAC_HO_RQD_CAUSE_POWER_BUDGET, "POWER_BUDGET" },
|
||||
{ IPAC_HO_RQD_CAUSE_ENQUIRY, "ENQUIRY" },
|
||||
{ IPAC_HO_RQD_CAUSE_ENQUIRY_FAILED, "ENQUIRY_FAILED" },
|
||||
{ 0, NULL }
|
||||
};
|
|
@ -0,0 +1,92 @@
|
|||
#ifndef OC2GBTS_H
|
||||
#define OC2GBTS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/protocol/gsm_08_58.h>
|
||||
|
||||
#include <nrw/oc2g/oc2g.h>
|
||||
#include <nrw/oc2g/gsml1const.h>
|
||||
|
||||
/*
|
||||
* Depending on the firmware version either GsmL1_Prim_t or Oc2g_Prim_t
|
||||
* is the bigger struct. For earlier firmware versions the GsmL1_Prim_t was the
|
||||
* bigger struct.
|
||||
*/
|
||||
#define OC2GBTS_PRIM_SIZE \
|
||||
(OSMO_MAX(sizeof(Oc2g_Prim_t), sizeof(GsmL1_Prim_t)) + 128)
|
||||
|
||||
enum l1prim_type {
|
||||
L1P_T_INVALID, /* this must be 0 to detect uninitialized elements */
|
||||
L1P_T_REQ,
|
||||
L1P_T_CONF,
|
||||
L1P_T_IND,
|
||||
};
|
||||
|
||||
enum oc2g_pedestal_mode{
|
||||
OC2G_PEDESTAL_OFF = 0,
|
||||
OC2G_PEDESTAL_ON,
|
||||
};
|
||||
|
||||
enum oc2g_led_control_mode{
|
||||
OC2G_LED_CONTROL_BTS = 0,
|
||||
OC2G_LED_CONTROL_EXT,
|
||||
};
|
||||
|
||||
enum oc2g_auto_pwr_adjust_mode{
|
||||
OC2G_TX_PWR_ADJ_NONE = 0,
|
||||
OC2G_TX_PWR_ADJ_AUTO,
|
||||
};
|
||||
|
||||
enum l1prim_type oc2gbts_get_l1prim_type(GsmL1_PrimId_t id);
|
||||
const struct value_string oc2gbts_l1prim_names[GsmL1_PrimId_NUM+1];
|
||||
GsmL1_PrimId_t oc2gbts_get_l1prim_conf(GsmL1_PrimId_t id);
|
||||
|
||||
enum l1prim_type oc2gbts_get_sysprim_type(Oc2g_PrimId_t id);
|
||||
const struct value_string oc2gbts_sysprim_names[Oc2g_PrimId_NUM+1];
|
||||
Oc2g_PrimId_t oc2gbts_get_sysprim_conf(Oc2g_PrimId_t id);
|
||||
|
||||
const struct value_string oc2gbts_l1sapi_names[GsmL1_Sapi_NUM+1];
|
||||
const struct value_string oc2gbts_l1status_names[GSML1_STATUS_NUM+1];
|
||||
|
||||
const struct value_string oc2gbts_tracef_names[29];
|
||||
const struct value_string oc2gbts_tracef_docs[29];
|
||||
|
||||
const struct value_string oc2gbts_tch_pl_names[15];
|
||||
|
||||
const struct value_string oc2gbts_clksrc_names[10];
|
||||
|
||||
const struct value_string oc2gbts_dir_names[6];
|
||||
|
||||
const struct value_string oc2gbts_rsl_ho_causes[IPAC_HO_RQD_CAUSE_MAX];
|
||||
|
||||
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];
|
||||
|
||||
/* OC2G default parameters */
|
||||
#define OC2G_BTS_MAX_CELL_SIZE_DEFAULT 166 /* 166 qbits is default value */
|
||||
#define OC2G_BTS_PEDESTAL_MODE_DEFAULT 0 /* Unused TS is off by default */
|
||||
#define OC2G_BTS_LED_CTRL_MODE_DEFAULT 0 /* LED is controlled by BTS by default */
|
||||
#define OC2G_BTS_DSP_ALIVE_TMR_DEFAULT 5 /* Default DSP alive timer is 5 seconds */
|
||||
#define OC2G_BTS_TX_PWR_ADJ_DEFAULT 0 /* Default Tx power auto adjustment is none */
|
||||
#define OC2G_BTS_TX_RED_PWR_8PSK_DEFAULT 0 /* Default 8-PSK maximum power level is 0 dB */
|
||||
#define OC2G_BTS_RTP_DRIFT_THRES_DEFAULT 0 /* Default RTP drift threshold is 0 ms (disabled) */
|
||||
#define OC2G_BTS_TX_C0_IDLE_RED_PWR_DEFAULT 0 /* Default C0 idle slot reduction power level is 0 dB */
|
||||
|
||||
#endif /* OC2GBTS_H */
|
|
@ -0,0 +1,733 @@
|
|||
/* VTY interface for NuRAN Wireless OC-2G */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
* Copyright (C) 2016 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2012,2013 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
|
||||
#include <osmocom/ctrl/control_cmd.h>
|
||||
#include <osmo-bts/signal.h>
|
||||
#include <osmo-bts/oml.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/phy_link.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/vty.h>
|
||||
#include <osmo-bts/rsl.h>
|
||||
|
||||
#include "oc2gbts.h"
|
||||
#include "l1_if.h"
|
||||
#include "utils.h"
|
||||
|
||||
extern int lchan_activate(struct gsm_lchan *lchan);
|
||||
extern int rsl_tx_preproc_meas_res(struct gsm_lchan *lchan);
|
||||
|
||||
#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 const struct value_string oc2g_pedestal_mode_strs[] = {
|
||||
{ OC2G_PEDESTAL_OFF, "off" },
|
||||
{ OC2G_PEDESTAL_ON, "on" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const struct value_string oc2g_led_mode_strs[] = {
|
||||
{ OC2G_LED_CONTROL_BTS, "bts" },
|
||||
{ OC2G_LED_CONTROL_EXT, "external" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const struct value_string oc2g_auto_adj_pwr_strs[] = {
|
||||
{ OC2G_TX_PWR_ADJ_NONE, "none" },
|
||||
{ OC2G_TX_PWR_ADJ_AUTO, "auto" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/* configuration */
|
||||
|
||||
DEFUN(cfg_phy_cal_path, cfg_phy_cal_path_cmd,
|
||||
"trx-calibration-path PATH",
|
||||
"Set the path name to TRX calibration data\n" "Path name\n")
|
||||
{
|
||||
struct phy_instance *pinst = vty->index;
|
||||
|
||||
if (pinst->u.oc2g.calib_path)
|
||||
talloc_free(pinst->u.oc2g.calib_path);
|
||||
|
||||
pinst->u.oc2g.calib_path = talloc_strdup(pinst, argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_phy_dsp_trace_f, cfg_phy_dsp_trace_f_cmd,
|
||||
"HIDDEN", TRX_STR)
|
||||
{
|
||||
struct phy_instance *pinst = vty->index;
|
||||
unsigned int flag;
|
||||
|
||||
flag = get_string_value(oc2gbts_tracef_names, argv[1]);
|
||||
pinst->u.oc2g.dsp_trace_f |= ~flag;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_phy_no_dsp_trace_f, cfg_phy_no_dsp_trace_f_cmd,
|
||||
"HIDDEN", NO_STR TRX_STR)
|
||||
{
|
||||
struct phy_instance *pinst = vty->index;
|
||||
unsigned int flag;
|
||||
|
||||
flag = get_string_value(oc2gbts_tracef_names, argv[1]);
|
||||
pinst->u.oc2g.dsp_trace_f &= ~flag;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* runtime */
|
||||
|
||||
DEFUN(show_dsp_trace_f, show_dsp_trace_f_cmd,
|
||||
"show trx <0-0> dsp-trace-flags",
|
||||
SHOW_TRX_STR "Display the current setting of the DSP trace flags")
|
||||
{
|
||||
int trx_nr = atoi(argv[0]);
|
||||
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
|
||||
struct oc2gl1_hdl *fl1h;
|
||||
int i;
|
||||
|
||||
if (!trx)
|
||||
return CMD_WARNING;
|
||||
|
||||
fl1h = trx_oc2gl1_hdl(trx);
|
||||
|
||||
vty_out(vty, "Oc2g L1 DSP trace flags:%s", VTY_NEWLINE);
|
||||
for (i = 0; i < ARRAY_SIZE(oc2gbts_tracef_names); i++) {
|
||||
const char *endis;
|
||||
|
||||
if (oc2gbts_tracef_names[i].value == 0 &&
|
||||
oc2gbts_tracef_names[i].str == NULL)
|
||||
break;
|
||||
|
||||
if (fl1h->dsp_trace_f & oc2gbts_tracef_names[i].value)
|
||||
endis = "enabled";
|
||||
else
|
||||
endis = "disabled";
|
||||
|
||||
vty_out(vty, "DSP Trace %-15s %s%s",
|
||||
oc2gbts_tracef_names[i].str, endis,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
DEFUN(dsp_trace_f, dsp_trace_f_cmd, "HIDDEN", TRX_STR)
|
||||
{
|
||||
int phy_nr = atoi(argv[0]);
|
||||
struct phy_instance *pinst;
|
||||
struct oc2gl1_hdl *fl1h;
|
||||
unsigned int flag ;
|
||||
|
||||
pinst = vty_get_phy_instance(vty, phy_nr, 0);
|
||||
if (!pinst)
|
||||
return CMD_WARNING;
|
||||
|
||||
fl1h = pinst->u.oc2g.hdl;
|
||||
flag = get_string_value(oc2gbts_tracef_names, argv[1]);
|
||||
l1if_set_trace_flags(fl1h, fl1h->dsp_trace_f | flag);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(no_dsp_trace_f, no_dsp_trace_f_cmd, "HIDDEN", NO_STR TRX_STR)
|
||||
{
|
||||
int phy_nr = atoi(argv[0]);
|
||||
struct phy_instance *pinst;
|
||||
struct oc2gl1_hdl *fl1h;
|
||||
unsigned int flag ;
|
||||
|
||||
pinst = vty_get_phy_instance(vty, phy_nr, 0);
|
||||
if (!pinst)
|
||||
return CMD_WARNING;
|
||||
|
||||
fl1h = pinst->u.oc2g.hdl;
|
||||
flag = get_string_value(oc2gbts_tracef_names, argv[1]);
|
||||
l1if_set_trace_flags(fl1h, fl1h->dsp_trace_f & ~flag);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_sys_info, show_sys_info_cmd,
|
||||
"show phy <0-0> instance <0-0> system-information",
|
||||
SHOW_TRX_STR "Display information about system\n")
|
||||
{
|
||||
int phy_nr = atoi(argv[0]);
|
||||
int inst_nr = atoi(argv[1]);
|
||||
struct phy_link *plink = phy_link_by_num(phy_nr);
|
||||
struct phy_instance *pinst;
|
||||
struct oc2gl1_hdl *fl1h;
|
||||
int i;
|
||||
|
||||
if (!plink) {
|
||||
vty_out(vty, "Cannot find PHY link %u%s",
|
||||
phy_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
pinst = phy_instance_by_num(plink, inst_nr);
|
||||
if (!plink) {
|
||||
vty_out(vty, "Cannot find PHY instance %u%s",
|
||||
phy_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
fl1h = pinst->u.oc2g.hdl;
|
||||
|
||||
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);
|
||||
vty_out(vty, "Min Tx Power: %d dBm%s", fl1h->phy_inst->u.oc2g.minTxPower, VTY_NEWLINE);
|
||||
vty_out(vty, "Max Tx Power: %d dBm%s", fl1h->phy_inst->u.oc2g.maxTxPower, 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 nr <0-1> tx-power <-110-100>",
|
||||
TRX_STR
|
||||
"TRX number \n"
|
||||
"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);
|
||||
|
||||
power_ramp_start(trx, to_mdB(power), 1);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_nominal_power, cfg_trx_nominal_power_cmd,
|
||||
"nominal-tx-power <0-40>",
|
||||
"Set the nominal transmit output power in dBm\n"
|
||||
"Nominal transmit output power level in dBm\n")
|
||||
{
|
||||
int nominal_power = atoi(argv[0]);
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
|
||||
if (( nominal_power > 40 ) || ( nominal_power < 0 )) {
|
||||
vty_out(vty, "Nominal Tx power level must be between 0 and 40 dBm (%d) %s",
|
||||
nominal_power, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
trx->nominal_power = nominal_power;
|
||||
trx->power_params.trx_p_max_out_mdBm = to_mdB(nominal_power);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_phy_max_cell_size, cfg_phy_max_cell_size_cmd,
|
||||
"max-cell-size <0-166>",
|
||||
"Set the maximum cell size in qbits\n")
|
||||
{
|
||||
struct phy_instance *pinst = vty->index;
|
||||
int cell_size = (uint8_t)atoi(argv[0]);
|
||||
|
||||
if (( cell_size > 166 ) || ( cell_size < 0 )) {
|
||||
vty_out(vty, "Max cell size must be between 0 and 166 qbits (%d) %s",
|
||||
cell_size, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
pinst->u.oc2g.max_cell_size = (uint8_t)cell_size;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_phy_pedestal_mode, cfg_phy_pedestal_mode_cmd,
|
||||
"pedestal-mode (on|off)",
|
||||
"Set unused time-slot transmission in pedestal mode\n"
|
||||
"Transmission pedestal mode can be (off, on)\n")
|
||||
{
|
||||
struct phy_instance *pinst = vty->index;
|
||||
int val = get_string_value(oc2g_pedestal_mode_strs, argv[0]);
|
||||
|
||||
if((val < OC2G_PEDESTAL_OFF) || (val > OC2G_PEDESTAL_ON)) {
|
||||
vty_out(vty, "Invalid unused time-slot transmission mode %d%s", val, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
pinst->u.oc2g.pedestal_mode = (uint8_t)val;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_phy_dsp_alive_timer, cfg_phy_dsp_alive_timer_cmd,
|
||||
"dsp-alive-period <0-60>",
|
||||
"Set DSP alive timer period in second\n")
|
||||
{
|
||||
struct phy_instance *pinst = vty->index;
|
||||
uint8_t period = (uint8_t)atoi(argv[0]);
|
||||
|
||||
if (( period > 60 ) || ( period < 0 )) {
|
||||
vty_out(vty, "DSP heart beat alive timer period must be between 0 and 60 seconds (%d) %s",
|
||||
period, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
pinst->u.oc2g.dsp_alive_period = period;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_phy_auto_tx_pwr_adj, cfg_phy_auto_tx_pwr_adj_cmd,
|
||||
"pwr-adj-mode (none|auto)",
|
||||
"Set output power adjustment mode\n")
|
||||
{
|
||||
struct phy_instance *pinst = vty->index;
|
||||
int val = get_string_value(oc2g_auto_adj_pwr_strs, argv[0]);
|
||||
|
||||
if((val < OC2G_TX_PWR_ADJ_NONE) || (val > OC2G_TX_PWR_ADJ_AUTO)) {
|
||||
vty_out(vty, "Invalid output power adjustment mode %d%s", val, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
pinst->u.oc2g.tx_pwr_adj_mode = (uint8_t)val;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_phy_tx_red_pwr_8psk, cfg_phy_tx_red_pwr_8psk_cmd,
|
||||
"tx-red-pwr-8psk <0-40>",
|
||||
"Set reduction output power for 8-PSK scheme in dB unit\n")
|
||||
{
|
||||
struct phy_instance *pinst = vty->index;
|
||||
int val = atoi(argv[0]);
|
||||
|
||||
if ((val > 40) || (val < 0)) {
|
||||
vty_out(vty, "Reduction Tx power level must be between 0 and 40 dB (%d) %s",
|
||||
val, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
pinst->u.oc2g.tx_pwr_red_8psk = (uint8_t)val;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_phy_c0_idle_red_pwr, cfg_phy_c0_idle_red_pwr_cmd,
|
||||
"c0-idle-red-pwr <0-40>",
|
||||
"Set reduction output power for C0 idle slot in dB unit\n")
|
||||
{
|
||||
struct phy_instance *pinst = vty->index;
|
||||
int val = atoi(argv[0]);
|
||||
|
||||
if ((val > 40) || (val < 0)) {
|
||||
vty_out(vty, "Reduction Tx power level must be between 0 and 40 dB (%d) %s",
|
||||
val, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
pinst->u.oc2g.tx_c0_idle_pwr_red = (uint8_t)val;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(trigger_ho_cause, trigger_ho_cause_cmd, "HIDDEN", TRX_STR)
|
||||
{
|
||||
struct gsm_network *net = gsmnet_from_vty(vty);
|
||||
struct gsm_bts *bts;
|
||||
struct gsm_bts_trx *trx;
|
||||
struct gsm_bts_trx_ts *ts;
|
||||
struct gsm_lchan *lchan;
|
||||
int trx_nr, ts_nr, lchan_nr;
|
||||
uint8_t ho_cause;
|
||||
uint8_t old_ho_cause;
|
||||
|
||||
/* get BTS pointer */
|
||||
bts = gsm_bts_num(net, 0);
|
||||
if (!bts) {
|
||||
vty_out(vty, "Can not get BTS node %s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
/* get TRX pointer */
|
||||
if (argc >= 1) {
|
||||
trx_nr = atoi(argv[0]);
|
||||
if (trx_nr >= bts->num_trx) {
|
||||
vty_out(vty, "%% can't find TRX %s%s", argv[0],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
trx = gsm_bts_trx_num(bts, trx_nr);
|
||||
}
|
||||
/* get TS pointer */
|
||||
if (argc >= 2) {
|
||||
ts_nr = atoi(argv[1]);
|
||||
if (ts_nr >= TRX_NR_TS) {
|
||||
vty_out(vty, "%% can't find TS %s%s", argv[1],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
ts = &trx->ts[ts_nr];
|
||||
}
|
||||
/* get lchan pointer */
|
||||
if (argc >= 3) {
|
||||
lchan_nr = atoi(argv[2]);
|
||||
if (lchan_nr >= TS_MAX_LCHAN) {
|
||||
vty_out(vty, "%% can't find LCHAN %s%s", argv[2],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
lchan = &ts->lchan[lchan_nr];
|
||||
}
|
||||
|
||||
/* get HO cause */
|
||||
if (argc >= 4) {
|
||||
ho_cause = get_string_value(oc2gbts_rsl_ho_causes, argv[3]);
|
||||
if (ho_cause >= IPAC_HO_RQD_CAUSE_MAX) {
|
||||
vty_out(vty, "%% can't find valid HO cause %s%s", argv[3],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
} else {
|
||||
vty_out(vty, "%% HO cause is not provided %s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
/* TODO(oramadan) MERGE
|
||||
/* store recorded HO causes * /
|
||||
old_ho_cause = lchan->meas_preproc.rec_ho_causes;
|
||||
|
||||
/* Apply new HO causes * /
|
||||
lchan->meas_preproc.rec_ho_causes = 1 << (ho_cause - 1);
|
||||
|
||||
/* Send measuremnt report to BSC * /
|
||||
rsl_tx_preproc_meas_res(lchan);
|
||||
|
||||
/* restore HO cause * /
|
||||
lchan->meas_preproc.rec_ho_causes = old_ho_cause;
|
||||
*/
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* TODO(oramadan) MERGE
|
||||
DEFUN(cfg_bts_rtp_drift_threshold, cfg_bts_rtp_drift_threshold_cmd,
|
||||
"rtp-drift-threshold <0-10000>",
|
||||
"RTP parameters\n"
|
||||
"RTP timestamp drift threshold in ms\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
btsb->oc2g.rtp_drift_thres_ms = (unsigned int) atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
*/
|
||||
|
||||
void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts)
|
||||
{
|
||||
/* TODO(oramadan) MERGE
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
vty_out(vty, " led-control-mode %s%s",
|
||||
get_value_string(oc2g_led_mode_strs, btsb->oc2g.led_ctrl_mode), VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " rtp-drift-threshold %d%s",
|
||||
btsb->oc2g.rtp_drift_thres_ms, VTY_NEWLINE);
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
|
||||
{
|
||||
vty_out(vty, " nominal-tx-power %d%s", trx->nominal_power,VTY_NEWLINE);
|
||||
}
|
||||
|
||||
void bts_model_config_write_phy(struct vty *vty, struct phy_link *plink)
|
||||
{
|
||||
}
|
||||
|
||||
void bts_model_config_write_phy_inst(struct vty *vty, struct phy_instance *pinst)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (pinst->u.oc2g.dsp_trace_f & (1 << i)) {
|
||||
const char *name;
|
||||
name = get_value_string(oc2gbts_tracef_names, (1 << i));
|
||||
vty_out(vty, " dsp-trace-flag %s%s", name,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
if (pinst->u.oc2g.calib_path)
|
||||
vty_out(vty, " trx-calibration-path %s%s",
|
||||
pinst->u.oc2g.calib_path, VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " max-cell-size %d%s",
|
||||
pinst->u.oc2g.max_cell_size, VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " pedestal-mode %s%s",
|
||||
get_value_string(oc2g_pedestal_mode_strs, pinst->u.oc2g.pedestal_mode) , VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " dsp-alive-period %d%s",
|
||||
pinst->u.oc2g.dsp_alive_period, VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " pwr-adj-mode %s%s",
|
||||
get_value_string(oc2g_auto_adj_pwr_strs, pinst->u.oc2g.tx_pwr_adj_mode), VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " tx-red-pwr-8psk %d%s",
|
||||
pinst->u.oc2g.tx_pwr_red_8psk, VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " c0-idle-red-pwr %d%s",
|
||||
pinst->u.oc2g.tx_c0_idle_pwr_red, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
int bts_model_vty_init(struct gsm_bts *bts)
|
||||
{
|
||||
vty_bts = bts;
|
||||
|
||||
/* runtime-patch the command strings with debug levels */
|
||||
dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(bts, oc2gbts_tracef_names,
|
||||
"phy <0-1> dsp-trace-flag (",
|
||||
"|",")", VTY_DO_LOWER);
|
||||
dsp_trace_f_cmd.doc = vty_cmd_string_from_valstr(bts, oc2gbts_tracef_docs,
|
||||
TRX_STR DSP_TRACE_F_STR,
|
||||
"\n", "", 0);
|
||||
|
||||
no_dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(bts, oc2gbts_tracef_names,
|
||||
"no phy <0-1> dsp-trace-flag (",
|
||||
"|",")", VTY_DO_LOWER);
|
||||
no_dsp_trace_f_cmd.doc = vty_cmd_string_from_valstr(bts, oc2gbts_tracef_docs,
|
||||
NO_STR TRX_STR DSP_TRACE_F_STR,
|
||||
"\n", "", 0);
|
||||
|
||||
cfg_phy_dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(bts,
|
||||
oc2gbts_tracef_names,
|
||||
"dsp-trace-flag (",
|
||||
"|",")", VTY_DO_LOWER);
|
||||
cfg_phy_dsp_trace_f_cmd.doc = vty_cmd_string_from_valstr(bts,
|
||||
oc2gbts_tracef_docs,
|
||||
DSP_TRACE_F_STR,
|
||||
"\n", "", 0);
|
||||
|
||||
cfg_phy_no_dsp_trace_f_cmd.string = vty_cmd_string_from_valstr(bts,
|
||||
oc2gbts_tracef_names,
|
||||
"no dsp-trace-flag (",
|
||||
"|",")", VTY_DO_LOWER);
|
||||
cfg_phy_no_dsp_trace_f_cmd.doc = vty_cmd_string_from_valstr(bts,
|
||||
oc2gbts_tracef_docs,
|
||||
NO_STR DSP_TRACE_F_STR,
|
||||
"\n", "", 0);
|
||||
|
||||
trigger_ho_cause_cmd.string = vty_cmd_string_from_valstr(bts,
|
||||
oc2gbts_rsl_ho_causes,
|
||||
"trigger-ho-cause trx <0-1> ts <0-7> lchan <0-1> cause (",
|
||||
"|",")", VTY_DO_LOWER);
|
||||
|
||||
install_element_ve(&show_dsp_trace_f_cmd);
|
||||
install_element_ve(&show_sys_info_cmd);
|
||||
install_element_ve(&dsp_trace_f_cmd);
|
||||
install_element_ve(&no_dsp_trace_f_cmd);
|
||||
|
||||
install_element(ENABLE_NODE, &activate_lchan_cmd);
|
||||
install_element(ENABLE_NODE, &set_tx_power_cmd);
|
||||
|
||||
install_element(ENABLE_NODE, &loopback_cmd);
|
||||
install_element(ENABLE_NODE, &no_loopback_cmd);
|
||||
|
||||
install_element(ENABLE_NODE, &trigger_ho_cause_cmd);
|
||||
|
||||
install_element(BTS_NODE, &cfg_bts_auto_band_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_no_auto_band_cmd);
|
||||
/* TODO(oramadan) MERGE
|
||||
install_element(BTS_NODE, &cfg_bts_rtp_drift_threshold_cmd);
|
||||
*/
|
||||
|
||||
install_element(TRX_NODE, &cfg_trx_nominal_power_cmd);
|
||||
|
||||
install_element(PHY_INST_NODE, &cfg_phy_dsp_trace_f_cmd);
|
||||
install_element(PHY_INST_NODE, &cfg_phy_no_dsp_trace_f_cmd);
|
||||
install_element(PHY_INST_NODE, &cfg_phy_cal_path_cmd);
|
||||
install_element(PHY_INST_NODE, &cfg_phy_pedestal_mode_cmd);
|
||||
install_element(PHY_INST_NODE, &cfg_phy_max_cell_size_cmd);
|
||||
install_element(PHY_INST_NODE, &cfg_phy_dsp_alive_timer_cmd);
|
||||
install_element(PHY_INST_NODE, &cfg_phy_auto_tx_pwr_adj_cmd);
|
||||
install_element(PHY_INST_NODE, &cfg_phy_tx_red_pwr_8psk_cmd);
|
||||
install_element(PHY_INST_NODE, &cfg_phy_c0_idle_red_pwr_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO(oramadan) MERGE
|
||||
/* OC2G BTS control interface * /
|
||||
CTRL_CMD_DEFINE_WO_NOVRF(oc2g_oml_alert, "oc2g-oml-alert");
|
||||
static int set_oc2g_oml_alert(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct gsm_bts *bts = cmd->node;
|
||||
int cause = atoi(cmd->value);
|
||||
char *saveptr = NULL;
|
||||
|
||||
alarm_sig_data.mo = &bts->mo;
|
||||
cause = atoi(strtok_r(cmd->value, ",", &saveptr));
|
||||
alarm_sig_data.event_serverity = (cause >> 8) & 0x0F;
|
||||
alarm_sig_data.add_text = strtok_r(NULL, "\n", &saveptr);
|
||||
memcpy(alarm_sig_data.spare, &cause, sizeof(int));
|
||||
LOGP(DLCTRL, LOGL_NOTICE, "BTS received MGR alarm cause=%d, text=%s\n", cause, alarm_sig_data.add_text);
|
||||
|
||||
/* dispatch OML alarm signal * /
|
||||
osmo_signal_dispatch(SS_NM, S_NM_OML_BTS_MGR_ALARM, &alarm_sig_data);
|
||||
|
||||
/* return with same alarm cause to MGR rather than OK string* /
|
||||
cmd->reply = talloc_asprintf(cmd, "%d", cause);
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_WO_NOVRF(oc2g_oml_ceased, "oc2g-oml-ceased");
|
||||
static int set_oc2g_oml_ceased(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct gsm_bts *bts = cmd->node;
|
||||
int cause = atoi(cmd->value);
|
||||
char *saveptr = NULL;
|
||||
|
||||
alarm_sig_data.mo = &bts->mo;
|
||||
cause = atoi(strtok_r(cmd->value, ",", &saveptr));
|
||||
alarm_sig_data.add_text = strtok_r(NULL, "\n", &saveptr);
|
||||
memcpy(alarm_sig_data.spare, &cause, sizeof(int));
|
||||
LOGP(DLCTRL, LOGL_NOTICE, "BTS received MGR ceased alarm cause=%d, text=%s\n", cause, alarm_sig_data.add_text);
|
||||
|
||||
/* dispatch OML alarm signal * /
|
||||
osmo_signal_dispatch(SS_NM, S_NM_OML_BTS_MGR_CEASED_ALARM, &alarm_sig_data);
|
||||
*/
|
||||
|
||||
/* return with same alarm cause to MGR rather than OK string* /
|
||||
cmd->reply = talloc_asprintf(cmd, "%d", cause);
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
*/
|
||||
|
||||
int bts_model_ctrl_cmds_install(struct gsm_bts *bts)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
|
||||
/* TODO(oramadan) MERGE
|
||||
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_oc2g_oml_alert);
|
||||
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_oc2g_oml_ceased);
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,132 @@
|
|||
/* Beginnings of an OML router */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* (C) 2014 by sysmocom s.f.m.c. GmbH
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "oml_router.h"
|
||||
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/oml.h>
|
||||
#include <osmo-bts/msg_utils.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/select.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int oml_router_read_cb(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
msg = oml_msgb_alloc();
|
||||
if (!msg) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Failed to allocate oml msgb.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = recv(fd->fd, msg->tail, msg->data_len, 0);
|
||||
if (rc <= 0) {
|
||||
close(fd->fd);
|
||||
osmo_fd_unregister(fd);
|
||||
fd->fd = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
msg->l1h = msgb_put(msg, rc);
|
||||
rc = msg_verify_ipa_structure(msg);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_ERROR,
|
||||
"OML Router: Invalid IPA message rc(%d)\n", rc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = msg_verify_oml_structure(msg);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_ERROR,
|
||||
"OML Router: Invalid OML message rc(%d)\n", rc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* todo dispatch message */
|
||||
|
||||
err:
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int oml_router_accept_cb(struct osmo_fd *accept_fd, unsigned int what)
|
||||
{
|
||||
int fd;
|
||||
struct osmo_fd *read_fd = (struct osmo_fd *) accept_fd->data;
|
||||
|
||||
/* Accept only one connection at a time. De-register it */
|
||||
if (read_fd->fd > -1) {
|
||||
LOGP(DL1C, LOGL_NOTICE,
|
||||
"New OML router connection. Closing old one.\n");
|
||||
close(read_fd->fd);
|
||||
osmo_fd_unregister(read_fd);
|
||||
read_fd->fd = -1;
|
||||
}
|
||||
|
||||
fd = accept(accept_fd->fd, NULL, NULL);
|
||||
if (fd < 0) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Failed to accept. errno: %s.\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
read_fd->fd = fd;
|
||||
if (osmo_fd_register(read_fd) != 0) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Registering the read fd failed.\n");
|
||||
close(fd);
|
||||
read_fd->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int oml_router_init(struct gsm_bts *bts, const char *path,
|
||||
struct osmo_fd *accept_fd, struct osmo_fd *read_fd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
memset(accept_fd, 0, sizeof(*accept_fd));
|
||||
memset(read_fd, 0, sizeof(*read_fd));
|
||||
|
||||
accept_fd->cb = oml_router_accept_cb;
|
||||
accept_fd->data = read_fd;
|
||||
|
||||
read_fd->cb = oml_router_read_cb;
|
||||
read_fd->data = bts;
|
||||
read_fd->when = BSC_FD_READ;
|
||||
read_fd->fd = -1;
|
||||
|
||||
rc = osmo_sock_unix_init_ofd(accept_fd, SOCK_SEQPACKET, 0,
|
||||
path,
|
||||
OSMO_SOCK_F_BIND | OSMO_SOCK_F_NONBLOCK);
|
||||
return rc;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
struct gsm_bts;
|
||||
struct osmo_fd;
|
||||
|
||||
/**
|
||||
* The default path oc2gbts will listen for incoming
|
||||
* registrations for OML routing and sending.
|
||||
*/
|
||||
#define OML_ROUTER_PATH "/var/run/oc2gbts_oml_router"
|
||||
|
||||
|
||||
int oml_router_init(struct gsm_bts *bts, const char *path, struct osmo_fd *accept, struct osmo_fd *read);
|
|
@ -0,0 +1,545 @@
|
|||
/* Traffic channel support for NuRAN Wireless OC-2G BTS L1 */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* (C) 2011-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 <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/msg_utils.h>
|
||||
#include <osmo-bts/measurement.h>
|
||||
#include <osmo-bts/amr.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
#include <osmo-bts/dtx_dl_amr_fsm.h>
|
||||
|
||||
#include <nrw/oc2g/oc2g.h>
|
||||
#include <nrw/oc2g/gsml1prim.h>
|
||||
#include <nrw/oc2g/gsml1const.h>
|
||||
#include <nrw/oc2g/gsml1types.h>
|
||||
|
||||
#include "oc2gbts.h"
|
||||
#include "l1_if.h"
|
||||
|
||||
static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len,
|
||||
struct gsm_lchan *lchan)
|
||||
{
|
||||
struct msgb *msg;
|
||||
uint8_t *cur;
|
||||
|
||||
msg = msgb_alloc_headroom(1024, 128, "L1P-to-RTP");
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
/* new L1 can deliver bits like we need them */
|
||||
cur = msgb_put(msg, GSM_FR_BYTES);
|
||||
memcpy(cur, l1_payload, GSM_FR_BYTES);
|
||||
|
||||
lchan_set_marker(osmo_fr_check_sid(l1_payload, payload_len), lchan);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/*! \brief convert GSM-FR from RTP payload to L1 format
|
||||
* \param[out] l1_payload payload part of L1 buffer
|
||||
* \param[in] rtp_payload pointer to RTP payload data
|
||||
* \param[in] payload_len length of \a rtp_payload
|
||||
* \returns number of \a l1_payload bytes filled
|
||||
*/
|
||||
static int rtppayload_to_l1_fr(uint8_t *l1_payload, const uint8_t *rtp_payload,
|
||||
unsigned int payload_len)
|
||||
{
|
||||
/* new L1 can deliver bits like we need them */
|
||||
memcpy(l1_payload, rtp_payload, GSM_FR_BYTES);
|
||||
return GSM_FR_BYTES;
|
||||
}
|
||||
|
||||
static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload,
|
||||
uint8_t payload_len,
|
||||
struct gsm_lchan *lchan)
|
||||
{
|
||||
struct msgb *msg;
|
||||
uint8_t *cur;
|
||||
|
||||
msg = msgb_alloc_headroom(1024, 128, "L1P-to-RTP");
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
/* new L1 can deliver bits like we need them */
|
||||
cur = msgb_put(msg, GSM_EFR_BYTES);
|
||||
memcpy(cur, l1_payload, GSM_EFR_BYTES);
|
||||
enum osmo_amr_type ft;
|
||||
enum osmo_amr_quality bfi;
|
||||
uint8_t cmr;
|
||||
int8_t sti, cmi;
|
||||
osmo_amr_rtp_dec(l1_payload, payload_len, &cmr, &cmi, &ft, &bfi, &sti);
|
||||
lchan_set_marker(ft == AMR_GSM_EFR_SID, lchan);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static int rtppayload_to_l1_efr(uint8_t *l1_payload, const uint8_t *rtp_payload,
|
||||
unsigned int payload_len)
|
||||
{
|
||||
memcpy(l1_payload, rtp_payload, payload_len);
|
||||
|
||||
return payload_len;
|
||||
}
|
||||
|
||||
static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len,
|
||||
struct gsm_lchan *lchan)
|
||||
{
|
||||
struct msgb *msg;
|
||||
uint8_t *cur;
|
||||
|
||||
msg = msgb_alloc_headroom(1024, 128, "L1P-to-RTP");
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
if (payload_len != GSM_HR_BYTES) {
|
||||
LOGP(DL1P, LOGL_ERROR, "L1 HR frame length %u != expected %u\n",
|
||||
payload_len, GSM_HR_BYTES);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cur = msgb_put(msg, GSM_HR_BYTES);
|
||||
memcpy(cur, l1_payload, GSM_HR_BYTES);
|
||||
|
||||
lchan_set_marker(osmo_hr_check_sid(l1_payload, payload_len), lchan);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/*! \brief convert GSM-FR from RTP payload to L1 format
|
||||
* \param[out] l1_payload payload part of L1 buffer
|
||||
* \param[in] rtp_payload pointer to RTP payload data
|
||||
* \param[in] payload_len length of \a rtp_payload
|
||||
* \returns number of \a l1_payload bytes filled
|
||||
*/
|
||||
static int rtppayload_to_l1_hr(uint8_t *l1_payload, const uint8_t *rtp_payload,
|
||||
unsigned int payload_len)
|
||||
{
|
||||
|
||||
if (payload_len != GSM_HR_BYTES) {
|
||||
LOGP(DL1P, LOGL_ERROR, "RTP HR frame length %u != expected %u\n",
|
||||
payload_len, GSM_HR_BYTES);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(l1_payload, rtp_payload, GSM_HR_BYTES);
|
||||
|
||||
return GSM_HR_BYTES;
|
||||
}
|
||||
|
||||
static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_len,
|
||||
struct gsm_lchan *lchan)
|
||||
{
|
||||
struct msgb *msg;
|
||||
uint8_t amr_if2_len = payload_len - 2;
|
||||
uint8_t *cur;
|
||||
|
||||
msg = msgb_alloc_headroom(1024, 128, "L1P-to-RTP");
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
cur = msgb_put(msg, amr_if2_len);
|
||||
memcpy(cur, l1_payload+2, amr_if2_len);
|
||||
|
||||
/*
|
||||
* Audiocode's MGW doesn't like receiving CMRs that are not
|
||||
* the same as the previous one. This means we need to patch
|
||||
* the content here.
|
||||
*/
|
||||
if ((cur[0] & 0xF0) == 0xF0)
|
||||
cur[0]= lchan->tch.last_cmr << 4;
|
||||
else
|
||||
lchan->tch.last_cmr = cur[0] >> 4;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/*! \brief convert AMR from RTP payload to L1 format
|
||||
* \param[out] l1_payload payload part of L1 buffer
|
||||
* \param[in] rtp_payload pointer to RTP payload data
|
||||
* \param[in] payload_len length of \a rtp_payload
|
||||
* \returns number of \a l1_payload bytes filled
|
||||
*/
|
||||
static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
|
||||
uint8_t payload_len, uint8_t ft)
|
||||
{
|
||||
memcpy(l1_payload, rtp_payload, payload_len);
|
||||
return payload_len;
|
||||
}
|
||||
|
||||
#define RTP_MSGB_ALLOC_SIZE 512
|
||||
|
||||
/*! \brief function for incoming RTP via TCH.req
|
||||
* \param[in] rtp_pl buffer containing RTP payload
|
||||
* \param[in] rtp_pl_len length of \a rtp_pl
|
||||
* \param[in] use_cache Use cached payload instead of parsing RTP
|
||||
* \param[in] marker RTP header Marker bit (indicates speech onset)
|
||||
* \returns 0 if encoding result can be sent further to L1 without extra actions
|
||||
* positive value if data is ready AND extra actions are required
|
||||
* negative value otherwise (no data for L1 encoded)
|
||||
*
|
||||
* This function prepares a msgb with a L1 PH-DATA.req primitive and
|
||||
* queues it into lchan->dl_tch_queue.
|
||||
*
|
||||
* Note that the actual L1 primitive header is not fully initialized
|
||||
* yet, as things like the frame number, etc. are unknown at the time we
|
||||
* pre-fill the primtive.
|
||||
*/
|
||||
int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
|
||||
const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn,
|
||||
bool use_cache, bool marker)
|
||||
{
|
||||
uint8_t *payload_type;
|
||||
uint8_t *l1_payload, ft;
|
||||
int rc = 0;
|
||||
bool is_sid = false;
|
||||
|
||||
DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan),
|
||||
osmo_hexdump(rtp_pl, rtp_pl_len));
|
||||
|
||||
payload_type = &data[0];
|
||||
l1_payload = &data[1];
|
||||
|
||||
switch (lchan->tch_mode) {
|
||||
case GSM48_CMODE_SPEECH_V1:
|
||||
if (lchan->type == GSM_LCHAN_TCH_F) {
|
||||
*payload_type = GsmL1_TchPlType_Fr;
|
||||
rc = rtppayload_to_l1_fr(l1_payload,
|
||||
rtp_pl, rtp_pl_len);
|
||||
if (rc && lchan->ts->trx->bts->dtxd)
|
||||
is_sid = osmo_fr_check_sid(rtp_pl, rtp_pl_len);
|
||||
} else{
|
||||
*payload_type = GsmL1_TchPlType_Hr;
|
||||
rc = rtppayload_to_l1_hr(l1_payload,
|
||||
rtp_pl, rtp_pl_len);
|
||||
if (rc && lchan->ts->trx->bts->dtxd)
|
||||
is_sid = osmo_hr_check_sid(rtp_pl, rtp_pl_len);
|
||||
}
|
||||
if (is_sid)
|
||||
dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, -1);
|
||||
break;
|
||||
case GSM48_CMODE_SPEECH_EFR:
|
||||
*payload_type = GsmL1_TchPlType_Efr;
|
||||
rc = rtppayload_to_l1_efr(l1_payload, rtp_pl,
|
||||
rtp_pl_len);
|
||||
/* FIXME: detect and save EFR SID */
|
||||
break;
|
||||
case GSM48_CMODE_SPEECH_AMR:
|
||||
if (use_cache) {
|
||||
*payload_type = GsmL1_TchPlType_Amr;
|
||||
rtppayload_to_l1_amr(l1_payload, lchan->tch.dtx.cache,
|
||||
lchan->tch.dtx.len, ft);
|
||||
*len = lchan->tch.dtx.len + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = dtx_dl_amr_fsm_step(lchan, rtp_pl, rtp_pl_len, fn,
|
||||
l1_payload, marker, len, &ft);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (!dtx_dl_amr_enabled(lchan)) {
|
||||
*payload_type = GsmL1_TchPlType_Amr;
|
||||
rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
|
||||
ft);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* DTX DL-specific logic below: */
|
||||
switch (lchan->tch.dtx.dl_amr_fsm->state) {
|
||||
case ST_ONSET_V:
|
||||
*payload_type = GsmL1_TchPlType_Amr_Onset;
|
||||
dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
|
||||
*len = 3;
|
||||
return 1;
|
||||
case ST_VOICE:
|
||||
*payload_type = GsmL1_TchPlType_Amr;
|
||||
rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
|
||||
ft);
|
||||
return 0;
|
||||
case ST_SID_F1:
|
||||
if (lchan->type == GSM_LCHAN_TCH_H) { /* AMR HR */
|
||||
*payload_type = GsmL1_TchPlType_Amr_SidFirstP1;
|
||||
rtppayload_to_l1_amr(l1_payload + 2, rtp_pl,
|
||||
rtp_pl_len, ft);
|
||||
return 0;
|
||||
}
|
||||
/* AMR FR */
|
||||
*payload_type = GsmL1_TchPlType_Amr;
|
||||
rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
|
||||
ft);
|
||||
return 0;
|
||||
case ST_SID_F2:
|
||||
*payload_type = GsmL1_TchPlType_Amr;
|
||||
rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
|
||||
ft);
|
||||
return 0;
|
||||
case ST_F1_INH_V:
|
||||
*payload_type = GsmL1_TchPlType_Amr_SidFirstInH;
|
||||
*len = 3;
|
||||
dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
|
||||
return 1;
|
||||
case ST_U_INH_V:
|
||||
*payload_type = GsmL1_TchPlType_Amr_SidUpdateInH;
|
||||
*len = 3;
|
||||
dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
|
||||
return 1;
|
||||
case ST_SID_U:
|
||||
case ST_U_NOINH:
|
||||
return -EAGAIN;
|
||||
case ST_FACCH:
|
||||
return -EBADMSG;
|
||||
default:
|
||||
LOGP(DRTP, LOGL_ERROR, "Unhandled DTX DL AMR FSM state "
|
||||
"%d\n", lchan->tch.dtx.dl_amr_fsm->state);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* we don't support CSD modes */
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
LOGP(DRTP, LOGL_ERROR, "%s unable to parse RTP payload\n",
|
||||
gsm_lchan_name(lchan));
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
*len = rc + 1;
|
||||
|
||||
DEBUGP(DRTP, "%s RTP->L1: %s\n", gsm_lchan_name(lchan),
|
||||
osmo_hexdump(data, *len));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_recv_only(uint8_t speech_mode)
|
||||
{
|
||||
return (speech_mode & 0xF0) == (1 << 4);
|
||||
}
|
||||
|
||||
/*! \brief receive a traffic L1 primitive for a given lchan */
|
||||
int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg)
|
||||
{
|
||||
GsmL1_Prim_t *l1p = msgb_l1prim(l1p_msg);
|
||||
GsmL1_PhDataInd_t *data_ind = &l1p->u.phDataInd;
|
||||
uint8_t *payload, payload_type, payload_len, sid_first[9] = { 0 };
|
||||
struct msgb *rmsg = NULL;
|
||||
struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)].lchan[l1sap_chan2ss(chan_nr)];
|
||||
|
||||
if (is_recv_only(lchan->abis_ip.speech_mode))
|
||||
return -EAGAIN;
|
||||
|
||||
if (data_ind->msgUnitParam.u8Size < 1) {
|
||||
LOGPFN(DL1P, LOGL_DEBUG, data_ind->u32Fn, "chan_nr %d Rx Payload size 0\n", chan_nr);
|
||||
/* Push empty payload to upper layers */
|
||||
rmsg = msgb_alloc_headroom(256, 128, "L1P-to-RTP");
|
||||
return add_l1sap_header(trx, rmsg, lchan, chan_nr, data_ind->u32Fn,
|
||||
data_ind->measParam.fBer * 10000,
|
||||
data_ind->measParam.fLinkQuality * 10);
|
||||
}
|
||||
|
||||
payload_type = data_ind->msgUnitParam.u8Buffer[0];
|
||||
payload = data_ind->msgUnitParam.u8Buffer + 1;
|
||||
payload_len = data_ind->msgUnitParam.u8Size - 1;
|
||||
|
||||
/* clear RTP marker if the marker has previously sent */
|
||||
if (!lchan->tch.dtx.is_speech_resume)
|
||||
lchan->rtp_tx_marker = false;
|
||||
|
||||
switch (payload_type) {
|
||||
case GsmL1_TchPlType_Fr:
|
||||
case GsmL1_TchPlType_Efr:
|
||||
if (lchan->type != GSM_LCHAN_TCH_F)
|
||||
goto err_payload_match;
|
||||
break;
|
||||
case GsmL1_TchPlType_Hr:
|
||||
if (lchan->type != GSM_LCHAN_TCH_H)
|
||||
goto err_payload_match;
|
||||
break;
|
||||
case GsmL1_TchPlType_Amr:
|
||||
if (lchan->type != GSM_LCHAN_TCH_H &&
|
||||
lchan->type != GSM_LCHAN_TCH_F)
|
||||
goto err_payload_match;
|
||||
break;
|
||||
case GsmL1_TchPlType_Amr_Onset:
|
||||
if (lchan->type != GSM_LCHAN_TCH_H &&
|
||||
lchan->type != GSM_LCHAN_TCH_F)
|
||||
goto err_payload_match;
|
||||
/* according to 3GPP TS 26.093 ONSET frames precede the first
|
||||
speech frame of a speech burst - set the marker for next RTP
|
||||
frame */
|
||||
lchan->tch.dtx.is_speech_resume = true;
|
||||
lchan->rtp_tx_marker = true;
|
||||
break;
|
||||
case GsmL1_TchPlType_Amr_SidFirstP1:
|
||||
if (lchan->type != GSM_LCHAN_TCH_H)
|
||||
goto err_payload_match;
|
||||
LOGPFN(DL1P, LOGL_DEBUG, data_ind->u32Fn, "DTX: received SID_FIRST_P1 from L1 "
|
||||
"(%d bytes)\n", payload_len);
|
||||
break;
|
||||
case GsmL1_TchPlType_Amr_SidFirstP2:
|
||||
if (lchan->type != GSM_LCHAN_TCH_H)
|
||||
goto err_payload_match;
|
||||
LOGPFN(DL1P, LOGL_DEBUG, data_ind->u32Fn, "DTX: received SID_FIRST_P2 from L1 "
|
||||
"(%d bytes)\n", payload_len);
|
||||
break;
|
||||
case GsmL1_TchPlType_Amr_SidFirstInH:
|
||||
if (lchan->type != GSM_LCHAN_TCH_H)
|
||||
goto err_payload_match;
|
||||
lchan->tch.dtx.is_speech_resume = true;
|
||||
lchan->rtp_tx_marker = true;
|
||||
LOGPFN(DL1P, LOGL_DEBUG, data_ind->u32Fn, "DTX: received SID_FIRST_INH from L1 "
|
||||
"(%d bytes)\n", payload_len);
|
||||
break;
|
||||
case GsmL1_TchPlType_Amr_SidUpdateInH:
|
||||
if (lchan->type != GSM_LCHAN_TCH_H)
|
||||
goto err_payload_match;
|
||||
lchan->tch.dtx.is_speech_resume = true;
|
||||
lchan->rtp_tx_marker = true;
|
||||
LOGPFN(DL1P, LOGL_DEBUG, data_ind->u32Fn, "DTX: received SID_UPDATE_INH from L1 "
|
||||
"(%d bytes)\n", payload_len);
|
||||
break;
|
||||
default:
|
||||
LOGPFN(DL1P, LOGL_NOTICE, data_ind->u32Fn, "%s Rx Payload Type %s is unsupported\n",
|
||||
gsm_lchan_name(lchan),
|
||||
get_value_string(oc2gbts_tch_pl_names, payload_type));
|
||||
break;
|
||||
}
|
||||
|
||||
LOGP(DL1P, LOGL_DEBUG, "%s %s lchan->rtp_tx_marker = %s, len=%u\n",
|
||||
gsm_lchan_name(lchan),
|
||||
get_value_string(oc2gbts_tch_pl_names, payload_type),
|
||||
lchan->rtp_tx_marker ? "true" : "false",
|
||||
payload_len);
|
||||
|
||||
switch (payload_type) {
|
||||
case GsmL1_TchPlType_Fr:
|
||||
rmsg = l1_to_rtppayload_fr(payload, payload_len, lchan);
|
||||
break;
|
||||
case GsmL1_TchPlType_Hr:
|
||||
rmsg = l1_to_rtppayload_hr(payload, payload_len, lchan);
|
||||
break;
|
||||
case GsmL1_TchPlType_Efr:
|
||||
rmsg = l1_to_rtppayload_efr(payload, payload_len, lchan);
|
||||
break;
|
||||
case GsmL1_TchPlType_Amr:
|
||||
rmsg = l1_to_rtppayload_amr(payload, payload_len, lchan);
|
||||
break;
|
||||
case GsmL1_TchPlType_Amr_SidFirstP1:
|
||||
memcpy(sid_first, payload, payload_len);
|
||||
int len = osmo_amr_rtp_enc(sid_first, 0, AMR_SID, AMR_GOOD);
|
||||
if (len < 0)
|
||||
return 0;
|
||||
rmsg = l1_to_rtppayload_amr(sid_first, len, lchan);
|
||||
break;
|
||||
}
|
||||
|
||||
if (rmsg)
|
||||
return add_l1sap_header(trx, rmsg, lchan, chan_nr, data_ind->u32Fn,
|
||||
data_ind->measParam.fBer * 10000,
|
||||
data_ind->measParam.fLinkQuality * 10);
|
||||
|
||||
return 0;
|
||||
|
||||
err_payload_match:
|
||||
LOGPFN(DL1P, LOGL_ERROR, data_ind->u32Fn, "%s Rx Payload Type %s incompatible with lchan\n",
|
||||
gsm_lchan_name(lchan), get_value_string(oc2gbts_tch_pl_names, payload_type));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn)
|
||||
{
|
||||
struct msgb *msg;
|
||||
GsmL1_Prim_t *l1p;
|
||||
GsmL1_PhDataReq_t *data_req;
|
||||
GsmL1_MsgUnitParam_t *msu_param;
|
||||
uint8_t *payload_type;
|
||||
uint8_t *l1_payload;
|
||||
int rc;
|
||||
|
||||
msg = l1p_msgb_alloc();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l1p = msgb_l1prim(msg);
|
||||
data_req = &l1p->u.phDataReq;
|
||||
msu_param = &data_req->msgUnitParam;
|
||||
payload_type = &msu_param->u8Buffer[0];
|
||||
l1_payload = &msu_param->u8Buffer[1];
|
||||
|
||||
switch (lchan->tch_mode) {
|
||||
case GSM48_CMODE_SPEECH_AMR:
|
||||
if (lchan->type == GSM_LCHAN_TCH_H &&
|
||||
dtx_dl_amr_enabled(lchan)) {
|
||||
/* we have to explicitly handle sending SID FIRST P2 for
|
||||
AMR HR in here */
|
||||
*payload_type = GsmL1_TchPlType_Amr_SidFirstP2;
|
||||
rc = dtx_dl_amr_fsm_step(lchan, NULL, 0, fn, l1_payload,
|
||||
false, &(msu_param->u8Size),
|
||||
NULL);
|
||||
if (rc == 0)
|
||||
return msg;
|
||||
}
|
||||
*payload_type = GsmL1_TchPlType_Amr;
|
||||
break;
|
||||
case GSM48_CMODE_SPEECH_V1:
|
||||
if (lchan->type == GSM_LCHAN_TCH_F)
|
||||
*payload_type = GsmL1_TchPlType_Fr;
|
||||
else
|
||||
*payload_type = GsmL1_TchPlType_Hr;
|
||||
break;
|
||||
case GSM48_CMODE_SPEECH_EFR:
|
||||
*payload_type = GsmL1_TchPlType_Efr;
|
||||
break;
|
||||
default:
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = repeat_last_sid(lchan, l1_payload, fn);
|
||||
if (!rc) {
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
msu_param->u8Size = rc;
|
||||
|
||||
return msg;
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/* Helper utilities that are used in OMLs */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
*
|
||||
* Based on sysmoBTS:
|
||||
* (C) 2011-2013 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2013 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 "utils.h"
|
||||
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
|
||||
#include "oc2gbts.h"
|
||||
#include "l1_if.h"
|
||||
|
||||
int band_oc2g2osmo(GsmL1_FreqBand_t band)
|
||||
{
|
||||
switch (band) {
|
||||
case GsmL1_FreqBand_850:
|
||||
return GSM_BAND_850;
|
||||
case GsmL1_FreqBand_900:
|
||||
return GSM_BAND_900;
|
||||
case GsmL1_FreqBand_1800:
|
||||
return GSM_BAND_1800;
|
||||
case GsmL1_FreqBand_1900:
|
||||
return GSM_BAND_1900;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int band_osmo2oc2g(struct gsm_bts_trx *trx, enum gsm_band osmo_band)
|
||||
{
|
||||
struct oc2gl1_hdl *fl1h = trx_oc2gl1_hdl(trx);
|
||||
|
||||
/* check if the TRX hardware actually supports the given band */
|
||||
if (!(fl1h->hw_info.band_support & osmo_band))
|
||||
return -1;
|
||||
|
||||
/* if yes, convert from osmcoom style band definition to L1 band */
|
||||
switch (osmo_band) {
|
||||
case GSM_BAND_850:
|
||||
return GsmL1_FreqBand_850;
|
||||
case GSM_BAND_900:
|
||||
return GsmL1_FreqBand_900;
|
||||
case GSM_BAND_1800:
|
||||
return GsmL1_FreqBand_1800;
|
||||
case GSM_BAND_1900:
|
||||
return GsmL1_FreqBand_1900;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the band that matches the ARFCN. In general the ARFCNs
|
||||
* for GSM1800 and GSM1900 overlap and one needs to specify the
|
||||
* rightband. When moving between GSM900/GSM1800 and GSM850/1900
|
||||
* modifying the BTS configuration is a bit annoying. The auto-band
|
||||
* configuration allows to ease with this transition.
|
||||
*/
|
||||
int oc2gbts_select_oc2g_band(struct gsm_bts_trx *trx, uint16_t arfcn)
|
||||
{
|
||||
enum gsm_band band;
|
||||
struct gsm_bts *bts = trx->bts;
|
||||
if (!bts->auto_band)
|
||||
return band_osmo2oc2g(trx, bts->band);
|
||||
|
||||
/*
|
||||
* We need to check what will happen now.
|
||||
*/
|
||||
band = gsm_arfcn2band(arfcn);
|
||||
|
||||
/* if we are already on the right band return */
|
||||
if (band == bts->band)
|
||||
return band_osmo2oc2g(trx, bts->band);
|
||||
|
||||
/* Check if it is GSM1800/GSM1900 */
|
||||
if (band == GSM_BAND_1800 && bts->band == GSM_BAND_1900)
|
||||
return band_osmo2oc2g(trx, bts->band);
|
||||
|
||||
/*
|
||||
* Now to the actual autobauding. We just want DCS/DCS and
|
||||
* PCS/PCS for PCS we check for 850/1800 though
|
||||
*/
|
||||
if ((band == GSM_BAND_900 && bts->band == GSM_BAND_1800)
|
||||
|| (band == GSM_BAND_1800 && bts->band == GSM_BAND_900)
|
||||
|| (band == GSM_BAND_850 && bts->band == GSM_BAND_1900))
|
||||
return band_osmo2oc2g(trx, band);
|
||||
if (band == GSM_BAND_1800 && bts->band == GSM_BAND_850)
|
||||
return band_osmo2oc2g(trx, GSM_BAND_1900);
|
||||
|
||||
/* give up */
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef _UTILS_H
|
||||
#define _UTILS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "oc2gbts.h"
|
||||
|
||||
struct gsm_bts_trx;
|
||||
|
||||
int band_oc2g2osmo(GsmL1_FreqBand_t band);
|
||||
|
||||
int oc2gbts_select_oc2g_band(struct gsm_bts_trx *trx, uint16_t arfcn);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue