handover_ctrl: add control interface for handover settings

The VTY handover_vtc.c offers a large number of handover specific
settings. Those settings are (with one exception) auto generated using
macros. Lets add an equivalent for the control interface that uses the
same auto generation mechanisms.

Change-Id: I12f143906818fd6b16e8783157cbb1eb51e49ffc
Depends: libosmocore I53fc207677f52b1dc748b01d58424839cdba807c
Related: SYS#5369
This commit is contained in:
Philipp Maier 2021-06-07 11:23:25 +02:00
parent 8ce07d021f
commit 181b5f3b9a
7 changed files with 254 additions and 0 deletions

View File

@ -40,6 +40,34 @@ TRX-specific commands are additionally prefixed with TRX number e. g.
|bts.N.rf_state|RO|No|"<oper>,<admin>,<pol>"|See <<rfs>> for details.
|bts.N.trx.M.arfcn|RW|No|"<arfcn>"|Set/Get ARFCN (value between (0, 1023)).
|bts.N.trx.M.max-power-reduction|RW|No|"<mpr>"|See <<mpr>> for details.
|[bts.N.]handover.active|RW|No|"0","1","default"|Enable/disable handover.
|[bts.N.]handover.algorithm|RW|No|"1","2","default"|Choose algorithm for handover decision (hodec1 or hodec2).
|[bts.N.]handover1.window.rxlev.averaging|RW|No|<1-10>,"default"|How many RxLev measurements to use for averaging.
|[bts.N.]handover1.window.rxqual.averaging|RW|No|<1-10>,"default"|How many RxQual measurements to use for averaging.
|[bts.N.]handover1.window.rxlev.neighbor.averaging|RW|No|<1-10>,"default"|How many Neighbor RxLev measurements to use for averaging.
|[bts.N.]handover1.power.budget.interval|RW|No|<1-99>,"default"|How often to check for a better cell (SACCH frames).
|[bts.N.]handover1.power.budget.hysteresis|RW|No|<0-999>,"default"|How many dB stronger must a neighbor be to become a HO candidate.
|[bts.N.]handover1.maximum.distance|RW|No|<0-9999>,"default"|Maximum Timing-Advance value (i.e. MS distance) before triggering HO.
|[bts.N.]handover2.window.rxlev.averaging|RW|No|<1-10>,"default"|How many RxLev measurements to use for averaging.
|[bts.N.]handover2.window.rxqual.averaging|RW|No|<1-10>,"default"|How many RxQual measurements to use for averaging.
|[bts.N.]handover2.window.rxlev.neighbor.averaging|RW|No|<1-10>,"default"|window rxlev neighbor averaging.
|[bts.N.]handover2.power.budget.interval|RW|No|<1-99>,"default"|How many dB stronger must a neighbor be to become a HO candidate.
|[bts.N.]handover2.power.budget.hysteresis|RW|No|<0-999>,"default"|How many dB stronger must a neighbor be to become a HO candidate.
|[bts.N.]handover2.maximum.distance|RW|No|<0-9999>,"default"|Maximum Timing-Advance value (i.e. MS distance) before triggering HO.
|[bts.N.]handover2.assignment|RW|No|"0","1","default"|Enable or disable in-call channel re-assignment within the same cell.
|[bts.N.]handover2.tdma-measurement|RW|No|"full","subset","default"|Define measurement set of TDMA frames.
|[bts.N.]handover2.min.rxlev|RW|No|<-110--50>,"default"|How weak may RxLev of an MS become before triggering HO.
|[bts.N.]handover2.min.rxqual|RW|No|<0-7>,"default"|How bad may RxQual of an MS become before triggering HO.
|[bts.N.]handover2.afs-bias.rxlev|RW|No|<0-20>,"default"|RxLev improvement bias for AFS over other codecs.
|[bts.N.]handover2.afs-bias.rxqual|RW|No|<0-7>,"default"|RxQual improvement bias for AFS over other codecs.
|[bts.N.]handover2.min-free-slots.tch-f|RW|No|<0-9999>,"default"|Minimum free TCH/F timeslots before cell is considered congested.
|[bts.N.]handover2.min-free-slots.tch-h|RW|No|<0-9999>,"default"|Minimum free TCH/H timeslots before cell is considered congested.
|[bts.N.]handover2.max-handovers|RW|No|<1-9999>,"default"|Maximum number of concurrent handovers allowed per cell.
|[bts.N.]handover2.penalty-time.max-distance|RW|No|<0-99999>,"default"|ime to suspend handover for a subscriber after leaving this cell due to exceeding max distance.
|[bts.N.]handover2.penalty-time.failed-ho|RW|No|<0-99999>,"default"|Time to suspend handover for a subscriber after a failed handover into this cell.
|[bts.N.]handover2.penalty-time.failed-assignment|RW|No|<0-99999>,"default"|Time to suspend handover for a subscriber after a failed re-assignment within this cell.
|[bts.N.]handover2.retries|RW|No|<0-9>,"default"|Number of times to immediately retry a failed handover/assignment, before a penalty time is applied.
|handover2.congestion-check|RW|No|"disabled",<1-999>,"now"|Congestion check interval in seconds, "now" triggers immediate congestion check.
|===
[[notif]]

View File

@ -23,6 +23,7 @@ noinst_HEADERS = \
gsm_data.h \
handover.h \
handover_cfg.h \
handover_ctrl.h \
handover_decision.h \
handover_decision_2.h \
handover_fsm.h \

View File

@ -0,0 +1,3 @@
#pragma once
int bsc_ho_ctrl_cmds_install(void *ctx);

View File

@ -61,6 +61,7 @@ osmo_bsc_SOURCES = \
gsm_04_08_rr.c \
gsm_data.c \
handover_cfg.c \
handover_ctrl.c \
handover_decision.c \
handover_decision_2.c \
handover_fsm.c \

View File

@ -0,0 +1,216 @@
/* OsmoBSC handover control interface implementation */
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* Author: Philipp Maier <pmaier@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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 <stdbool.h>
#include <talloc.h>
#include <osmocom/bsc/vty.h>
#include <osmocom/bsc/handover_cfg.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/handover_decision_2.h>
#include <osmocom/ctrl/control_cmd.h>
/* In handover_cfg.h the config items are described in VTY syntax. To be able to
* use those here in the CTRL interface, we parse the config arguments like the
* VTY would. (the value specification may be in the form of "<from-to>" or
* "A|B|C|..." */
static bool verify_vty_cmd_arg(void *ctx, const char *range, const char *value)
{
bool success;
char *range_tok;
char *valid_val;
/* "default" value is always a valid value */
if (strcmp(value, "default") == 0)
return true;
/* Try to check for a range first since it is the most common case */
if (range[0] == '<') {
if (vty_cmd_range_match(range, value))
return true;
else
return false;
}
/* Try to tokenize the string to check for distintinct values */
success = false;
range_tok = talloc_zero_size(ctx, strlen(range) + 1);
memcpy(range_tok, range, strlen(range));
valid_val = strtok(range_tok, "|");
while (valid_val != NULL) {
if (strcmp(valid_val, value) == 0) {
success = true;
break;
}
valid_val = strtok(NULL, "|");
}
talloc_free(range_tok);
return success;
}
/* NOTE: The following macro scheme has been designed for using it in the VTY
* code. However, for the most part it also works for CTRL interface code as
* well. */
#define HO_CFG_ONE_MEMBER(TYPE, NAME, DEFAULT_VAL, VTY_CMD_PREFIX, VTY_CMD, VTY_CMD_ARG, VTY_ARG_EVAL, VTY_WRITE_FMT, VTY_WRITE_CONV, VTY6) \
CTRL_CMD_DEFINE(NAME, VTY_CMD_PREFIX VTY_CMD); \
static int get_##NAME(struct ctrl_cmd *cmd, void *_data) \
{ \
struct gsm_network *net = cmd->node; \
struct handover_cfg *ho = net->ho; \
TYPE val; \
if (ho_isset_##NAME(ho)) { \
val = ho_get_##NAME(ho); \
cmd->reply = talloc_asprintf(cmd, VTY_WRITE_FMT, VTY_WRITE_CONV(val)); \
} else \
cmd->reply = talloc_asprintf(cmd, "%s", #DEFAULT_VAL); \
return CTRL_CMD_REPLY; \
} \
static int set_##NAME(struct ctrl_cmd *cmd, void *_data) \
{ \
struct gsm_network *net = cmd->node; \
struct handover_cfg *ho = net->ho; \
TYPE value; \
if (strcmp(cmd->value, "default") == 0) \
value = VTY_ARG_EVAL(#DEFAULT_VAL); \
else \
value = VTY_ARG_EVAL(cmd->value); \
ho_set_##NAME(ho, value); \
return get_##NAME(cmd, _data); \
} \
static int verify_##NAME(struct ctrl_cmd *cmd, const char *value, void *_data) \
{ \
if (verify_vty_cmd_arg(cmd, VTY_CMD_ARG, value) != true) \
return -1; \
return 0; \
} \
CTRL_CMD_DEFINE(bts_##NAME, VTY_CMD_PREFIX VTY_CMD); \
static int get_bts_##NAME(struct ctrl_cmd *cmd, void *_data) \
{ \
struct gsm_bts *bts = cmd->node; \
struct handover_cfg *ho = bts->ho; \
TYPE val; \
if (ho_isset_##NAME(ho)) { \
val = ho_get_##NAME(ho); \
cmd->reply = talloc_asprintf(cmd, VTY_WRITE_FMT, VTY_WRITE_CONV(val)); \
} else { \
cmd->reply = talloc_asprintf(cmd, "%s", #DEFAULT_VAL); \
} \
return CTRL_CMD_REPLY; \
} \
static int set_bts_##NAME(struct ctrl_cmd *cmd, void *_data) \
{ \
struct gsm_bts *bts = cmd->node; \
struct handover_cfg *ho = bts->ho; \
TYPE value; \
if (strcmp(cmd->value, "default") == 0) \
value = VTY_ARG_EVAL(#DEFAULT_VAL); \
else \
value = VTY_ARG_EVAL(cmd->value); \
ho_set_##NAME(ho, value); \
return get_bts_##NAME(cmd, _data); \
} \
static int verify_bts_##NAME(struct ctrl_cmd *cmd, const char *value, void *_data) \
{ \
return verify_##NAME(cmd, value, _data); \
} \
/* Expand the above macro using the definitions from handover_cfg.h */
HO_CFG_ALL_MEMBERS
#undef HO_CFG_ONE_MEMBER
CTRL_CMD_DEFINE(congestion_check_interval, "handover2 congestion-check");
static int get_congestion_check_interval(struct ctrl_cmd *cmd, void *_data)
{
struct gsm_network *net = cmd->node;
if (net->hodec2.congestion_check_interval_s > 0)
cmd->reply = talloc_asprintf(cmd, "%u", net->hodec2.congestion_check_interval_s);
else
cmd->reply = "disabled";
return CTRL_CMD_REPLY;
}
static int set_congestion_check_interval(struct ctrl_cmd *cmd, void *_data)
{
struct gsm_network *net = cmd->node;
int value;
/* Trigger congestion check and leave without changing anything */
if (strcmp(cmd->value, "now") == 0) {
hodec2_congestion_check(net);
return get_congestion_check_interval(cmd, _data);
}
if (strcmp(cmd->value, "disabled") == 0)
value = 0;
else
value = atoi(cmd->value);
hodec2_on_change_congestion_check_interval(net, value);
return get_congestion_check_interval(cmd, _data);
}
static int verify_congestion_check_interval(struct ctrl_cmd *cmd, const char *value, void *_data)
{
if (strcmp(value, "disabled") == 0)
return 0;
if (strcmp(value, "now") == 0)
return 0;
if (verify_vty_cmd_arg(cmd, "<1-999>", value))
return 0;
return -1;
}
/* Filter name member in cmd for illegal '/' characters */
static struct ctrl_cmd_element *filter_name(void *ctx,
struct ctrl_cmd_element *cmd)
{
unsigned int i;
char *name;
if (osmo_separated_identifiers_valid(cmd->name, " -"))
return cmd;
name = talloc_strdup(ctx, cmd->name);
for (i = 0; i < strlen(name); i++) {
if (name[i] == '/')
name[i] = '-';
}
cmd->name = name;
return cmd;
}
int bsc_ho_ctrl_cmds_install(void *ctx)
{
int rc = 0;
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_congestion_check_interval);
#define HO_CFG_ONE_MEMBER(TYPE, NAME, DEFAULT_VAL, VTY0, VTY1, VTY2, VTY_ARG_EVAL, VTY4, VTY5, VTY6) \
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, filter_name(ctx, &cmd_##NAME)); \
rc |= ctrl_cmd_install(CTRL_NODE_BTS, filter_name(ctx, &cmd_bts_##NAME)); \
HO_CFG_ALL_MEMBERS
#undef HO_CFG_ONE_MEMBER
return rc;
}

View File

@ -28,6 +28,7 @@
#include <osmocom/bsc/signal.h>
#include <osmocom/bsc/a_reset.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/handover_ctrl.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/signal.h>
@ -695,6 +696,9 @@ int bsc_ctrl_cmds_install(struct gsm_network *net)
int rc;
rc = bsc_base_ctrl_cmds_install();
if (rc)
goto end;
rc = bsc_ho_ctrl_cmds_install(net);
if (rc)
goto end;
rc = ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_loc);

View File

@ -50,6 +50,7 @@ handover_test_LDADD = \
$(top_builddir)/src/osmo-bsc/acc.o \
$(top_builddir)/src/osmo-bsc/assignment_fsm.o \
$(top_builddir)/src/osmo-bsc/bsc_ctrl_commands.o \
$(top_builddir)/src/osmo-bsc/handover_ctrl.o \
$(top_builddir)/src/osmo-bsc/bsc_init.o \
$(top_builddir)/src/osmo-bsc/bsc_rf_ctrl.o \
$(top_builddir)/src/osmo-bsc/bsc_rll.o \