2013-01-09 16:03:27 +00:00
|
|
|
/*
|
2015-01-31 21:16:00 +00:00
|
|
|
* (C) 2013-2015 by Holger Hans Peter Freyther
|
2022-10-05 12:35:32 +00:00
|
|
|
* (C) 2013-2022 by sysmocom s.f.m.c. GmbH
|
2013-01-09 16:03:27 +00:00
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*
|
|
|
|
*/
|
2022-10-05 12:35:32 +00:00
|
|
|
|
2014-08-20 21:47:15 +00:00
|
|
|
#include <errno.h>
|
2015-02-10 20:37:16 +00:00
|
|
|
#include <time.h>
|
2013-01-09 16:03:27 +00:00
|
|
|
|
2014-08-20 21:47:15 +00:00
|
|
|
#include <osmocom/ctrl/control_cmd.h>
|
2021-05-17 16:59:58 +00:00
|
|
|
|
|
|
|
#include <osmocom/vty/command.h>
|
2021-06-21 12:47:21 +00:00
|
|
|
#include <osmocom/vty/misc.h>
|
2021-05-17 16:59:58 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
#include <osmocom/bsc/ctrl.h>
|
2017-09-04 13:15:32 +00:00
|
|
|
#include <osmocom/bsc/osmo_bsc_rf.h>
|
2020-07-15 18:53:16 +00:00
|
|
|
#include <osmocom/bsc/bts.h>
|
2022-10-05 12:35:32 +00:00
|
|
|
#include <osmocom/bsc/ipaccess.h>
|
|
|
|
#include <osmocom/bsc/chan_alloc.h>
|
|
|
|
#include <osmocom/bsc/abis_nm.h>
|
2021-10-27 08:57:41 +00:00
|
|
|
#include <osmocom/bsc/neighbor_ident.h>
|
2023-04-05 23:29:25 +00:00
|
|
|
#include <osmocom/bsc/system_information.h>
|
2013-01-09 16:03:27 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
static int location_equal(struct bts_location *a, struct bts_location *b)
|
|
|
|
{
|
|
|
|
return ((a->tstamp == b->tstamp) && (a->valid == b->valid) && (a->lat == b->lat) &&
|
|
|
|
(a->lon == b->lon) && (a->height == b->height));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cleanup_locations(struct llist_head *locations)
|
|
|
|
{
|
|
|
|
struct bts_location *myloc, *tmp;
|
|
|
|
int invalpos = 0, i = 0;
|
|
|
|
|
|
|
|
LOGP(DCTRL, LOGL_DEBUG, "Checking position list.\n");
|
|
|
|
llist_for_each_entry_safe(myloc, tmp, locations, list) {
|
|
|
|
i++;
|
|
|
|
if (i > 3) {
|
|
|
|
LOGP(DCTRL, LOGL_DEBUG, "Deleting old position.\n");
|
|
|
|
llist_del(&myloc->list);
|
|
|
|
talloc_free(myloc);
|
|
|
|
} else if (myloc->valid == BTS_LOC_FIX_INVALID) {
|
|
|
|
/* Only capture the newest of subsequent invalid positions */
|
|
|
|
invalpos++;
|
|
|
|
if (invalpos > 1) {
|
|
|
|
LOGP(DCTRL, LOGL_DEBUG, "Deleting subsequent invalid position.\n");
|
|
|
|
invalpos--;
|
|
|
|
i--;
|
|
|
|
llist_del(&myloc->list);
|
|
|
|
talloc_free(myloc);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
invalpos = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LOGP(DCTRL, LOGL_DEBUG, "Found %d positions.\n", i);
|
|
|
|
}
|
2021-05-17 16:59:58 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
static int get_bts_loc(struct ctrl_cmd *cmd, void *data);
|
2021-05-17 16:59:58 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
void ctrl_generate_bts_location_state_trap(struct gsm_bts *bts, struct bsc_msc_data *msc)
|
2021-05-17 16:59:58 +00:00
|
|
|
{
|
2022-10-05 12:35:32 +00:00
|
|
|
struct ctrl_cmd *cmd;
|
|
|
|
const char *oper, *admin, *policy;
|
2021-05-17 16:59:58 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
cmd = ctrl_cmd_create(msc, CTRL_TYPE_TRAP);
|
|
|
|
if (!cmd) {
|
|
|
|
LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n");
|
|
|
|
return;
|
2021-05-17 16:59:58 +00:00
|
|
|
}
|
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
cmd->id = "0";
|
|
|
|
cmd->variable = talloc_asprintf(cmd, "bts.%d.location-state", bts->nr);
|
2021-06-21 12:47:21 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
/* Prepare the location reply */
|
|
|
|
cmd->node = bts;
|
|
|
|
get_bts_loc(cmd, NULL);
|
2021-06-21 12:47:21 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts));
|
|
|
|
admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts));
|
|
|
|
policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts));
|
2021-06-21 12:47:21 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
cmd->reply = talloc_asprintf_append(cmd->reply,
|
|
|
|
",%s,%s,%s,%s,%s",
|
|
|
|
oper, admin, policy,
|
|
|
|
osmo_mcc_name(bts->network->plmn.mcc),
|
|
|
|
osmo_mnc_name(bts->network->plmn.mnc,
|
|
|
|
bts->network->plmn.mnc_3_digits));
|
2021-06-21 12:47:21 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
osmo_bsc_send_trap(cmd, msc);
|
|
|
|
talloc_free(cmd);
|
2021-06-21 12:47:21 +00:00
|
|
|
}
|
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
void bsc_gen_location_state_trap(struct gsm_bts *bts)
|
2018-03-05 01:09:40 +00:00
|
|
|
{
|
2022-10-05 12:35:32 +00:00
|
|
|
struct bsc_msc_data *msc;
|
2018-03-05 01:09:40 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
llist_for_each_entry(msc, &bts->network->mscs, entry)
|
|
|
|
ctrl_generate_bts_location_state_trap(bts, msc);
|
2018-03-05 01:09:40 +00:00
|
|
|
}
|
2022-10-05 12:35:32 +00:00
|
|
|
|
|
|
|
CTRL_CMD_DEFINE(bts_loc, "location");
|
|
|
|
static int get_bts_loc(struct ctrl_cmd *cmd, void *data)
|
2018-03-05 01:09:40 +00:00
|
|
|
{
|
2022-10-05 12:35:32 +00:00
|
|
|
struct bts_location *curloc;
|
|
|
|
struct gsm_bts *bts = (struct gsm_bts *) cmd->node;
|
|
|
|
if (!bts) {
|
|
|
|
cmd->reply = "bts not found.";
|
2018-03-05 01:09:40 +00:00
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
2013-01-09 16:03:27 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
if (llist_empty(&bts->loc_list)) {
|
|
|
|
cmd->reply = talloc_asprintf(cmd, "0,invalid,0,0,0");
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
2013-01-09 16:30:11 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
curloc = llist_entry(bts->loc_list.next, struct bts_location, list);
|
2013-01-09 16:30:11 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
cmd->reply = talloc_asprintf(cmd, "%lu,%s,%f,%f,%f", curloc->tstamp,
|
|
|
|
get_value_string(bts_loc_fix_names, curloc->valid), curloc->lat, curloc->lon, curloc->height);
|
|
|
|
if (!cmd->reply) {
|
|
|
|
cmd->reply = "OOM";
|
|
|
|
return CTRL_CMD_ERROR;
|
2013-01-09 16:30:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
static int set_bts_loc(struct ctrl_cmd *cmd, void *data)
|
2014-03-04 16:16:58 +00:00
|
|
|
{
|
2022-10-05 12:35:32 +00:00
|
|
|
char *saveptr, *lat, *lon, *height, *tstamp, *valid, *tmp;
|
|
|
|
struct bts_location *curloc, *lastloc;
|
|
|
|
int ret;
|
|
|
|
struct gsm_bts *bts = (struct gsm_bts *) cmd->node;
|
|
|
|
if (!bts) {
|
|
|
|
cmd->reply = "bts not found.";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
2014-03-04 16:16:58 +00:00
|
|
|
|
|
|
|
tmp = talloc_strdup(cmd, cmd->value);
|
|
|
|
if (!tmp)
|
|
|
|
goto oom;
|
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
tstamp = strtok_r(tmp, ",", &saveptr);
|
|
|
|
valid = strtok_r(NULL, ",", &saveptr);
|
|
|
|
lat = strtok_r(NULL, ",", &saveptr);
|
|
|
|
lon = strtok_r(NULL, ",", &saveptr);
|
|
|
|
height = strtok_r(NULL, "\0", &saveptr);
|
2014-03-04 16:16:58 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
/* Check if one of the strtok results was NULL. This will probably never occur since we will only see verified
|
|
|
|
* input in this code path */
|
|
|
|
if ((tstamp == NULL) || (valid == NULL) || (lat == NULL) || (lon == NULL) || (height == NULL)) {
|
2018-04-11 10:41:44 +00:00
|
|
|
talloc_free(tmp);
|
2022-10-05 12:35:32 +00:00
|
|
|
cmd->reply = "parse error";
|
2018-03-05 01:09:40 +00:00
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
curloc = talloc_zero(tall_bsc_ctx, struct bts_location);
|
|
|
|
if (!curloc) {
|
2018-04-11 10:41:44 +00:00
|
|
|
talloc_free(tmp);
|
2022-10-05 12:35:32 +00:00
|
|
|
goto oom;
|
2018-03-05 01:09:40 +00:00
|
|
|
}
|
2022-10-05 12:35:32 +00:00
|
|
|
INIT_LLIST_HEAD(&curloc->list);
|
2014-03-04 16:16:58 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
curloc->tstamp = atol(tstamp);
|
|
|
|
curloc->valid = get_string_value(bts_loc_fix_names, valid);
|
|
|
|
curloc->lat = atof(lat);
|
|
|
|
curloc->lon = atof(lon);
|
|
|
|
curloc->height = atof(height);
|
2014-03-04 16:16:58 +00:00
|
|
|
talloc_free(tmp);
|
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
lastloc = llist_entry(bts->loc_list.next, struct bts_location, list);
|
|
|
|
|
|
|
|
/* Add location to the end of the list */
|
|
|
|
llist_add(&curloc->list, &bts->loc_list);
|
2014-03-04 16:16:58 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
ret = get_bts_loc(cmd, data);
|
2014-03-04 16:16:58 +00:00
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
if (!location_equal(curloc, lastloc))
|
|
|
|
bsc_gen_location_state_trap(bts);
|
|
|
|
|
|
|
|
cleanup_locations(&bts->loc_list);
|
|
|
|
|
|
|
|
return ret;
|
2014-03-04 16:16:58 +00:00
|
|
|
|
|
|
|
oom:
|
|
|
|
cmd->reply = "OOM";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
2022-10-05 12:35:32 +00:00
|
|
|
|
|
|
|
static int verify_bts_loc(struct ctrl_cmd *cmd, const char *value, void *data)
|
|
|
|
{
|
|
|
|
char *saveptr, *latstr, *lonstr, *heightstr, *tstampstr, *validstr, *tmp;
|
|
|
|
time_t tstamp;
|
|
|
|
int valid;
|
|
|
|
double lat, lon, height __attribute__((unused));
|
|
|
|
|
|
|
|
tmp = talloc_strdup(cmd, value);
|
|
|
|
if (!tmp)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
tstampstr = strtok_r(tmp, ",", &saveptr);
|
|
|
|
validstr = strtok_r(NULL, ",", &saveptr);
|
|
|
|
latstr = strtok_r(NULL, ",", &saveptr);
|
|
|
|
lonstr = strtok_r(NULL, ",", &saveptr);
|
|
|
|
heightstr = strtok_r(NULL, "\0", &saveptr);
|
|
|
|
|
|
|
|
if ((tstampstr == NULL) || (validstr == NULL) || (latstr == NULL) ||
|
|
|
|
(lonstr == NULL) || (heightstr == NULL))
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
tstamp = atol(tstampstr);
|
|
|
|
valid = get_string_value(bts_loc_fix_names, validstr);
|
|
|
|
lat = atof(latstr);
|
|
|
|
lon = atof(lonstr);
|
|
|
|
height = atof(heightstr);
|
|
|
|
talloc_free(tmp);
|
|
|
|
tmp = NULL;
|
|
|
|
|
|
|
|
if (((tstamp == 0) && (valid != BTS_LOC_FIX_INVALID)) || (lat < -90) || (lat > 90) ||
|
|
|
|
(lon < -180) || (lon > 180) || (valid < 0)) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
talloc_free(tmp);
|
|
|
|
cmd->reply = talloc_strdup(cmd, "The format is <unixtime>,(invalid|fix2d|fix3d),<lat>,<lon>,<height>");
|
|
|
|
return 1;
|
|
|
|
}
|
2014-03-04 16:16:58 +00:00
|
|
|
|
2014-11-10 10:41:03 +00:00
|
|
|
/* BTS related commands below */
|
|
|
|
CTRL_CMD_DEFINE_RANGE(bts_lac, "location-area-code", struct gsm_bts, location_area_code, 0, 65535);
|
2014-11-21 09:54:42 +00:00
|
|
|
CTRL_CMD_DEFINE_RANGE(bts_ci, "cell-identity", struct gsm_bts, cell_identity, 0, 65535);
|
2014-11-10 10:41:03 +00:00
|
|
|
|
2014-11-21 09:20:29 +00:00
|
|
|
static int set_bts_apply_config(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
|
|
|
|
|
|
|
if (!is_ipaccess_bts(bts)) {
|
|
|
|
cmd->reply = "BTS is not IP based";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
2019-01-02 14:28:17 +00:00
|
|
|
ipaccess_drop_oml(bts, "ctrl bts.apply-configuration");
|
2014-11-21 09:20:29 +00:00
|
|
|
cmd->reply = "Tried to drop the BTS";
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
2017-01-11 17:37:55 +00:00
|
|
|
CTRL_CMD_DEFINE_WO_NOVRF(bts_apply_config, "apply-configuration");
|
2014-11-21 10:18:45 +00:00
|
|
|
|
|
|
|
static int set_bts_si(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
2015-05-30 18:40:54 +00:00
|
|
|
int rc;
|
2014-11-21 10:18:45 +00:00
|
|
|
|
2015-05-30 18:40:54 +00:00
|
|
|
rc = gsm_bts_set_system_infos(bts);
|
|
|
|
if (rc != 0) {
|
|
|
|
cmd->reply = "Failed to generate SI";
|
|
|
|
return CTRL_CMD_ERROR;
|
2014-11-21 10:18:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cmd->reply = "Generated new System Information";
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
2017-01-11 17:37:55 +00:00
|
|
|
CTRL_CMD_DEFINE_WO_NOVRF(bts_si, "send-new-system-informations");
|
2014-12-05 11:03:24 +00:00
|
|
|
|
2022-09-29 17:28:59 +00:00
|
|
|
static int set_bts_power_ctrl_defs(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
const struct gsm_bts *bts = cmd->node;
|
|
|
|
const struct gsm_bts_trx *trx;
|
|
|
|
|
|
|
|
if (bts->ms_power_ctrl.mode != GSM_PWR_CTRL_MODE_DYN_BTS) {
|
|
|
|
cmd->reply = "BTS is not using dyn-bts mode";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bts->model->power_ctrl_send_def_params == NULL) {
|
|
|
|
cmd->reply = "Not implemented for this BTS model";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
llist_for_each_entry(trx, &bts->trx_list, list) {
|
|
|
|
if (bts->model->power_ctrl_send_def_params(trx) != 0) {
|
|
|
|
cmd->reply = "power_ctrl_send_def_params() failed";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd->reply = "Default power control parameters have been sent";
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
CTRL_CMD_DEFINE_WO_NOVRF(bts_power_ctrl_defs, "send-power-control-defaults");
|
|
|
|
|
2014-12-05 11:03:24 +00:00
|
|
|
static int get_bts_chan_load(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct pchan_load pl;
|
|
|
|
struct gsm_bts *bts;
|
|
|
|
const char *space = "";
|
|
|
|
|
|
|
|
bts = cmd->node;
|
|
|
|
memset(&pl, 0, sizeof(pl));
|
2016-09-25 15:01:20 +00:00
|
|
|
bts_chan_load(&pl, bts);
|
2014-12-05 11:03:24 +00:00
|
|
|
|
|
|
|
cmd->reply = talloc_strdup(cmd, "");
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(pl.pchan); ++i) {
|
|
|
|
const struct load_counter *lc = &pl.pchan[i];
|
|
|
|
|
|
|
|
/* These can never have user load */
|
|
|
|
if (i == GSM_PCHAN_NONE)
|
|
|
|
continue;
|
|
|
|
if (i == GSM_PCHAN_CCCH)
|
|
|
|
continue;
|
|
|
|
if (i == GSM_PCHAN_PDCH)
|
|
|
|
continue;
|
2015-01-31 18:42:42 +00:00
|
|
|
if (i == GSM_PCHAN_UNKNOWN)
|
|
|
|
continue;
|
2014-12-05 11:03:24 +00:00
|
|
|
|
|
|
|
cmd->reply = talloc_asprintf_append(cmd->reply,
|
|
|
|
"%s%s,%u,%u",
|
|
|
|
space, gsm_pchan_name(i), lc->used, lc->total);
|
|
|
|
if (!cmd->reply)
|
|
|
|
goto error;
|
|
|
|
space = " ";
|
|
|
|
}
|
|
|
|
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
|
|
|
|
error:
|
|
|
|
cmd->reply = "Memory allocation failure";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
2017-01-11 17:37:55 +00:00
|
|
|
CTRL_CMD_DEFINE_RO(bts_chan_load, "channel-load");
|
2014-12-05 13:44:21 +00:00
|
|
|
|
|
|
|
static int get_bts_oml_conn(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
2017-10-09 15:12:53 +00:00
|
|
|
const struct gsm_bts *bts = cmd->node;
|
|
|
|
|
|
|
|
cmd->reply = get_model_oml_status(bts);
|
2014-12-05 13:44:21 +00:00
|
|
|
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
2017-01-11 17:37:55 +00:00
|
|
|
CTRL_CMD_DEFINE_RO(bts_oml_conn, "oml-connection-state");
|
2014-12-05 13:44:21 +00:00
|
|
|
|
2017-10-10 12:50:35 +00:00
|
|
|
static int get_bts_oml_up(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
const struct gsm_bts *bts = cmd->node;
|
|
|
|
|
|
|
|
cmd->reply = talloc_asprintf(cmd, "%llu", bts_uptime(bts));
|
|
|
|
if (!cmd->reply) {
|
|
|
|
cmd->reply = "OOM";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
CTRL_CMD_DEFINE_RO(bts_oml_up, "oml-uptime");
|
|
|
|
|
2015-01-31 21:16:00 +00:00
|
|
|
static int verify_bts_gprs_mode(struct ctrl_cmd *cmd, const char *value, void *_data)
|
|
|
|
{
|
|
|
|
int valid;
|
|
|
|
enum bts_gprs_mode mode;
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
|
|
|
|
|
|
|
mode = bts_gprs_mode_parse(value, &valid);
|
|
|
|
if (!valid) {
|
|
|
|
cmd->reply = "Mode is not known";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bts_gprs_mode_is_compat(bts, mode)) {
|
|
|
|
cmd->reply = "bts does not support this mode";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_bts_gprs_mode(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
|
|
|
|
|
|
|
cmd->reply = talloc_strdup(cmd, bts_gprs_mode_name(bts->gprs.mode));
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_bts_gprs_mode(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
|
|
|
|
|
|
|
bts->gprs.mode = bts_gprs_mode_parse(cmd->value, NULL);
|
|
|
|
return get_bts_gprs_mode(cmd, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
CTRL_CMD_DEFINE(bts_gprs_mode, "gprs-mode");
|
|
|
|
|
2015-02-10 20:37:16 +00:00
|
|
|
static int get_bts_rf_state(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
const char *oper, *admin, *policy;
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
|
|
|
|
|
|
|
if (!bts) {
|
|
|
|
cmd->reply = "bts not found.";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts));
|
|
|
|
admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts));
|
|
|
|
policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts));
|
|
|
|
|
|
|
|
cmd->reply = talloc_asprintf(cmd, "%s,%s,%s", oper, admin, policy);
|
|
|
|
if (!cmd->reply) {
|
2023-04-25 12:52:09 +00:00
|
|
|
cmd->reply = "OOM";
|
2015-02-10 20:37:16 +00:00
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
CTRL_CMD_DEFINE_RO(bts_rf_state, "rf_state");
|
|
|
|
|
2021-09-14 09:16:42 +00:00
|
|
|
/* Return a list of the states of each TRX for a given BTS.
|
|
|
|
* <bts_nr>,<trx_nr>,<opstate>,<adminstate>,<rf_policy>,<rsl_status>;<bts_nr>,<trx_nr>,...;...;
|
|
|
|
* For details on the string, see bsc_rf_states_c();
|
add CTRL 'rf_states' and 'bts.N.rf_states'
These commands return a listing of OML state, RF policy as well as RSL
connection status for each TRX in the form:
<bts_nr>,<trx_nr>,<opstate>,<adminstate>,<rf_policy>,<rsl_status>;<bts_nr>,<trx_nr>,...
For example, the root node 'rf_states' may return:
0,0,operational,unlocked,on,rsl-up;1,0,operational,unlocked,on,rsl-down;2,0,inoperational,locked,on,rsl-down;
A 'bts.N.rf_states' returns the same form of string, but lists only the
TRX for the given BTS nr.
Note, there is already a CTRL command 'bts.N.rf_state' (singular
'rf_state', not plural 'rf_states'), which only reflects the overall
status of all TRX combined. This new command has per-TRX resolution.
The rf-policy is so far always looked up in the global gsm_network flag,
as does the old 'rf_state' command; see osmo_bsc_rf_get_policy_by_bts()
which does not depend on the specific BTS at all. This may be worth
revisiting in the future, so I am already including the rf-policy in the
rf_state string for each TRX, even though it is globally identical.
Related: SYS#5542
Related: I01e6f391a5e71b0606c42be9b57f8a1687d59bcb (osmo-ttcn3-hacks)
Change-Id: I14fa2678fc8f2c11a879c5e9615ac552782c5b7e
2021-09-06 20:02:38 +00:00
|
|
|
*/
|
|
|
|
static int get_bts_rf_states(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
|
|
|
|
|
|
|
if (!bts) {
|
|
|
|
cmd->reply = "bts not found.";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd->reply = bsc_rf_states_of_bts_c(cmd, bts);
|
|
|
|
if (!cmd->reply) {
|
2023-04-25 12:52:09 +00:00
|
|
|
cmd->reply = "OOM";
|
add CTRL 'rf_states' and 'bts.N.rf_states'
These commands return a listing of OML state, RF policy as well as RSL
connection status for each TRX in the form:
<bts_nr>,<trx_nr>,<opstate>,<adminstate>,<rf_policy>,<rsl_status>;<bts_nr>,<trx_nr>,...
For example, the root node 'rf_states' may return:
0,0,operational,unlocked,on,rsl-up;1,0,operational,unlocked,on,rsl-down;2,0,inoperational,locked,on,rsl-down;
A 'bts.N.rf_states' returns the same form of string, but lists only the
TRX for the given BTS nr.
Note, there is already a CTRL command 'bts.N.rf_state' (singular
'rf_state', not plural 'rf_states'), which only reflects the overall
status of all TRX combined. This new command has per-TRX resolution.
The rf-policy is so far always looked up in the global gsm_network flag,
as does the old 'rf_state' command; see osmo_bsc_rf_get_policy_by_bts()
which does not depend on the specific BTS at all. This may be worth
revisiting in the future, so I am already including the rf-policy in the
rf_state string for each TRX, even though it is globally identical.
Related: SYS#5542
Related: I01e6f391a5e71b0606c42be9b57f8a1687d59bcb (osmo-ttcn3-hacks)
Change-Id: I14fa2678fc8f2c11a879c5e9615ac552782c5b7e
2021-09-06 20:02:38 +00:00
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
CTRL_CMD_DEFINE_RO(bts_rf_states, "rf_states");
|
|
|
|
|
power_control: implement BCCH carrier power reduction operation
The BCCH carrier (sometimes called C0) of a BTS shall maintain
discontinuous Downlink transmission at full power in order to
stay 'visible' to the mobile stations. Because of that, early
versions of 3GPP TS 45.008 prohibited BS power reduction on C0.
However, starting from version 13.0.0 (2015-11) there is a feature
called 'BCCH carrier power reduction operation'. This is a special
mode of operation, where the variation of RF level for some
timeslots is relaxed for the purpose of energy saving.
In BCCH carrier power reduction operation, for timeslots on the
C0 carrier, except timeslots carrying BCCH/CCCH, the output power
may be lower than the output power used for timeslots carrying
BCCH/CCCH. In this case the maximum allowed difference in output
power actually transmitted by the BTS is 6 dB.
Introduce a VTY command to turn on and off the BCCH carrier power
reduction operation. Also introduce a CTRL command. On the
A-bis/RSL, abuse the BS POWER CONTROL message by setting
the Channel Number IE to 0x80 (RSL_CHAN_BCCH).
Currently, only osmo-bts-trx is supported. A value greater than
zero makes it reduce the power on *inactive* timeslots of the
BCCH carrier. Sending zero disables the BCCH power reduction
mode completely.
For more details, see 3GPP TS 45.008, section 7.1, and 3GPP TR 45.926.
Change-Id: I047fce33d4d3e4c569dd006ba17858467a2f4783
Related: SYS#4919
2021-06-21 19:55:46 +00:00
|
|
|
static int verify_bts_c0_power_red(struct ctrl_cmd *cmd, const char *value, void *_data)
|
|
|
|
{
|
|
|
|
const int red = atoi(value);
|
|
|
|
|
|
|
|
if (red < 0 || red > 6) {
|
|
|
|
cmd->reply = "Value is out of range";
|
|
|
|
return 1;
|
|
|
|
} else if (red % 2 != 0) {
|
|
|
|
cmd->reply = "Value must be even";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_bts_c0_power_red(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
const struct gsm_bts *bts = cmd->node;
|
|
|
|
|
|
|
|
cmd->reply = talloc_asprintf(cmd, "%u", bts->c0_max_power_red_db);
|
|
|
|
if (!cmd->reply) {
|
2023-04-25 12:52:09 +00:00
|
|
|
cmd->reply = "OOM";
|
power_control: implement BCCH carrier power reduction operation
The BCCH carrier (sometimes called C0) of a BTS shall maintain
discontinuous Downlink transmission at full power in order to
stay 'visible' to the mobile stations. Because of that, early
versions of 3GPP TS 45.008 prohibited BS power reduction on C0.
However, starting from version 13.0.0 (2015-11) there is a feature
called 'BCCH carrier power reduction operation'. This is a special
mode of operation, where the variation of RF level for some
timeslots is relaxed for the purpose of energy saving.
In BCCH carrier power reduction operation, for timeslots on the
C0 carrier, except timeslots carrying BCCH/CCCH, the output power
may be lower than the output power used for timeslots carrying
BCCH/CCCH. In this case the maximum allowed difference in output
power actually transmitted by the BTS is 6 dB.
Introduce a VTY command to turn on and off the BCCH carrier power
reduction operation. Also introduce a CTRL command. On the
A-bis/RSL, abuse the BS POWER CONTROL message by setting
the Channel Number IE to 0x80 (RSL_CHAN_BCCH).
Currently, only osmo-bts-trx is supported. A value greater than
zero makes it reduce the power on *inactive* timeslots of the
BCCH carrier. Sending zero disables the BCCH power reduction
mode completely.
For more details, see 3GPP TS 45.008, section 7.1, and 3GPP TR 45.926.
Change-Id: I047fce33d4d3e4c569dd006ba17858467a2f4783
Related: SYS#4919
2021-06-21 19:55:46 +00:00
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_bts_c0_power_red(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
|
|
|
const int red = atoi(cmd->value);
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = gsm_bts_set_c0_power_red(bts, red);
|
2023-04-27 19:35:16 +00:00
|
|
|
switch (rc) {
|
|
|
|
case 0: /* success */
|
|
|
|
return get_bts_c0_power_red(cmd, data);
|
|
|
|
case -ENOTCONN:
|
|
|
|
cmd->reply = "BTS is offline";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
case -ENOTSUP:
|
power_control: implement BCCH carrier power reduction operation
The BCCH carrier (sometimes called C0) of a BTS shall maintain
discontinuous Downlink transmission at full power in order to
stay 'visible' to the mobile stations. Because of that, early
versions of 3GPP TS 45.008 prohibited BS power reduction on C0.
However, starting from version 13.0.0 (2015-11) there is a feature
called 'BCCH carrier power reduction operation'. This is a special
mode of operation, where the variation of RF level for some
timeslots is relaxed for the purpose of energy saving.
In BCCH carrier power reduction operation, for timeslots on the
C0 carrier, except timeslots carrying BCCH/CCCH, the output power
may be lower than the output power used for timeslots carrying
BCCH/CCCH. In this case the maximum allowed difference in output
power actually transmitted by the BTS is 6 dB.
Introduce a VTY command to turn on and off the BCCH carrier power
reduction operation. Also introduce a CTRL command. On the
A-bis/RSL, abuse the BS POWER CONTROL message by setting
the Channel Number IE to 0x80 (RSL_CHAN_BCCH).
Currently, only osmo-bts-trx is supported. A value greater than
zero makes it reduce the power on *inactive* timeslots of the
BCCH carrier. Sending zero disables the BCCH power reduction
mode completely.
For more details, see 3GPP TS 45.008, section 7.1, and 3GPP TR 45.926.
Change-Id: I047fce33d4d3e4c569dd006ba17858467a2f4783
Related: SYS#4919
2021-06-21 19:55:46 +00:00
|
|
|
cmd->reply = "BCCH carrier power reduction is not supported";
|
|
|
|
return CTRL_CMD_ERROR;
|
2023-04-27 19:35:16 +00:00
|
|
|
default:
|
power_control: implement BCCH carrier power reduction operation
The BCCH carrier (sometimes called C0) of a BTS shall maintain
discontinuous Downlink transmission at full power in order to
stay 'visible' to the mobile stations. Because of that, early
versions of 3GPP TS 45.008 prohibited BS power reduction on C0.
However, starting from version 13.0.0 (2015-11) there is a feature
called 'BCCH carrier power reduction operation'. This is a special
mode of operation, where the variation of RF level for some
timeslots is relaxed for the purpose of energy saving.
In BCCH carrier power reduction operation, for timeslots on the
C0 carrier, except timeslots carrying BCCH/CCCH, the output power
may be lower than the output power used for timeslots carrying
BCCH/CCCH. In this case the maximum allowed difference in output
power actually transmitted by the BTS is 6 dB.
Introduce a VTY command to turn on and off the BCCH carrier power
reduction operation. Also introduce a CTRL command. On the
A-bis/RSL, abuse the BS POWER CONTROL message by setting
the Channel Number IE to 0x80 (RSL_CHAN_BCCH).
Currently, only osmo-bts-trx is supported. A value greater than
zero makes it reduce the power on *inactive* timeslots of the
BCCH carrier. Sending zero disables the BCCH power reduction
mode completely.
For more details, see 3GPP TS 45.008, section 7.1, and 3GPP TR 45.926.
Change-Id: I047fce33d4d3e4c569dd006ba17858467a2f4783
Related: SYS#4919
2021-06-21 19:55:46 +00:00
|
|
|
cmd->reply = "Failed to enable BCCH carrier power reduction";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CTRL_CMD_DEFINE(bts_c0_power_red, "c0-power-reduction");
|
|
|
|
|
2021-10-27 15:28:49 +00:00
|
|
|
static int verify_bts_neighbor_list_add_del(struct ctrl_cmd *cmd, const char *value, void *_data)
|
|
|
|
{
|
|
|
|
int arfcn;
|
|
|
|
|
|
|
|
if (osmo_str_to_int(&arfcn, value, 10, 0, 1023) < 0) {
|
|
|
|
cmd->reply = "Invalid ARFCN value";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_bts_neighbor_list_add_del(struct ctrl_cmd *cmd, void *data, bool add)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
|
|
|
struct bitvec *bv = &bts->si_common.neigh_list;
|
|
|
|
int arfcn_int;
|
|
|
|
uint16_t arfcn;
|
|
|
|
enum gsm_band unused;
|
|
|
|
|
|
|
|
if (osmo_str_to_int(&arfcn_int, cmd->value, 10, 0, 1023) < 0) {
|
|
|
|
cmd->reply = "Failed to parse ARFCN value";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
arfcn = (uint16_t) arfcn_int;
|
|
|
|
|
|
|
|
if (bts->neigh_list_manual_mode == NL_MODE_AUTOMATIC) {
|
|
|
|
cmd->reply = "Neighbor list not in manual mode";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
|
|
|
|
cmd->reply = "Invalid arfcn detected";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (add)
|
|
|
|
bitvec_set_bit_pos(bv, arfcn, 1);
|
|
|
|
else
|
|
|
|
bitvec_set_bit_pos(bv, arfcn, 0);
|
|
|
|
|
|
|
|
cmd->reply = "OK";
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int verify_bts_neighbor_list_add(struct ctrl_cmd *cmd, const char *value, void *_data)
|
|
|
|
{
|
|
|
|
return verify_bts_neighbor_list_add_del(cmd, value, _data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_bts_neighbor_list_add(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
return set_bts_neighbor_list_add_del(cmd, data, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
CTRL_CMD_DEFINE_WO(bts_neighbor_list_add, "neighbor-list add");
|
|
|
|
|
|
|
|
static int verify_bts_neighbor_list_del(struct ctrl_cmd *cmd, const char *value, void *_data)
|
|
|
|
{
|
|
|
|
return verify_bts_neighbor_list_add_del(cmd, value, _data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_bts_neighbor_list_del(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
return set_bts_neighbor_list_add_del(cmd, data, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
CTRL_CMD_DEFINE_WO(bts_neighbor_list_del, "neighbor-list del");
|
|
|
|
|
|
|
|
static int verify_bts_neighbor_list_mode(struct ctrl_cmd *cmd, const char *value, void *_data)
|
|
|
|
{
|
|
|
|
if (!strcmp(value, "automatic"))
|
|
|
|
return 0;
|
|
|
|
if (!strcmp(value, "manual"))
|
|
|
|
return 0;
|
|
|
|
if (!strcmp(value, "manual-si5"))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cmd->reply = "Invalid mode";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_bts_neighbor_list_mode(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
2021-11-04 08:51:26 +00:00
|
|
|
int mode = NL_MODE_AUTOMATIC;
|
2021-10-27 15:28:49 +00:00
|
|
|
|
|
|
|
if (!strcmp(cmd->value, "automatic"))
|
|
|
|
mode = NL_MODE_AUTOMATIC;
|
|
|
|
else if (!strcmp(cmd->value, "manual"))
|
|
|
|
mode = NL_MODE_MANUAL;
|
|
|
|
else if (!strcmp(cmd->value, "manual-si5"))
|
|
|
|
mode = NL_MODE_MANUAL_SI5SEP;
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case NL_MODE_MANUAL_SI5SEP:
|
|
|
|
case NL_MODE_MANUAL:
|
|
|
|
/* make sure we clear the current list when switching to
|
|
|
|
* manual mode */
|
|
|
|
if (bts->neigh_list_manual_mode == 0)
|
|
|
|
memset(&bts->si_common.data.neigh_list, 0, sizeof(bts->si_common.data.neigh_list));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bts->neigh_list_manual_mode = mode;
|
|
|
|
|
|
|
|
cmd->reply = "OK";
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
CTRL_CMD_DEFINE_WO(bts_neighbor_list_mode, "neighbor-list mode");
|
|
|
|
|
2023-04-05 23:29:25 +00:00
|
|
|
/* si2quater neighbor management: delete an EARFCN.
|
|
|
|
* Format: bts.<0-255>.si2quater-neighbor-list.del.earfcn EARFCN
|
|
|
|
* EARFCN is in range 0..65535 */
|
|
|
|
static int set_bts_si2quater_neighbor_list_del_earfcn(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = (struct gsm_bts *)cmd->node;
|
|
|
|
struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
|
|
|
|
int earfcn;
|
|
|
|
|
|
|
|
if (osmo_str_to_int(&earfcn, cmd->value, 10, 0, 65535) < 0) {
|
|
|
|
cmd->reply = "Failed to parse neighbor EARFCN value";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (osmo_earfcn_del(e, earfcn) < 0) {
|
|
|
|
cmd->reply = "Failed to delete a (not existent?) neighbor EARFCN";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd->reply = "OK";
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
CTRL_CMD_DEFINE_WO_NOVRF(bts_si2quater_neighbor_list_del_earfcn,
|
|
|
|
"si2quater-neighbor-list del earfcn");
|
|
|
|
|
|
|
|
/* si2quater neighbor management: delete an UARFCN
|
|
|
|
* Format: bts.<0-255>.si2quater-neighbor-list.del.uarfcn UARFCN,SCRAMBLE
|
|
|
|
* UARFCN is in range 0..16383, SCRAMBLE is in range 0..511 */
|
|
|
|
static int set_bts_si2quater_neighbor_list_del_uarfcn(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = (struct gsm_bts *)cmd->node;
|
|
|
|
char *uarfcn_str, *scramble_str;
|
|
|
|
char *tmp, *saveptr;
|
|
|
|
int uarfcn, scramble;
|
|
|
|
|
|
|
|
tmp = talloc_strdup(OTC_SELECT, cmd->value);
|
|
|
|
if (!tmp) {
|
|
|
|
cmd->reply = "OOM";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
uarfcn_str = strtok_r(tmp, ",", &saveptr);
|
|
|
|
scramble_str = strtok_r(NULL, ",", &saveptr);
|
|
|
|
|
|
|
|
if (!uarfcn_str || osmo_str_to_int(&uarfcn, uarfcn_str, 10, 0, 16383) < 0) {
|
|
|
|
cmd->reply = "Failed to parse neighbor UARFCN value";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!scramble_str || osmo_str_to_int(&scramble, scramble_str, 10, 0, 511) < 0) {
|
|
|
|
cmd->reply = "Failed to parse neighbor scrambling code";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bts_uarfcn_del(bts, uarfcn, scramble) < 0) {
|
|
|
|
cmd->reply = "Failed to delete a (not existent?) neighbor UARFCN";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd->reply = "OK";
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
CTRL_CMD_DEFINE_WO_NOVRF(bts_si2quater_neighbor_list_del_uarfcn,
|
|
|
|
"si2quater-neighbor-list del uarfcn");
|
|
|
|
|
|
|
|
/* TODO: si2quater neighbor management: add EARFCN */
|
|
|
|
/* TODO: si2quater neighbor management: add UARFCN */
|
|
|
|
|
2023-04-25 07:23:40 +00:00
|
|
|
static int verify_bts_cell_reselection_offset(struct ctrl_cmd *cmd, const char *value, void *_data)
|
|
|
|
{
|
|
|
|
const int cell_reselection_offset = atoi(value);
|
|
|
|
|
|
|
|
if (cell_reselection_offset < 0 || cell_reselection_offset > 126) {
|
|
|
|
cmd->reply = "Value is out of range";
|
|
|
|
return 1;
|
|
|
|
} else if (cell_reselection_offset % 2 != 0) {
|
|
|
|
cmd->reply = "Value must be even";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_bts_cell_reselection_offset(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
|
|
|
|
|
|
|
if (!bts->si_common.cell_ro_sel_par.present) {
|
|
|
|
cmd->reply = "0";
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd->reply = talloc_asprintf(cmd, "%u", bts->si_common.cell_ro_sel_par.cell_resel_off * 2);
|
|
|
|
if (!cmd->reply) {
|
|
|
|
cmd->reply = "OOM";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_bts_cell_reselection_offset(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
|
|
|
bts->si_common.cell_ro_sel_par.present = 1;
|
|
|
|
bts->si_common.cell_ro_sel_par.cell_resel_off = atoi(cmd->value) / 2;
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
CTRL_CMD_DEFINE(bts_cell_reselection_offset, "cell-reselection-offset");
|
|
|
|
|
2023-04-25 18:24:12 +00:00
|
|
|
static int verify_bts_cell_reselection_penalty_time(struct ctrl_cmd *cmd, const char *value, void *_data)
|
|
|
|
{
|
|
|
|
int penalty_time;
|
|
|
|
|
|
|
|
if (strcmp(value, "reserved") == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
penalty_time = atoi(value);
|
|
|
|
|
|
|
|
if (penalty_time < 20 || penalty_time > 620) {
|
|
|
|
cmd->reply = "Value is out of range";
|
|
|
|
return 1;
|
|
|
|
} else if (penalty_time % 20 != 0) {
|
|
|
|
cmd->reply = "Value must be a multiple of 20";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* According to 3GPP TS 45.008, PENALTY_TIME in the Control parameters section */
|
|
|
|
static int get_bts_cell_reselection_penalty_time(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
|
|
|
|
|
|
|
if (!bts->si_common.cell_ro_sel_par.present) {
|
|
|
|
cmd->reply = "0";
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bts->si_common.cell_ro_sel_par.penalty_time == 31) {
|
|
|
|
cmd->reply = "reserved";
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate the penalty time in seconds */
|
|
|
|
cmd->reply = talloc_asprintf(cmd, "%u", (bts->si_common.cell_ro_sel_par.penalty_time * 20) + 20);
|
|
|
|
if (!cmd->reply) {
|
|
|
|
cmd->reply = "OOM";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_bts_cell_reselection_penalty_time(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
|
|
|
bts->si_common.cell_ro_sel_par.present = 1;
|
|
|
|
|
|
|
|
if (strcmp(cmd->value, "reserved") == 0)
|
|
|
|
bts->si_common.cell_ro_sel_par.penalty_time = 31;
|
|
|
|
else
|
|
|
|
bts->si_common.cell_ro_sel_par.penalty_time = (atoi(cmd->value) - 20) / 20;
|
|
|
|
|
|
|
|
cmd->reply = "OK";
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
CTRL_CMD_DEFINE(bts_cell_reselection_penalty_time, "cell-reselection-penalty-time");
|
|
|
|
|
2023-04-26 07:58:55 +00:00
|
|
|
static int verify_bts_cell_reselection_hysteresis(struct ctrl_cmd *cmd, const char *value, void *_data)
|
|
|
|
{
|
|
|
|
const int cell_reselection_hysteresis = atoi(value);
|
|
|
|
|
|
|
|
if (cell_reselection_hysteresis < 0 || cell_reselection_hysteresis > 14) {
|
|
|
|
cmd->reply = "Value is out of range";
|
|
|
|
return 1;
|
|
|
|
} else if (cell_reselection_hysteresis % 2 != 0) {
|
|
|
|
cmd->reply = "Value must be even";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_bts_cell_reselection_hysteresis(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
|
|
|
|
|
|
|
cmd->reply = talloc_asprintf(cmd, "%u", bts->si_common.cell_sel_par.cell_resel_hyst * 2);
|
|
|
|
if (!cmd->reply) {
|
|
|
|
cmd->reply = "OOM";
|
|
|
|
return CTRL_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_bts_cell_reselection_hysteresis(struct ctrl_cmd *cmd, void *data)
|
|
|
|
{
|
|
|
|
struct gsm_bts *bts = cmd->node;
|
|
|
|
bts->si_common.cell_sel_par.cell_resel_hyst = atoi(cmd->value) / 2;
|
|
|
|
cmd->reply = "OK";
|
|
|
|
return CTRL_CMD_REPLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
CTRL_CMD_DEFINE(bts_cell_reselection_hysteresis, "cell-reselection-hysteresis");
|
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
int bsc_bts_ctrl_cmds_install(void)
|
2013-01-09 16:03:27 +00:00
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_loc);
|
2014-11-10 10:41:03 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_lac);
|
2014-11-21 09:54:42 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_ci);
|
2014-11-21 09:20:29 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_apply_config);
|
2014-11-21 10:18:45 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si);
|
2022-09-29 17:28:59 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_power_ctrl_defs);
|
2014-12-05 11:03:24 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_chan_load);
|
2014-12-05 13:44:21 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_conn);
|
2017-10-10 12:50:35 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_up);
|
2015-01-31 21:16:00 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_gprs_mode);
|
2015-02-10 20:37:16 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_state);
|
add CTRL 'rf_states' and 'bts.N.rf_states'
These commands return a listing of OML state, RF policy as well as RSL
connection status for each TRX in the form:
<bts_nr>,<trx_nr>,<opstate>,<adminstate>,<rf_policy>,<rsl_status>;<bts_nr>,<trx_nr>,...
For example, the root node 'rf_states' may return:
0,0,operational,unlocked,on,rsl-up;1,0,operational,unlocked,on,rsl-down;2,0,inoperational,locked,on,rsl-down;
A 'bts.N.rf_states' returns the same form of string, but lists only the
TRX for the given BTS nr.
Note, there is already a CTRL command 'bts.N.rf_state' (singular
'rf_state', not plural 'rf_states'), which only reflects the overall
status of all TRX combined. This new command has per-TRX resolution.
The rf-policy is so far always looked up in the global gsm_network flag,
as does the old 'rf_state' command; see osmo_bsc_rf_get_policy_by_bts()
which does not depend on the specific BTS at all. This may be worth
revisiting in the future, so I am already including the rf-policy in the
rf_state string for each TRX, even though it is globally identical.
Related: SYS#5542
Related: I01e6f391a5e71b0606c42be9b57f8a1687d59bcb (osmo-ttcn3-hacks)
Change-Id: I14fa2678fc8f2c11a879c5e9615ac552782c5b7e
2021-09-06 20:02:38 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_states);
|
power_control: implement BCCH carrier power reduction operation
The BCCH carrier (sometimes called C0) of a BTS shall maintain
discontinuous Downlink transmission at full power in order to
stay 'visible' to the mobile stations. Because of that, early
versions of 3GPP TS 45.008 prohibited BS power reduction on C0.
However, starting from version 13.0.0 (2015-11) there is a feature
called 'BCCH carrier power reduction operation'. This is a special
mode of operation, where the variation of RF level for some
timeslots is relaxed for the purpose of energy saving.
In BCCH carrier power reduction operation, for timeslots on the
C0 carrier, except timeslots carrying BCCH/CCCH, the output power
may be lower than the output power used for timeslots carrying
BCCH/CCCH. In this case the maximum allowed difference in output
power actually transmitted by the BTS is 6 dB.
Introduce a VTY command to turn on and off the BCCH carrier power
reduction operation. Also introduce a CTRL command. On the
A-bis/RSL, abuse the BS POWER CONTROL message by setting
the Channel Number IE to 0x80 (RSL_CHAN_BCCH).
Currently, only osmo-bts-trx is supported. A value greater than
zero makes it reduce the power on *inactive* timeslots of the
BCCH carrier. Sending zero disables the BCCH power reduction
mode completely.
For more details, see 3GPP TS 45.008, section 7.1, and 3GPP TR 45.926.
Change-Id: I047fce33d4d3e4c569dd006ba17858467a2f4783
Related: SYS#4919
2021-06-21 19:55:46 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_c0_power_red);
|
2021-10-27 15:28:49 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_neighbor_list_add);
|
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_neighbor_list_del);
|
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_neighbor_list_mode);
|
2023-04-05 23:29:25 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si2quater_neighbor_list_del_earfcn);
|
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si2quater_neighbor_list_del_uarfcn);
|
2023-04-25 07:23:40 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_cell_reselection_offset);
|
2023-04-25 18:24:12 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_cell_reselection_penalty_time);
|
2023-04-26 07:58:55 +00:00
|
|
|
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_cell_reselection_hysteresis);
|
2014-11-10 10:41:03 +00:00
|
|
|
|
2021-10-27 08:57:41 +00:00
|
|
|
rc |= neighbor_ident_ctrl_init();
|
|
|
|
|
2022-10-05 12:35:32 +00:00
|
|
|
rc = bsc_bts_trx_ctrl_cmds_install();
|
2013-01-09 18:55:04 +00:00
|
|
|
|
2013-01-09 16:03:27 +00:00
|
|
|
return rc;
|
|
|
|
}
|