pespin submitted this change.
Rearrange ctrl interface code
Current organization is totally mess, there's actually no organization
at all for lots of commands.
Let's organize most commands based on CTRL node where they are applied:
global, bts, trx, etc.
Specific set of commands such as neighbor-related, rf-related, etc.
are left in separate files as subsections inside the same node, so the
hierarchy is still clear.
Change-Id: I51a9b31780a4a8026aafb2d732369cdc10c8bb70
---
M include/osmocom/bsc/ctrl.h
M include/osmocom/bsc/gsm_data.h
M src/osmo-bsc/Makefile.am
A src/osmo-bsc/bsc_ctrl.c
D src/osmo-bsc/bsc_ctrl_commands.c
A src/osmo-bsc/bts_ctrl.c
A src/osmo-bsc/bts_trx_ctrl.c
D src/osmo-bsc/osmo_bsc_ctrl.c
8 files changed, 1,642 insertions(+), 1,595 deletions(-)
diff --git a/include/osmocom/bsc/ctrl.h b/include/osmocom/bsc/ctrl.h
index 04ca2cb..af42b39 100644
--- a/include/osmocom/bsc/ctrl.h
+++ b/include/osmocom/bsc/ctrl.h
@@ -2,9 +2,20 @@
#include <osmocom/ctrl/control_cmd.h>
+struct gsm_network;
+struct gsm_bts;
+struct bsc_msc_data;
+
struct ctrl_handle *bsc_controlif_setup(struct gsm_network *net,
const char *bind_addr, uint16_t port);
+/* Used internally in different ctrl source code files: */
+int bsc_bts_ctrl_cmds_install(void);
+int bsc_bts_trx_ctrl_cmds_install(void);
+void ctrl_generate_bts_location_state_trap(struct gsm_bts *bts, struct bsc_msc_data *msc);
+void osmo_bsc_send_trap(struct ctrl_cmd *cmd, struct bsc_msc_data *msc_data);
+
+
enum bsc_ctrl_node {
CTRL_NODE_MSC = _LAST_CTRL_NODE,
_LAST_CTRL_NODE_BSC
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index 52b20a8..37d7947 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -998,9 +998,6 @@
/* generic E1 line operations for all ISDN-based BTS. */
extern struct e1inp_line_ops bts_isdn_e1inp_line_ops;
-/* control interface handling */
-int bsc_base_ctrl_cmds_install(void);
-
bool ts_is_usable(const struct gsm_bts_trx_ts *ts);
int gsm_lchan_type_by_pchan(enum gsm_phys_chan_config pchan);
diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am
index c242e36..40381fe 100644
--- a/src/osmo-bsc/Makefile.am
+++ b/src/osmo-bsc/Makefile.am
@@ -33,7 +33,7 @@
abis_rsl.c \
acc.c \
assignment_fsm.c \
- bsc_ctrl_commands.c \
+ bsc_ctrl.c \
bsc_ctrl_lookup.c \
bsc_init.c \
bsc_rf_ctrl.c \
@@ -45,6 +45,7 @@
bsc_vty.c \
bts.c \
bts_trx.c \
+ bts_trx_ctrl.c \
bts_ericsson_rbs2000.c \
bts_init.c \
bts_ipaccess_nanobts.c \
@@ -54,6 +55,7 @@
bts_sm.c \
bts_osmobts.c \
bts_unknown.c \
+ bts_ctrl.c \
bts_vty.c \
bts_trx_vty.c \
chan_alloc.c \
@@ -93,7 +95,6 @@
nm_rcarrier_fsm.c \
gsm_08_08.c \
osmo_bsc_bssap.c \
- osmo_bsc_ctrl.c \
osmo_bsc_filter.c \
osmo_bsc_grace.c \
osmo_bsc_lcls.c \
diff --git a/src/osmo-bsc/bsc_ctrl.c b/src/osmo-bsc/bsc_ctrl.c
new file mode 100644
index 0000000..ca246e7
--- /dev/null
+++ b/src/osmo-bsc/bsc_ctrl.c
@@ -0,0 +1,857 @@
+/*
+ * (C) 2011 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2011 by On-Waves
+ * (C) 2011-2015 by Holger Hans Peter Freyther
+ * (C) 2013-2015 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 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 <errno.h>
+#include <time.h>
+
+#include <osmocom/gsm/ipa.h>
+
+#include <osmocom/ctrl/control_cmd.h>
+#include <osmocom/ctrl/control_if.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/misc.h>
+
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/bsc/ipaccess.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/chan_alloc.h>
+#include <osmocom/bsc/osmo_bsc_rf.h>
+#include <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/a_reset.h>
+#include <osmocom/bsc/ctrl.h>
+#include <osmocom/bsc/handover_ctrl.h>
+
+static int verify_net_apply_config_file(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ FILE *cfile;
+
+ if (!cmd->value || cmd->value[0] == '\0')
+ return -1;
+
+ cfile = fopen(cmd->value, "r");
+ if (!cfile)
+ return -1;
+
+ fclose(cfile);
+
+ return 0;
+}
+static int set_net_apply_config_file(struct ctrl_cmd *cmd, void *_data)
+{
+ int rc;
+ FILE *cfile;
+ unsigned cmd_ret = CTRL_CMD_ERROR;
+
+ LOGP(DCTRL, LOGL_NOTICE, "Applying VTY snippet from %s...\n", cmd->value);
+ cfile = fopen(cmd->value, "r");
+ if (!cfile) {
+ LOGP(DCTRL, LOGL_NOTICE, "Applying VTY snippet from %s: fopen() failed: %d\n",
+ cmd->value, errno);
+ cmd->reply = "NoFile";
+ return cmd_ret;
+ }
+
+ rc = vty_read_config_filep(cfile, NULL);
+ LOGP(DCTRL, LOGL_NOTICE, "Applying VTY snippet from %s returned %d\n", cmd->value, rc);
+ if (rc) {
+ cmd->reply = talloc_asprintf(cmd, "ParseError=%d", rc);
+ if (!cmd->reply)
+ cmd->reply = "OOM";
+ goto close_ret;
+ }
+
+ cmd->reply = "OK";
+ cmd_ret = CTRL_CMD_REPLY;
+close_ret:
+ fclose(cfile);
+ return cmd_ret;
+}
+CTRL_CMD_DEFINE_WO(net_apply_config_file, "apply-config-file");
+
+static int verify_net_write_config_file(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ return 0;
+}
+static int set_net_write_config_file(struct ctrl_cmd *cmd, void *_data)
+{
+ const char *cfile_name;
+ unsigned cmd_ret = CTRL_CMD_ERROR;
+
+ if (strcmp(cmd->value, "overwrite"))
+ host_config_set(cmd->value);
+
+ cfile_name = host_config_file();
+
+ LOGP(DCTRL, LOGL_NOTICE, "Writing VTY config to file %s...\n", cfile_name);
+ if (osmo_vty_write_config_file(cfile_name) < 0)
+ goto ret;
+
+ cmd->reply = "OK";
+ cmd_ret = CTRL_CMD_REPLY;
+ret:
+ return cmd_ret;
+}
+CTRL_CMD_DEFINE_WO(net_write_config_file, "write-config-file");
+
+CTRL_CMD_DEFINE(net_mcc, "mcc");
+static int get_net_mcc(struct ctrl_cmd *cmd, void *_data)
+{
+ struct gsm_network *net = cmd->node;
+ cmd->reply = talloc_asprintf(cmd, "%s", osmo_mcc_name(net->plmn.mcc));
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+ return CTRL_CMD_REPLY;
+}
+static int set_net_mcc(struct ctrl_cmd *cmd, void *_data)
+{
+ struct gsm_network *net = cmd->node;
+ uint16_t mcc;
+ if (osmo_mcc_from_str(cmd->value, &mcc))
+ return -1;
+ net->plmn.mcc = mcc;
+ return get_net_mcc(cmd, _data);
+}
+static int verify_net_mcc(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (osmo_mcc_from_str(value, NULL))
+ return -1;
+ return 0;
+}
+
+CTRL_CMD_DEFINE(net_mnc, "mnc");
+static int get_net_mnc(struct ctrl_cmd *cmd, void *_data)
+{
+ struct gsm_network *net = cmd->node;
+ cmd->reply = talloc_asprintf(cmd, "%s", osmo_mnc_name(net->plmn.mnc, net->plmn.mnc_3_digits));
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+ return CTRL_CMD_REPLY;
+}
+static int set_net_mnc(struct ctrl_cmd *cmd, void *_data)
+{
+ struct gsm_network *net = cmd->node;
+ struct osmo_plmn_id plmn = net->plmn;
+ if (osmo_mnc_from_str(cmd->value, &plmn.mnc, &plmn.mnc_3_digits)) {
+ cmd->reply = "Error while decoding MNC";
+ return CTRL_CMD_ERROR;
+ }
+ net->plmn = plmn;
+ return get_net_mnc(cmd, _data);
+}
+static int verify_net_mnc(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (osmo_mnc_from_str(value, NULL, NULL))
+ return -1;
+ return 0;
+}
+
+static int set_net_apply_config(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_network *net = cmd->node;
+ struct gsm_bts *bts;
+
+ llist_for_each_entry(bts, &net->bts_list, list) {
+ if (!is_ipaccess_bts(bts))
+ continue;
+
+ /*
+ * The ip.access nanoBTS seems to be unreliable on BSSGP
+ * so let's us just reboot it. For the sysmoBTS we can just
+ * restart the process as all state is gone.
+ */
+ if (!is_osmobts(bts) && strcmp(cmd->value, "restart") == 0) {
+ struct gsm_bts_trx *trx;
+ llist_for_each_entry_reverse(trx, &bts->trx_list, list)
+ abis_nm_ipaccess_restart(trx);
+ } else
+ ipaccess_drop_oml(bts, "ctrl net.apply-configuration");
+ }
+
+ cmd->reply = "Tried to drop the BTS";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_WO_NOVRF(net_apply_config, "apply-configuration");
+
+static int verify_net_mcc_mnc_apply(struct ctrl_cmd *cmd, const char *value, void *d)
+{
+ char *tmp, *saveptr, *mcc, *mnc;
+ int rc = 0;
+
+ tmp = talloc_strdup(cmd, value);
+ if (!tmp)
+ return 1;
+
+ mcc = strtok_r(tmp, ",", &saveptr);
+ mnc = strtok_r(NULL, ",", &saveptr);
+
+ if (osmo_mcc_from_str(mcc, NULL) || osmo_mnc_from_str(mnc, NULL, NULL))
+ rc = -1;
+
+ talloc_free(tmp);
+ return rc;
+}
+
+static int set_net_mcc_mnc_apply(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_network *net = cmd->node;
+ char *tmp, *saveptr, *mcc_str, *mnc_str;
+ struct osmo_plmn_id plmn;
+
+ tmp = talloc_strdup(cmd, cmd->value);
+ if (!tmp)
+ goto oom;
+
+ mcc_str = strtok_r(tmp, ",", &saveptr);
+ mnc_str = strtok_r(NULL, ",", &saveptr);
+
+ if (osmo_mcc_from_str(mcc_str, &plmn.mcc)) {
+ cmd->reply = "Error while decoding MCC";
+ talloc_free(tmp);
+ return CTRL_CMD_ERROR;
+ }
+
+ if (osmo_mnc_from_str(mnc_str, &plmn.mnc, &plmn.mnc_3_digits)) {
+ cmd->reply = "Error while decoding MNC";
+ talloc_free(tmp);
+ return CTRL_CMD_ERROR;
+ }
+
+ talloc_free(tmp);
+
+ if (!osmo_plmn_cmp(&net->plmn, &plmn)) {
+ cmd->reply = "Nothing changed";
+ return CTRL_CMD_REPLY;
+ }
+
+ net->plmn = plmn;
+
+ return set_net_apply_config(cmd, data);
+
+oom:
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+}
+CTRL_CMD_DEFINE_WO(net_mcc_mnc_apply, "mcc-mnc-apply");
+
+static int get_net_rf_lock(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_network *net = cmd->node;
+ struct gsm_bts *bts;
+ const char *policy_name;
+
+ policy_name = osmo_bsc_rf_get_policy_name(net->rf_ctrl->policy);
+
+ llist_for_each_entry(bts, &net->bts_list, list) {
+ struct gsm_bts_trx *trx;
+
+ /* Exclude the BTS from the global lock */
+ if (bts->excl_from_rf_lock)
+ continue;
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ if (trx->mo.nm_state.availability == NM_AVSTATE_OK &&
+ trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) {
+ cmd->reply = talloc_asprintf(cmd,
+ "state=on,policy=%s,bts=%u,trx=%u",
+ policy_name, bts->nr, trx->nr);
+ return CTRL_CMD_REPLY;
+ }
+ }
+ }
+
+ cmd->reply = talloc_asprintf(cmd, "state=off,policy=%s",
+ policy_name);
+ return CTRL_CMD_REPLY;
+}
+
+#define TIME_FORMAT_RFC2822 "%a, %d %b %Y %T %z"
+
+static int set_net_rf_lock(struct ctrl_cmd *cmd, void *data)
+{
+ int locked = atoi(cmd->value);
+ struct gsm_network *net = cmd->node;
+ time_t now = time(NULL);
+ char now_buf[64];
+ struct osmo_bsc_rf *rf;
+
+ if (!net) {
+ cmd->reply = "net not found.";
+ return CTRL_CMD_ERROR;
+ }
+
+ rf = net->rf_ctrl;
+
+ if (!rf) {
+ cmd->reply = "RF Ctrl is not enabled in the BSC Configuration";
+ return CTRL_CMD_ERROR;
+ }
+
+ talloc_free(rf->last_rf_lock_ctrl_command);
+ strftime(now_buf, sizeof(now_buf), TIME_FORMAT_RFC2822, gmtime(&now));
+ rf->last_rf_lock_ctrl_command =
+ talloc_asprintf(rf, "rf_locked %u (%s)", locked, now_buf);
+
+ osmo_bsc_rf_schedule_lock(rf, locked == 1 ? '0' : '1');
+
+ cmd->reply = talloc_asprintf(cmd, "%u", locked);
+ if (!cmd->reply) {
+ cmd->reply = "OOM.";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+static int verify_net_rf_lock(struct ctrl_cmd *cmd, const char *value, void *data)
+{
+ int locked = atoi(cmd->value);
+
+ if ((locked != 0) && (locked != 1))
+ return 1;
+
+ return 0;
+}
+CTRL_CMD_DEFINE(net_rf_lock, "rf_locked");
+
+static int get_net_bts_num(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_network *net = cmd->node;
+
+ cmd->reply = talloc_asprintf(cmd, "%u", net->num_bts);
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_RO(net_bts_num, "number-of-bts");
+
+/* Return a list of the states of each TRX for all 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();
+ */
+static int get_net_rf_states(struct ctrl_cmd *cmd, void *data)
+{
+ cmd->reply = bsc_rf_states_c(cmd);
+ if (!cmd->reply) {
+ cmd->reply = "OOM.";
+ return CTRL_CMD_ERROR;
+ }
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_RO(net_rf_states, "rf_states");
+
+CTRL_CMD_DEFINE(net_timezone, "timezone");
+static int get_net_timezone(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_network *net = (struct gsm_network *)cmd->node;
+
+ struct gsm_tz *tz = &net->tz;
+ if (tz->override)
+ cmd->reply = talloc_asprintf(cmd, "%d,%d,%d",
+ tz->hr, tz->mn, tz->dst);
+ else
+ cmd->reply = talloc_asprintf(cmd, "off");
+
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+static int set_net_timezone(struct ctrl_cmd *cmd, void *data)
+{
+ char *saveptr, *hourstr, *minstr, *dststr, *tmp = 0;
+ int override = 0;
+ struct gsm_network *net = (struct gsm_network *)cmd->node;
+ struct gsm_tz *tz = &net->tz;
+
+ tmp = talloc_strdup(cmd, cmd->value);
+ if (!tmp)
+ goto oom;
+
+ hourstr = strtok_r(tmp, ",", &saveptr);
+ minstr = strtok_r(NULL, ",", &saveptr);
+ dststr = strtok_r(NULL, ",", &saveptr);
+
+ if (hourstr != NULL) {
+ override = strcasecmp(hourstr, "off") != 0;
+ if (override) {
+ tz->hr = atol(hourstr);
+ tz->mn = minstr ? atol(minstr) : 0;
+ tz->dst = dststr ? atol(dststr) : 0;
+ }
+ }
+
+ tz->override = override;
+
+
+ talloc_free(tmp);
+ tmp = NULL;
+
+ return get_net_timezone(cmd, data);
+
+oom:
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+}
+
+static int verify_net_timezone(struct ctrl_cmd *cmd, const char *value, void *data)
+{
+ char *saveptr, *hourstr, *minstr, *dststr, *tmp;
+ int override, tz_hours, tz_mins, tz_dst;
+
+ tmp = talloc_strdup(cmd, value);
+ if (!tmp)
+ return 1;
+
+ hourstr = strtok_r(tmp, ",", &saveptr);
+ minstr = strtok_r(NULL, ",", &saveptr);
+ dststr = strtok_r(NULL, ",", &saveptr);
+
+ if (hourstr == NULL)
+ goto err;
+
+ override = strcasecmp(hourstr, "off") != 0;
+
+ if (!override) {
+ talloc_free(tmp);
+ return 0;
+ }
+
+ if (minstr == NULL || dststr == NULL)
+ goto err;
+
+ tz_hours = atol(hourstr);
+ tz_mins = atol(minstr);
+ tz_dst = atol(dststr);
+
+ talloc_free(tmp);
+ tmp = NULL;
+
+ if ((tz_hours < -19) || (tz_hours > 19) ||
+ (tz_mins < 0) || (tz_mins >= 60) || (tz_mins % 15 != 0) ||
+ (tz_dst < 0) || (tz_dst > 2))
+ goto err;
+
+ return 0;
+
+err:
+ talloc_free(tmp);
+ cmd->reply = talloc_strdup(cmd, "The format is <hours>,<mins>,<dst> or 'off' where -19 <= hours <= 19, mins in {0, 15, 30, 45}, and 0 <= dst <= 2");
+ return 1;
+}
+
+CTRL_CMD_DEFINE_RO(bts_connection_status, "bts_connection_status");
+static int bts_connection_status = 0;
+
+static int get_bts_connection_status(struct ctrl_cmd *cmd, void *data)
+{
+ if (bts_connection_status)
+ cmd->reply = "connected";
+ else
+ cmd->reply = "disconnected";
+ return CTRL_CMD_REPLY;
+}
+
+static int bts_connection_status_trap_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data)
+{
+ struct ctrl_cmd *cmd;
+ struct gsm_network *gsmnet = (struct gsm_network *)handler_data;
+ struct gsm_bts *bts;
+ int bts_current_status;
+
+ if (signal != S_L_INP_TEI_DN && signal != S_L_INP_TEI_UP) {
+ return 0;
+ }
+
+ bts_current_status = 0;
+ /* Check if OML on at least one BTS is up */
+ llist_for_each_entry(bts, &gsmnet->bts_list, list) {
+ if (bts->oml_link) {
+ bts_current_status = 1;
+ break;
+ }
+ }
+ if (bts_connection_status == 0 && bts_current_status == 1) {
+ LOGP(DCTRL, LOGL_DEBUG, "BTS connection (re)established, sending TRAP.\n");
+ } else if (bts_connection_status == 1 && bts_current_status == 0) {
+ LOGP(DCTRL, LOGL_DEBUG, "No more BTS connected, sending TRAP.\n");
+ } else {
+ return 0;
+ }
+
+ cmd = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
+ if (!cmd) {
+ LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n");
+ return 0;
+ }
+
+ bts_connection_status = bts_current_status;
+
+ cmd->id = "0";
+ cmd->variable = "bts_connection_status";
+
+ get_bts_connection_status(cmd, NULL);
+
+ ctrl_cmd_send_to_all(gsmnet->ctrl, cmd);
+
+ talloc_free(cmd);
+
+ return 0;
+}
+
+CTRL_CMD_DEFINE_RO(msc_connection_status, "connection_status");
+static int get_msc_connection_status(struct ctrl_cmd *cmd, void *data)
+{
+ struct bsc_msc_data *msc = (struct bsc_msc_data *)cmd->node;
+
+ if (msc == NULL) {
+ cmd->reply = "msc not found";
+ return CTRL_CMD_ERROR;
+ }
+ if (a_reset_conn_ready(msc))
+ cmd->reply = "connected";
+ else
+ cmd->reply = "disconnected";
+ return CTRL_CMD_REPLY;
+}
+
+/* Backwards compat. */
+CTRL_CMD_DEFINE_RO(msc0_connection_status, "msc_connection_status");
+
+static int get_msc0_connection_status(struct ctrl_cmd *cmd, void *data)
+{
+ struct bsc_msc_data *msc = osmo_msc_data_find(bsc_gsmnet, 0);
+ void *old_node = cmd->node;
+ int rc;
+
+ cmd->node = msc;
+ rc = get_msc_connection_status(cmd, data);
+ cmd->node = old_node;
+
+ return rc;
+}
+
+static int msc_connection_status_trap_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data)
+{
+ struct ctrl_cmd *cmd;
+ struct gsm_network *gsmnet = (struct gsm_network *)handler_data;
+ struct bsc_msc_data *msc = (struct bsc_msc_data *)signal_data;
+
+ if (signal == S_MSC_LOST) {
+ LOGP(DCTRL, LOGL_DEBUG, "MSC connection lost, sending TRAP.\n");
+ } else if (signal == S_MSC_CONNECTED) {
+ LOGP(DCTRL, LOGL_DEBUG, "MSC connection (re)established, sending TRAP.\n");
+ } else {
+ return 0;
+ }
+
+ cmd = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
+ if (!cmd) {
+ LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n");
+ return 0;
+ }
+
+ cmd->id = "0";
+ cmd->variable = talloc_asprintf(cmd, "msc.%d.connection_status", msc->nr);
+ cmd->node = msc;
+
+ get_msc_connection_status(cmd, NULL);
+
+ ctrl_cmd_send_to_all(gsmnet->ctrl, cmd);
+
+ if (msc->nr == 0) {
+ /* Backwards compat. */
+ cmd->variable = "msc_connection_status";
+ ctrl_cmd_send_to_all(gsmnet->ctrl, cmd);
+ }
+
+ talloc_free(cmd);
+
+ return 0;
+}
+
+static int msc_signal_handler(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct msc_signal_data *msc;
+ struct gsm_network *net;
+ struct gsm_bts *bts;
+
+ if (subsys != SS_MSC)
+ return 0;
+ if (signal != S_MSC_AUTHENTICATED)
+ return 0;
+
+ msc = signal_data;
+
+ net = msc->data->network;
+ llist_for_each_entry(bts, &net->bts_list, list)
+ ctrl_generate_bts_location_state_trap(bts, msc->data);
+
+ return 0;
+}
+
+/* Obtain SS7 application server currently handling given MSC (DPC) */
+static struct osmo_ss7_as *msc_get_ss7_as(struct bsc_msc_data *msc)
+{
+ struct osmo_ss7_route *rt;
+ struct osmo_ss7_instance *ss7 = osmo_sccp_get_ss7(msc->a.sccp);
+ rt = osmo_ss7_route_lookup(ss7, msc->a.msc_addr.pc);
+ if (!rt)
+ return NULL;
+ return rt->dest.as;
+}
+
+static int _ss7_as_send(struct osmo_ss7_as *as, struct msgb *msg)
+{
+ struct osmo_ss7_asp *asp;
+ unsigned int i;
+
+ /* FIXME: unify with xua_as_transmit_msg() and perform proper ASP lookup */
+ for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
+ asp = as->cfg.asps[i];
+ if (!asp)
+ continue;
+ /* FIXME: deal with multiple ASPs per AS */
+ return osmo_ss7_asp_send(asp, msg);
+ }
+ msgb_free(msg);
+ return -1;
+}
+
+int bsc_sccplite_msc_send(struct bsc_msc_data *msc, struct msgb *msg)
+{
+ struct osmo_ss7_as *as;
+
+ as = msc_get_ss7_as(msc);
+ if (!as) {
+ msgb_free(msg);
+ return -1;
+ }
+
+ /* don't attempt to send CTRL on a non-SCCPlite AS */
+ if (as->cfg.proto != OSMO_SS7_ASP_PROT_IPA)
+ return 0;
+
+ return _ss7_as_send(as, msg);
+}
+
+/* Encode a CTRL command and send it to the given ASP
+ * \param[in] asp ASP through which we shall send the encoded message
+ * \param[in] cmd decoded CTRL command to be encoded and sent. Ownership is *NOT*
+ * transferred, to permit caller to send the same CMD to several ASPs.
+ * Caller must hence free 'cmd' itself.
+ * \returns 0 on success; negative on error */
+static int sccplite_asp_ctrl_cmd_send(struct osmo_ss7_asp *asp, struct ctrl_cmd *cmd)
+{
+ /* this is basically like libosmoctrl:ctrl_cmd_send(), not for a dedicated
+ * CTRL connection but for the CTRL piggy-back on the IPA/SCCPlite link */
+ struct msgb *msg;
+
+ /* don't attempt to send CTRL on a non-SCCPlite ASP */
+ if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA)
+ return 0;
+
+ msg = ctrl_cmd_make(cmd);
+ if (!msg)
+ return -1;
+
+ ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
+ ipa_prepend_header(msg, IPAC_PROTO_OSMO);
+
+ return osmo_ss7_asp_send(asp, msg);
+}
+
+/* Ownership of 'cmd' is *NOT* transferred, to permit caller to send the same CMD to several ASPs.
+ * Caller must hence free 'cmd' itself. */
+static int sccplite_msc_ctrl_cmd_send(struct bsc_msc_data *msc, struct ctrl_cmd *cmd)
+{
+ struct msgb *msg;
+
+ msg = ctrl_cmd_make(cmd);
+ if (!msg)
+ return -1;
+
+ ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
+ ipa_prepend_header(msg, IPAC_PROTO_OSMO);
+
+ return bsc_sccplite_msc_send(msc, msg);
+}
+
+/* receive + process a CTRL command from the piggy-back on the IPA/SCCPlite link.
+ * Transfers msg ownership. */
+int bsc_sccplite_rx_ctrl(struct osmo_ss7_asp *asp, struct msgb *msg)
+{
+ struct ctrl_cmd *cmd;
+ bool parse_failed;
+ int rc;
+
+ /* caller has already ensured ipaccess_head + ipaccess_head_ext */
+ OSMO_ASSERT(msg->l2h);
+
+ /* prase raw (ASCII) CTRL command into ctrl_cmd */
+ cmd = ctrl_cmd_parse3(asp, msg, &parse_failed);
+ OSMO_ASSERT(cmd);
+ msgb_free(msg);
+ if (cmd->type == CTRL_TYPE_ERROR && parse_failed)
+ goto send_reply;
+
+ /* handle the CTRL command */
+ ctrl_cmd_handle(bsc_gsmnet->ctrl, cmd, bsc_gsmnet);
+
+send_reply:
+ rc = sccplite_asp_ctrl_cmd_send(asp, cmd);
+ talloc_free(cmd);
+ return rc;
+}
+
+
+void osmo_bsc_send_trap(struct ctrl_cmd *cmd, struct bsc_msc_data *msc_data)
+{
+ struct ctrl_cmd *trap;
+ struct ctrl_handle *ctrl;
+
+ ctrl = msc_data->network->ctrl;
+
+ trap = ctrl_cmd_trap(cmd);
+ if (!trap) {
+
+ LOGP(DCTRL, LOGL_ERROR, "Failed to create trap.\n");
+ return;
+ }
+
+ ctrl_cmd_send_to_all(ctrl, trap);
+ sccplite_msc_ctrl_cmd_send(msc_data, trap);
+
+ talloc_free(trap);
+}
+
+CTRL_CMD_DEFINE_WO_NOVRF(net_notification, "notification");
+static int set_net_notification(struct ctrl_cmd *cmd, void *data)
+{
+ struct ctrl_cmd *trap;
+ struct gsm_network *net;
+
+ net = cmd->node;
+
+ trap = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
+ if (!trap) {
+ LOGP(DCTRL, LOGL_ERROR, "Trap creation failed\n");
+ goto handled;
+ }
+
+ trap->id = "0";
+ trap->variable = "notification";
+ trap->reply = talloc_strdup(trap, cmd->value);
+
+ /*
+ * This should only be sent to local systems. In the future
+ * we might even ask for systems to register to receive
+ * the notifications.
+ */
+ ctrl_cmd_send_to_all(net->ctrl, trap);
+ talloc_free(trap);
+
+handled:
+ return CTRL_CMD_HANDLED;
+}
+
+CTRL_CMD_DEFINE_WO_NOVRF(net_inform_msc, "inform-msc-v1");
+static int set_net_inform_msc(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_network *net;
+ struct bsc_msc_data *msc;
+
+ net = cmd->node;
+ llist_for_each_entry(msc, &net->mscs, entry) {
+ struct ctrl_cmd *trap;
+
+ trap = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
+ if (!trap) {
+ LOGP(DCTRL, LOGL_ERROR, "Trap creation failed\n");
+ continue;
+ }
+
+ trap->id = "0";
+ trap->variable = "inform-msc-v1";
+ trap->reply = talloc_strdup(trap, cmd->value);
+ sccplite_msc_ctrl_cmd_send(msc, trap);
+ talloc_free(trap);
+ }
+
+
+ return CTRL_CMD_HANDLED;
+}
+
+static int bsc_base_ctrl_cmds_install(struct gsm_network *net)
+{
+ int rc = 0;
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_apply_config_file);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_write_config_file);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mnc);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_apply_config);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc_mnc_apply);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_rf_lock);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_bts_num);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_rf_states);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_timezone);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_bts_connection_status);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_msc0_connection_status);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_notification);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_inform_msc);
+
+ rc = ctrl_cmd_install(CTRL_NODE_MSC, &cmd_msc_connection_status);
+
+ rc |= osmo_signal_register_handler(SS_L_INPUT, &bts_connection_status_trap_cb, net);
+ rc |= osmo_signal_register_handler(SS_MSC, &msc_connection_status_trap_cb, net);
+ rc |= osmo_signal_register_handler(SS_MSC, msc_signal_handler, NULL);
+
+ return rc;
+}
+
+
+int bsc_ctrl_cmds_install(struct gsm_network *net)
+{
+ int rc;
+
+ rc = bsc_base_ctrl_cmds_install(net);
+ if (rc)
+ goto end;
+ rc = bsc_ho_ctrl_cmds_install(net);
+ if (rc)
+ goto end;
+ rc = bsc_bts_ctrl_cmds_install();
+ if (rc)
+ goto end;
+end:
+ return rc;
+}
diff --git a/src/osmo-bsc/bsc_ctrl_commands.c b/src/osmo-bsc/bsc_ctrl_commands.c
deleted file mode 100644
index fd0f5d2..0000000
--- a/src/osmo-bsc/bsc_ctrl_commands.c
+++ /dev/null
@@ -1,848 +0,0 @@
-/*
- * (C) 2013-2015 by Holger Hans Peter Freyther
- * (C) 2013-2015 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 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 <errno.h>
-#include <time.h>
-
-#include <osmocom/ctrl/control_cmd.h>
-
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/misc.h>
-
-#include <osmocom/gsm/gsm48.h>
-#include <osmocom/bsc/ipaccess.h>
-#include <osmocom/bsc/gsm_data.h>
-#include <osmocom/bsc/abis_nm.h>
-#include <osmocom/bsc/debug.h>
-#include <osmocom/bsc/chan_alloc.h>
-#include <osmocom/bsc/osmo_bsc_rf.h>
-#include <osmocom/bsc/bsc_msc_data.h>
-#include <osmocom/bsc/bts.h>
-#include <osmocom/bsc/neighbor_ident.h>
-
-static int verify_net_apply_config_file(struct ctrl_cmd *cmd, const char *value, void *_data)
-{
- FILE *cfile;
-
- if (!cmd->value || cmd->value[0] == '\0')
- return -1;
-
- cfile = fopen(cmd->value, "r");
- if (!cfile)
- return -1;
-
- fclose(cfile);
-
- return 0;
-}
-static int set_net_apply_config_file(struct ctrl_cmd *cmd, void *_data)
-{
- int rc;
- FILE *cfile;
- unsigned cmd_ret = CTRL_CMD_ERROR;
-
- LOGP(DCTRL, LOGL_NOTICE, "Applying VTY snippet from %s...\n", cmd->value);
- cfile = fopen(cmd->value, "r");
- if (!cfile) {
- LOGP(DCTRL, LOGL_NOTICE, "Applying VTY snippet from %s: fopen() failed: %d\n",
- cmd->value, errno);
- cmd->reply = "NoFile";
- return cmd_ret;
- }
-
- rc = vty_read_config_filep(cfile, NULL);
- LOGP(DCTRL, LOGL_NOTICE, "Applying VTY snippet from %s returned %d\n", cmd->value, rc);
- if (rc) {
- cmd->reply = talloc_asprintf(cmd, "ParseError=%d", rc);
- if (!cmd->reply)
- cmd->reply = "OOM";
- goto close_ret;
- }
-
- cmd->reply = "OK";
- cmd_ret = CTRL_CMD_REPLY;
-close_ret:
- fclose(cfile);
- return cmd_ret;
-}
-CTRL_CMD_DEFINE_WO(net_apply_config_file, "apply-config-file");
-
-static int verify_net_write_config_file(struct ctrl_cmd *cmd, const char *value, void *_data)
-{
- return 0;
-}
-static int set_net_write_config_file(struct ctrl_cmd *cmd, void *_data)
-{
- const char *cfile_name;
- unsigned cmd_ret = CTRL_CMD_ERROR;
-
- if (strcmp(cmd->value, "overwrite"))
- host_config_set(cmd->value);
-
- cfile_name = host_config_file();
-
- LOGP(DCTRL, LOGL_NOTICE, "Writing VTY config to file %s...\n", cfile_name);
- if (osmo_vty_write_config_file(cfile_name) < 0)
- goto ret;
-
- cmd->reply = "OK";
- cmd_ret = CTRL_CMD_REPLY;
-ret:
- return cmd_ret;
-}
-CTRL_CMD_DEFINE_WO(net_write_config_file, "write-config-file");
-
-CTRL_CMD_DEFINE(net_mcc, "mcc");
-static int get_net_mcc(struct ctrl_cmd *cmd, void *_data)
-{
- struct gsm_network *net = cmd->node;
- cmd->reply = talloc_asprintf(cmd, "%s", osmo_mcc_name(net->plmn.mcc));
- if (!cmd->reply) {
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
- }
- return CTRL_CMD_REPLY;
-}
-static int set_net_mcc(struct ctrl_cmd *cmd, void *_data)
-{
- struct gsm_network *net = cmd->node;
- uint16_t mcc;
- if (osmo_mcc_from_str(cmd->value, &mcc))
- return -1;
- net->plmn.mcc = mcc;
- return get_net_mcc(cmd, _data);
-}
-static int verify_net_mcc(struct ctrl_cmd *cmd, const char *value, void *_data)
-{
- if (osmo_mcc_from_str(value, NULL))
- return -1;
- return 0;
-}
-
-CTRL_CMD_DEFINE(net_mnc, "mnc");
-static int get_net_mnc(struct ctrl_cmd *cmd, void *_data)
-{
- struct gsm_network *net = cmd->node;
- cmd->reply = talloc_asprintf(cmd, "%s", osmo_mnc_name(net->plmn.mnc, net->plmn.mnc_3_digits));
- if (!cmd->reply) {
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
- }
- return CTRL_CMD_REPLY;
-}
-static int set_net_mnc(struct ctrl_cmd *cmd, void *_data)
-{
- struct gsm_network *net = cmd->node;
- struct osmo_plmn_id plmn = net->plmn;
- if (osmo_mnc_from_str(cmd->value, &plmn.mnc, &plmn.mnc_3_digits)) {
- cmd->reply = "Error while decoding MNC";
- return CTRL_CMD_ERROR;
- }
- net->plmn = plmn;
- return get_net_mnc(cmd, _data);
-}
-static int verify_net_mnc(struct ctrl_cmd *cmd, const char *value, void *_data)
-{
- if (osmo_mnc_from_str(value, NULL, NULL))
- return -1;
- return 0;
-}
-
-static int set_net_apply_config(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = cmd->node;
- struct gsm_bts *bts;
-
- llist_for_each_entry(bts, &net->bts_list, list) {
- if (!is_ipaccess_bts(bts))
- continue;
-
- /*
- * The ip.access nanoBTS seems to be unreliable on BSSGP
- * so let's us just reboot it. For the sysmoBTS we can just
- * restart the process as all state is gone.
- */
- if (!is_osmobts(bts) && strcmp(cmd->value, "restart") == 0) {
- struct gsm_bts_trx *trx;
- llist_for_each_entry_reverse(trx, &bts->trx_list, list)
- abis_nm_ipaccess_restart(trx);
- } else
- ipaccess_drop_oml(bts, "ctrl net.apply-configuration");
- }
-
- cmd->reply = "Tried to drop the BTS";
- return CTRL_CMD_REPLY;
-}
-
-CTRL_CMD_DEFINE_WO_NOVRF(net_apply_config, "apply-configuration");
-
-static int verify_net_mcc_mnc_apply(struct ctrl_cmd *cmd, const char *value, void *d)
-{
- char *tmp, *saveptr, *mcc, *mnc;
- int rc = 0;
-
- tmp = talloc_strdup(cmd, value);
- if (!tmp)
- return 1;
-
- mcc = strtok_r(tmp, ",", &saveptr);
- mnc = strtok_r(NULL, ",", &saveptr);
-
- if (osmo_mcc_from_str(mcc, NULL) || osmo_mnc_from_str(mnc, NULL, NULL))
- rc = -1;
-
- talloc_free(tmp);
- return rc;
-}
-
-static int set_net_mcc_mnc_apply(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = cmd->node;
- char *tmp, *saveptr, *mcc_str, *mnc_str;
- struct osmo_plmn_id plmn;
-
- tmp = talloc_strdup(cmd, cmd->value);
- if (!tmp)
- goto oom;
-
- mcc_str = strtok_r(tmp, ",", &saveptr);
- mnc_str = strtok_r(NULL, ",", &saveptr);
-
- if (osmo_mcc_from_str(mcc_str, &plmn.mcc)) {
- cmd->reply = "Error while decoding MCC";
- talloc_free(tmp);
- return CTRL_CMD_ERROR;
- }
-
- if (osmo_mnc_from_str(mnc_str, &plmn.mnc, &plmn.mnc_3_digits)) {
- cmd->reply = "Error while decoding MNC";
- talloc_free(tmp);
- return CTRL_CMD_ERROR;
- }
-
- talloc_free(tmp);
-
- if (!osmo_plmn_cmp(&net->plmn, &plmn)) {
- cmd->reply = "Nothing changed";
- return CTRL_CMD_REPLY;
- }
-
- net->plmn = plmn;
-
- return set_net_apply_config(cmd, data);
-
-oom:
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
-}
-CTRL_CMD_DEFINE_WO(net_mcc_mnc_apply, "mcc-mnc-apply");
-
-/* BTS related commands below */
-CTRL_CMD_DEFINE_RANGE(bts_lac, "location-area-code", struct gsm_bts, location_area_code, 0, 65535);
-CTRL_CMD_DEFINE_RANGE(bts_ci, "cell-identity", struct gsm_bts, cell_identity, 0, 65535);
-
-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;
- }
-
- ipaccess_drop_oml(bts, "ctrl bts.apply-configuration");
- cmd->reply = "Tried to drop the BTS";
- return CTRL_CMD_REPLY;
-}
-
-CTRL_CMD_DEFINE_WO_NOVRF(bts_apply_config, "apply-configuration");
-
-static int set_bts_si(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_bts *bts = cmd->node;
- int rc;
-
- rc = gsm_bts_set_system_infos(bts);
- if (rc != 0) {
- cmd->reply = "Failed to generate SI";
- return CTRL_CMD_ERROR;
- }
-
- cmd->reply = "Generated new System Information";
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_WO_NOVRF(bts_si, "send-new-system-informations");
-
-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");
-
-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));
- bts_chan_load(&pl, bts);
-
- 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;
- if (i == GSM_PCHAN_UNKNOWN)
- continue;
-
- 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;
-}
-
-CTRL_CMD_DEFINE_RO(bts_chan_load, "channel-load");
-
-static int get_bts_oml_conn(struct ctrl_cmd *cmd, void *data)
-{
- const struct gsm_bts *bts = cmd->node;
-
- cmd->reply = get_model_oml_status(bts);
-
- return CTRL_CMD_REPLY;
-}
-
-CTRL_CMD_DEFINE_RO(bts_oml_conn, "oml-connection-state");
-
-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");
-
-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");
-
-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) {
- cmd->reply = "OOM.";
- return CTRL_CMD_ERROR;
- }
-
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_RO(bts_rf_state, "rf_state");
-
-/* 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();
- */
-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) {
- cmd->reply = "OOM.";
- return CTRL_CMD_ERROR;
- }
-
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_RO(bts_rf_states, "rf_states");
-
-/* Return a list of the states of each TRX for all 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();
- */
-static int get_net_rf_states(struct ctrl_cmd *cmd, void *data)
-{
- cmd->reply = bsc_rf_states_c(cmd);
- if (!cmd->reply) {
- cmd->reply = "OOM.";
- return CTRL_CMD_ERROR;
- }
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_RO(net_rf_states, "rf_states");
-
-static int get_net_rf_lock(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = cmd->node;
- struct gsm_bts *bts;
- const char *policy_name;
-
- policy_name = osmo_bsc_rf_get_policy_name(net->rf_ctrl->policy);
-
- llist_for_each_entry(bts, &net->bts_list, list) {
- struct gsm_bts_trx *trx;
-
- /* Exclude the BTS from the global lock */
- if (bts->excl_from_rf_lock)
- continue;
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- if (trx->mo.nm_state.availability == NM_AVSTATE_OK &&
- trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) {
- cmd->reply = talloc_asprintf(cmd,
- "state=on,policy=%s,bts=%u,trx=%u",
- policy_name, bts->nr, trx->nr);
- return CTRL_CMD_REPLY;
- }
- }
- }
-
- cmd->reply = talloc_asprintf(cmd, "state=off,policy=%s",
- policy_name);
- return CTRL_CMD_REPLY;
-}
-
-#define TIME_FORMAT_RFC2822 "%a, %d %b %Y %T %z"
-
-static int set_net_rf_lock(struct ctrl_cmd *cmd, void *data)
-{
- int locked = atoi(cmd->value);
- struct gsm_network *net = cmd->node;
- time_t now = time(NULL);
- char now_buf[64];
- struct osmo_bsc_rf *rf;
-
- if (!net) {
- cmd->reply = "net not found.";
- return CTRL_CMD_ERROR;
- }
-
- rf = net->rf_ctrl;
-
- if (!rf) {
- cmd->reply = "RF Ctrl is not enabled in the BSC Configuration";
- return CTRL_CMD_ERROR;
- }
-
- talloc_free(rf->last_rf_lock_ctrl_command);
- strftime(now_buf, sizeof(now_buf), TIME_FORMAT_RFC2822, gmtime(&now));
- rf->last_rf_lock_ctrl_command =
- talloc_asprintf(rf, "rf_locked %u (%s)", locked, now_buf);
-
- osmo_bsc_rf_schedule_lock(rf, locked == 1 ? '0' : '1');
-
- cmd->reply = talloc_asprintf(cmd, "%u", locked);
- if (!cmd->reply) {
- cmd->reply = "OOM.";
- return CTRL_CMD_ERROR;
- }
-
- return CTRL_CMD_REPLY;
-}
-
-static int verify_net_rf_lock(struct ctrl_cmd *cmd, const char *value, void *data)
-{
- int locked = atoi(cmd->value);
-
- if ((locked != 0) && (locked != 1))
- return 1;
-
- return 0;
-}
-CTRL_CMD_DEFINE(net_rf_lock, "rf_locked");
-
-static int get_trx_rf_locked(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_bts_trx *trx = cmd->node;
- /* Return rf_locked = 1 only if it is explicitly locked. If it is in shutdown or null state, do not "trick" the
- * caller into thinking that sending "rf_locked 0" is necessary to bring the TRX up. */
- cmd->reply = (trx->mo.nm_state.administrative == NM_STATE_LOCKED) ? "1" : "0";
- return CTRL_CMD_REPLY;
-}
-
-static int set_trx_rf_locked(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_bts_trx *trx = cmd->node;
- int locked;
- if (osmo_str_to_int(&locked, cmd->value, 10, 0, 1)) {
- cmd->reply = "Invalid value";
- return CTRL_CMD_ERROR;
- }
-
- gsm_trx_lock_rf(trx, locked, "ctrl");
-
- /* Let's not assume the nm FSM has already switched its state, just return the intended rf_locked value. */
- cmd->reply = locked ? "1" : "0";
- return CTRL_CMD_REPLY;
-}
-
-static int verify_trx_rf_locked(struct ctrl_cmd *cmd, const char *value, void *data)
-{
- return osmo_str_to_int(NULL, value, 10, 0, 1);
-}
-CTRL_CMD_DEFINE(trx_rf_locked, "rf_locked");
-
-static int get_net_bts_num(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = cmd->node;
-
- cmd->reply = talloc_asprintf(cmd, "%u", net->num_bts);
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_RO(net_bts_num, "number-of-bts");
-
-/* TRX related commands below here */
-CTRL_HELPER_GET_INT(trx_max_power, struct gsm_bts_trx, max_power_red);
-static int verify_trx_max_power(struct ctrl_cmd *cmd, const char *value, void *_data)
-{
- int tmp = atoi(value);
-
- if (tmp < 0 || tmp > 22) {
- cmd->reply = "Value must be between 0 and 22";
- return -1;
- }
-
- if (tmp & 1) {
- cmd->reply = "Value must be even";
- return -1;
- }
-
- return 0;
-}
-CTRL_CMD_DEFINE_RANGE(trx_arfcn, "arfcn", struct gsm_bts_trx, arfcn, 0, 1023);
-
-static int set_trx_max_power(struct ctrl_cmd *cmd, void *_data)
-{
- struct gsm_bts_trx *trx = cmd->node;
- int old_power;
-
- /* remember the old value, set the new one */
- old_power = trx->max_power_red;
- trx->max_power_red = atoi(cmd->value);
-
- /* Maybe update the value */
- if (old_power != trx->max_power_red) {
- LOGP(DCTRL, LOGL_NOTICE,
- "%s updating max_pwr_red(%d)\n",
- gsm_trx_name(trx), trx->max_power_red);
- abis_nm_update_max_power_red(trx);
- }
-
- return get_trx_max_power(cmd, _data);
-}
-CTRL_CMD_DEFINE(trx_max_power, "max-power-reduction");
-
-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) {
- cmd->reply = "OOM.";
- 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);
- if (rc == -ENOTSUP) {
- cmd->reply = "BCCH carrier power reduction is not supported";
- return CTRL_CMD_ERROR;
- } else if (rc != 0) {
- cmd->reply = "Failed to enable BCCH carrier power reduction";
- return CTRL_CMD_ERROR;
- }
-
- return get_bts_c0_power_red(cmd, data);
-}
-
-CTRL_CMD_DEFINE(bts_c0_power_red, "c0-power-reduction");
-
-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;
- int mode = NL_MODE_AUTOMATIC;
-
- 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");
-
-int bsc_base_ctrl_cmds_install(void)
-{
- int rc = 0;
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_apply_config_file);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_write_config_file);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mnc);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_apply_config);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc_mnc_apply);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_rf_lock);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_bts_num);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_rf_states);
-
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_lac);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_ci);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_apply_config);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_power_ctrl_defs);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_chan_load);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_conn);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_up);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_gprs_mode);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_state);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_states);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_c0_power_red);
- 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);
-
- rc |= neighbor_ident_ctrl_init();
-
- rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_max_power);
- rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_arfcn);
- rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_rf_locked);
-
- return rc;
-}
diff --git a/src/osmo-bsc/bts_ctrl.c b/src/osmo-bsc/bts_ctrl.c
new file mode 100644
index 0000000..728d89f
--- /dev/null
+++ b/src/osmo-bsc/bts_ctrl.c
@@ -0,0 +1,652 @@
+/*
+ * (C) 2013-2015 by Holger Hans Peter Freyther
+ * (C) 2013-2022 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 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 <errno.h>
+#include <time.h>
+
+#include <osmocom/ctrl/control_cmd.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/misc.h>
+
+#include <osmocom/bsc/ctrl.h>
+#include <osmocom/bsc/osmo_bsc_rf.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/ipaccess.h>
+#include <osmocom/bsc/chan_alloc.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/neighbor_ident.h>
+
+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);
+}
+
+static int get_bts_loc(struct ctrl_cmd *cmd, void *data);
+
+void ctrl_generate_bts_location_state_trap(struct gsm_bts *bts, struct bsc_msc_data *msc)
+{
+ struct ctrl_cmd *cmd;
+ const char *oper, *admin, *policy;
+
+ cmd = ctrl_cmd_create(msc, CTRL_TYPE_TRAP);
+ if (!cmd) {
+ LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n");
+ return;
+ }
+
+ cmd->id = "0";
+ cmd->variable = talloc_asprintf(cmd, "bts.%d.location-state", bts->nr);
+
+ /* Prepare the location reply */
+ cmd->node = bts;
+ get_bts_loc(cmd, NULL);
+
+ 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_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));
+
+ osmo_bsc_send_trap(cmd, msc);
+ talloc_free(cmd);
+}
+
+void bsc_gen_location_state_trap(struct gsm_bts *bts)
+{
+ struct bsc_msc_data *msc;
+
+ llist_for_each_entry(msc, &bts->network->mscs, entry)
+ ctrl_generate_bts_location_state_trap(bts, msc);
+}
+
+CTRL_CMD_DEFINE(bts_loc, "location");
+static int get_bts_loc(struct ctrl_cmd *cmd, void *data)
+{
+ struct bts_location *curloc;
+ struct gsm_bts *bts = (struct gsm_bts *) cmd->node;
+ if (!bts) {
+ cmd->reply = "bts not found.";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (llist_empty(&bts->loc_list)) {
+ cmd->reply = talloc_asprintf(cmd, "0,invalid,0,0,0");
+ return CTRL_CMD_REPLY;
+ }
+
+ curloc = llist_entry(bts->loc_list.next, struct bts_location, list);
+
+ 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;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+static int set_bts_loc(struct ctrl_cmd *cmd, void *data)
+{
+ 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;
+ }
+
+ tmp = talloc_strdup(cmd, cmd->value);
+ if (!tmp)
+ goto oom;
+
+ 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);
+
+ /* 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)) {
+ talloc_free(tmp);
+ cmd->reply = "parse error";
+ return CTRL_CMD_ERROR;
+ }
+
+ curloc = talloc_zero(tall_bsc_ctx, struct bts_location);
+ if (!curloc) {
+ talloc_free(tmp);
+ goto oom;
+ }
+ INIT_LLIST_HEAD(&curloc->list);
+
+ 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);
+ talloc_free(tmp);
+
+ 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);
+
+ ret = get_bts_loc(cmd, data);
+
+ if (!location_equal(curloc, lastloc))
+ bsc_gen_location_state_trap(bts);
+
+ cleanup_locations(&bts->loc_list);
+
+ return ret;
+
+oom:
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+}
+
+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;
+}
+
+/* BTS related commands below */
+CTRL_CMD_DEFINE_RANGE(bts_lac, "location-area-code", struct gsm_bts, location_area_code, 0, 65535);
+CTRL_CMD_DEFINE_RANGE(bts_ci, "cell-identity", struct gsm_bts, cell_identity, 0, 65535);
+
+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;
+ }
+
+ ipaccess_drop_oml(bts, "ctrl bts.apply-configuration");
+ cmd->reply = "Tried to drop the BTS";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_WO_NOVRF(bts_apply_config, "apply-configuration");
+
+static int set_bts_si(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ rc = gsm_bts_set_system_infos(bts);
+ if (rc != 0) {
+ cmd->reply = "Failed to generate SI";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "Generated new System Information";
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_WO_NOVRF(bts_si, "send-new-system-informations");
+
+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");
+
+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));
+ bts_chan_load(&pl, bts);
+
+ 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;
+ if (i == GSM_PCHAN_UNKNOWN)
+ continue;
+
+ 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;
+}
+
+CTRL_CMD_DEFINE_RO(bts_chan_load, "channel-load");
+
+static int get_bts_oml_conn(struct ctrl_cmd *cmd, void *data)
+{
+ const struct gsm_bts *bts = cmd->node;
+
+ cmd->reply = get_model_oml_status(bts);
+
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_RO(bts_oml_conn, "oml-connection-state");
+
+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");
+
+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");
+
+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) {
+ cmd->reply = "OOM.";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_RO(bts_rf_state, "rf_state");
+
+/* 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();
+ */
+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) {
+ cmd->reply = "OOM.";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_RO(bts_rf_states, "rf_states");
+
+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) {
+ cmd->reply = "OOM.";
+ 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);
+ if (rc == -ENOTSUP) {
+ cmd->reply = "BCCH carrier power reduction is not supported";
+ return CTRL_CMD_ERROR;
+ } else if (rc != 0) {
+ cmd->reply = "Failed to enable BCCH carrier power reduction";
+ return CTRL_CMD_ERROR;
+ }
+
+ return get_bts_c0_power_red(cmd, data);
+}
+
+CTRL_CMD_DEFINE(bts_c0_power_red, "c0-power-reduction");
+
+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;
+ int mode = NL_MODE_AUTOMATIC;
+
+ 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");
+
+int bsc_bts_ctrl_cmds_install(void)
+{
+ int rc = 0;
+
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_loc);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_lac);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_ci);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_apply_config);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_power_ctrl_defs);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_chan_load);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_conn);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_up);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_gprs_mode);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_state);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_states);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_c0_power_red);
+ 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);
+
+ rc |= neighbor_ident_ctrl_init();
+
+ rc = bsc_bts_trx_ctrl_cmds_install();
+
+ return rc;
+}
diff --git a/src/osmo-bsc/bts_trx_ctrl.c b/src/osmo-bsc/bts_trx_ctrl.c
new file mode 100644
index 0000000..584f4bb
--- /dev/null
+++ b/src/osmo-bsc/bts_trx_ctrl.c
@@ -0,0 +1,119 @@
+/*
+ * (C) 2013-2015 by Holger Hans Peter Freyther
+ * (C) 2013-2022 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 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 <errno.h>
+#include <time.h>
+
+#include <osmocom/ctrl/control_cmd.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/misc.h>
+
+#include <osmocom/bsc/ctrl.h>
+#include <osmocom/bsc/osmo_bsc_rf.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/ipaccess.h>
+#include <osmocom/bsc/chan_alloc.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/neighbor_ident.h>
+
+static int get_trx_rf_locked(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts_trx *trx = cmd->node;
+ /* Return rf_locked = 1 only if it is explicitly locked. If it is in shutdown or null state, do not "trick" the
+ * caller into thinking that sending "rf_locked 0" is necessary to bring the TRX up. */
+ cmd->reply = (trx->mo.nm_state.administrative == NM_STATE_LOCKED) ? "1" : "0";
+ return CTRL_CMD_REPLY;
+}
+
+static int set_trx_rf_locked(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts_trx *trx = cmd->node;
+ int locked;
+ if (osmo_str_to_int(&locked, cmd->value, 10, 0, 1)) {
+ cmd->reply = "Invalid value";
+ return CTRL_CMD_ERROR;
+ }
+
+ gsm_trx_lock_rf(trx, locked, "ctrl");
+
+ /* Let's not assume the nm FSM has already switched its state, just return the intended rf_locked value. */
+ cmd->reply = locked ? "1" : "0";
+ return CTRL_CMD_REPLY;
+}
+
+static int verify_trx_rf_locked(struct ctrl_cmd *cmd, const char *value, void *data)
+{
+ return osmo_str_to_int(NULL, value, 10, 0, 1);
+}
+CTRL_CMD_DEFINE(trx_rf_locked, "rf_locked");
+
+/* TRX related commands below here */
+CTRL_HELPER_GET_INT(trx_max_power, struct gsm_bts_trx, max_power_red);
+static int verify_trx_max_power(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ int tmp = atoi(value);
+
+ if (tmp < 0 || tmp > 22) {
+ cmd->reply = "Value must be between 0 and 22";
+ return -1;
+ }
+
+ if (tmp & 1) {
+ cmd->reply = "Value must be even";
+ return -1;
+ }
+
+ return 0;
+}
+CTRL_CMD_DEFINE_RANGE(trx_arfcn, "arfcn", struct gsm_bts_trx, arfcn, 0, 1023);
+
+static int set_trx_max_power(struct ctrl_cmd *cmd, void *_data)
+{
+ struct gsm_bts_trx *trx = cmd->node;
+ int old_power;
+
+ /* remember the old value, set the new one */
+ old_power = trx->max_power_red;
+ trx->max_power_red = atoi(cmd->value);
+
+ /* Maybe update the value */
+ if (old_power != trx->max_power_red) {
+ LOGP(DCTRL, LOGL_NOTICE,
+ "%s updating max_pwr_red(%d)\n",
+ gsm_trx_name(trx), trx->max_power_red);
+ abis_nm_update_max_power_red(trx);
+ }
+
+ return get_trx_max_power(cmd, _data);
+}
+CTRL_CMD_DEFINE(trx_max_power, "max-power-reduction");
+
+int bsc_bts_trx_ctrl_cmds_install(void)
+{
+ int rc = 0;
+
+ rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_max_power);
+ rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_arfcn);
+ rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_rf_locked);
+
+ return rc;
+}
diff --git a/src/osmo-bsc/osmo_bsc_ctrl.c b/src/osmo-bsc/osmo_bsc_ctrl.c
deleted file mode 100644
index 969efb5..0000000
--- a/src/osmo-bsc/osmo_bsc_ctrl.c
+++ /dev/null
@@ -1,742 +0,0 @@
-/* (C) 2011 by Daniel Willmann <daniel@totalueberwachung.de>
- * (C) 2011 by Holger Hans Peter Freyther
- * (C) 2011 by On-Waves
- * 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 <osmocom/ctrl/control_cmd.h>
-#include <osmocom/bsc/ctrl.h>
-#include <osmocom/bsc/debug.h>
-#include <osmocom/bsc/gsm_data.h>
-#include <osmocom/bsc/osmo_bsc.h>
-#include <osmocom/bsc/osmo_bsc_rf.h>
-#include <osmocom/bsc/bsc_msc_data.h>
-#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>
-
-#include <osmocom/ctrl/control_if.h>
-
-#include <osmocom/gsm/protocol/ipaccess.h>
-#include <osmocom/gsm/ipa.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-
-/* Obtain SS7 application server currently handling given MSC (DPC) */
-static struct osmo_ss7_as *msc_get_ss7_as(struct bsc_msc_data *msc)
-{
- struct osmo_ss7_route *rt;
- struct osmo_ss7_instance *ss7 = osmo_sccp_get_ss7(msc->a.sccp);
- rt = osmo_ss7_route_lookup(ss7, msc->a.msc_addr.pc);
- if (!rt)
- return NULL;
- return rt->dest.as;
-}
-
-static int _ss7_as_send(struct osmo_ss7_as *as, struct msgb *msg)
-{
- struct osmo_ss7_asp *asp;
- unsigned int i;
-
- /* FIXME: unify with xua_as_transmit_msg() and perform proper ASP lookup */
- for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
- asp = as->cfg.asps[i];
- if (!asp)
- continue;
- /* FIXME: deal with multiple ASPs per AS */
- return osmo_ss7_asp_send(asp, msg);
- }
- msgb_free(msg);
- return -1;
-}
-
-int bsc_sccplite_msc_send(struct bsc_msc_data *msc, struct msgb *msg)
-{
- struct osmo_ss7_as *as;
-
- as = msc_get_ss7_as(msc);
- if (!as) {
- msgb_free(msg);
- return -1;
- }
-
- /* don't attempt to send CTRL on a non-SCCPlite AS */
- if (as->cfg.proto != OSMO_SS7_ASP_PROT_IPA)
- return 0;
-
- return _ss7_as_send(as, msg);
-}
-
-/* Encode a CTRL command and send it to the given ASP
- * \param[in] asp ASP through which we shall send the encoded message
- * \param[in] cmd decoded CTRL command to be encoded and sent. Ownership is *NOT*
- * transferred, to permit caller to send the same CMD to several ASPs.
- * Caller must hence free 'cmd' itself.
- * \returns 0 on success; negative on error */
-static int sccplite_asp_ctrl_cmd_send(struct osmo_ss7_asp *asp, struct ctrl_cmd *cmd)
-{
- /* this is basically like libosmoctrl:ctrl_cmd_send(), not for a dedicated
- * CTRL connection but for the CTRL piggy-back on the IPA/SCCPlite link */
- struct msgb *msg;
-
- /* don't attempt to send CTRL on a non-SCCPlite ASP */
- if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA)
- return 0;
-
- msg = ctrl_cmd_make(cmd);
- if (!msg)
- return -1;
-
- ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
- ipa_prepend_header(msg, IPAC_PROTO_OSMO);
-
- return osmo_ss7_asp_send(asp, msg);
-}
-
-/* Ownership of 'cmd' is *NOT* transferred, to permit caller to send the same CMD to several ASPs.
- * Caller must hence free 'cmd' itself. */
-static int sccplite_msc_ctrl_cmd_send(struct bsc_msc_data *msc, struct ctrl_cmd *cmd)
-{
- struct msgb *msg;
-
- msg = ctrl_cmd_make(cmd);
- if (!msg)
- return -1;
-
- ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
- ipa_prepend_header(msg, IPAC_PROTO_OSMO);
-
- return bsc_sccplite_msc_send(msc, msg);
-}
-
-/* receive + process a CTRL command from the piggy-back on the IPA/SCCPlite link.
- * Transfers msg ownership. */
-int bsc_sccplite_rx_ctrl(struct osmo_ss7_asp *asp, struct msgb *msg)
-{
- struct ctrl_cmd *cmd;
- bool parse_failed;
- int rc;
-
- /* caller has already ensured ipaccess_head + ipaccess_head_ext */
- OSMO_ASSERT(msg->l2h);
-
- /* prase raw (ASCII) CTRL command into ctrl_cmd */
- cmd = ctrl_cmd_parse3(asp, msg, &parse_failed);
- OSMO_ASSERT(cmd);
- msgb_free(msg);
- if (cmd->type == CTRL_TYPE_ERROR && parse_failed)
- goto send_reply;
-
- /* handle the CTRL command */
- ctrl_cmd_handle(bsc_gsmnet->ctrl, cmd, bsc_gsmnet);
-
-send_reply:
- rc = sccplite_asp_ctrl_cmd_send(asp, cmd);
- talloc_free(cmd);
- return rc;
-}
-
-
-void osmo_bsc_send_trap(struct ctrl_cmd *cmd, struct bsc_msc_data *msc_data)
-{
- struct ctrl_cmd *trap;
- struct ctrl_handle *ctrl;
-
- ctrl = msc_data->network->ctrl;
-
- trap = ctrl_cmd_trap(cmd);
- if (!trap) {
-
- LOGP(DCTRL, LOGL_ERROR, "Failed to create trap.\n");
- return;
- }
-
- ctrl_cmd_send_to_all(ctrl, trap);
- sccplite_msc_ctrl_cmd_send(msc_data, trap);
-
- talloc_free(trap);
-}
-
-CTRL_CMD_DEFINE_RO(msc_connection_status, "connection_status");
-static int get_msc_connection_status(struct ctrl_cmd *cmd, void *data)
-{
- struct bsc_msc_data *msc = (struct bsc_msc_data *)cmd->node;
-
- if (msc == NULL) {
- cmd->reply = "msc not found";
- return CTRL_CMD_ERROR;
- }
- if (a_reset_conn_ready(msc))
- cmd->reply = "connected";
- else
- cmd->reply = "disconnected";
- return CTRL_CMD_REPLY;
-}
-
-/* Backwards compat. */
-CTRL_CMD_DEFINE_RO(msc0_connection_status, "msc_connection_status");
-
-static int get_msc0_connection_status(struct ctrl_cmd *cmd, void *data)
-{
- struct bsc_msc_data *msc = osmo_msc_data_find(bsc_gsmnet, 0);
- void *old_node = cmd->node;
- int rc;
-
- cmd->node = msc;
- rc = get_msc_connection_status(cmd, data);
- cmd->node = old_node;
-
- return rc;
-}
-
-static int msc_connection_status_trap_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data)
-{
- struct ctrl_cmd *cmd;
- struct gsm_network *gsmnet = (struct gsm_network *)handler_data;
- struct bsc_msc_data *msc = (struct bsc_msc_data *)signal_data;
-
- if (signal == S_MSC_LOST) {
- LOGP(DCTRL, LOGL_DEBUG, "MSC connection lost, sending TRAP.\n");
- } else if (signal == S_MSC_CONNECTED) {
- LOGP(DCTRL, LOGL_DEBUG, "MSC connection (re)established, sending TRAP.\n");
- } else {
- return 0;
- }
-
- cmd = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
- if (!cmd) {
- LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n");
- return 0;
- }
-
- cmd->id = "0";
- cmd->variable = talloc_asprintf(cmd, "msc.%d.connection_status", msc->nr);
- cmd->node = msc;
-
- get_msc_connection_status(cmd, NULL);
-
- ctrl_cmd_send_to_all(gsmnet->ctrl, cmd);
-
- if (msc->nr == 0) {
- /* Backwards compat. */
- cmd->variable = "msc_connection_status";
- ctrl_cmd_send_to_all(gsmnet->ctrl, cmd);
- }
-
- talloc_free(cmd);
-
- return 0;
-}
-
-CTRL_CMD_DEFINE_RO(bts_connection_status, "bts_connection_status");
-static int bts_connection_status = 0;
-
-static int get_bts_connection_status(struct ctrl_cmd *cmd, void *data)
-{
- if (bts_connection_status)
- cmd->reply = "connected";
- else
- cmd->reply = "disconnected";
- return CTRL_CMD_REPLY;
-}
-
-static int bts_connection_status_trap_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data)
-{
- struct ctrl_cmd *cmd;
- struct gsm_network *gsmnet = (struct gsm_network *)handler_data;
- struct gsm_bts *bts;
- int bts_current_status;
-
- if (signal != S_L_INP_TEI_DN && signal != S_L_INP_TEI_UP) {
- return 0;
- }
-
- bts_current_status = 0;
- /* Check if OML on at least one BTS is up */
- llist_for_each_entry(bts, &gsmnet->bts_list, list) {
- if (bts->oml_link) {
- bts_current_status = 1;
- break;
- }
- }
- if (bts_connection_status == 0 && bts_current_status == 1) {
- LOGP(DCTRL, LOGL_DEBUG, "BTS connection (re)established, sending TRAP.\n");
- } else if (bts_connection_status == 1 && bts_current_status == 0) {
- LOGP(DCTRL, LOGL_DEBUG, "No more BTS connected, sending TRAP.\n");
- } else {
- return 0;
- }
-
- cmd = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
- if (!cmd) {
- LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n");
- return 0;
- }
-
- bts_connection_status = bts_current_status;
-
- cmd->id = "0";
- cmd->variable = "bts_connection_status";
-
- get_bts_connection_status(cmd, NULL);
-
- ctrl_cmd_send_to_all(gsmnet->ctrl, cmd);
-
- talloc_free(cmd);
-
- return 0;
-}
-
-static int get_bts_loc(struct ctrl_cmd *cmd, void *data);
-
-static void generate_location_state_trap(struct gsm_bts *bts, struct bsc_msc_data *msc)
-{
- struct ctrl_cmd *cmd;
- const char *oper, *admin, *policy;
-
- cmd = ctrl_cmd_create(msc, CTRL_TYPE_TRAP);
- if (!cmd) {
- LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n");
- return;
- }
-
- cmd->id = "0";
- cmd->variable = talloc_asprintf(cmd, "bts.%i.location-state", bts->nr);
-
- /* Prepare the location reply */
- cmd->node = bts;
- get_bts_loc(cmd, NULL);
-
- 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_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));
-
- osmo_bsc_send_trap(cmd, msc);
- talloc_free(cmd);
-}
-
-void bsc_gen_location_state_trap(struct gsm_bts *bts)
-{
- struct bsc_msc_data *msc;
-
- llist_for_each_entry(msc, &bts->network->mscs, entry)
- generate_location_state_trap(bts, msc);
-}
-
-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 %i positions.\n", i);
-}
-
-CTRL_CMD_DEFINE(bts_loc, "location");
-static int get_bts_loc(struct ctrl_cmd *cmd, void *data)
-{
- struct bts_location *curloc;
- struct gsm_bts *bts = (struct gsm_bts *) cmd->node;
- if (!bts) {
- cmd->reply = "bts not found.";
- return CTRL_CMD_ERROR;
- }
-
- if (llist_empty(&bts->loc_list)) {
- cmd->reply = talloc_asprintf(cmd, "0,invalid,0,0,0");
- return CTRL_CMD_REPLY;
- } else {
- curloc = llist_entry(bts->loc_list.next, struct bts_location, list);
- }
-
- 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;
- }
-
- return CTRL_CMD_REPLY;
-}
-
-static int set_bts_loc(struct ctrl_cmd *cmd, void *data)
-{
- 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;
- }
-
- tmp = talloc_strdup(cmd, cmd->value);
- if (!tmp)
- goto oom;
-
- 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);
-
- /* 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)) {
- talloc_free(tmp);
- cmd->reply = "parse error";
- return CTRL_CMD_ERROR;
- }
-
- curloc = talloc_zero(tall_bsc_ctx, struct bts_location);
- if (!curloc) {
- talloc_free(tmp);
- goto oom;
- }
- INIT_LLIST_HEAD(&curloc->list);
-
- 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);
- talloc_free(tmp);
-
- 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);
-
- ret = get_bts_loc(cmd, data);
-
- if (!location_equal(curloc, lastloc))
- bsc_gen_location_state_trap(bts);
-
- cleanup_locations(&bts->loc_list);
-
- return ret;
-
-oom:
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
-}
-
-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;
-}
-
-CTRL_CMD_DEFINE(net_timezone, "timezone");
-static int get_net_timezone(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = (struct gsm_network*)cmd->node;
-
- struct gsm_tz *tz = &net->tz;
- if (tz->override)
- cmd->reply = talloc_asprintf(cmd, "%d,%d,%d",
- tz->hr, tz->mn, tz->dst);
- else
- cmd->reply = talloc_asprintf(cmd, "off");
-
- if (!cmd->reply) {
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
- }
-
- return CTRL_CMD_REPLY;
-}
-
-static int set_net_timezone(struct ctrl_cmd *cmd, void *data)
-{
- char *saveptr, *hourstr, *minstr, *dststr, *tmp = 0;
- int override = 0;
- struct gsm_network *net = (struct gsm_network*)cmd->node;
- struct gsm_tz *tz = &net->tz;
-
- tmp = talloc_strdup(cmd, cmd->value);
- if (!tmp)
- goto oom;
-
- hourstr = strtok_r(tmp, ",", &saveptr);
- minstr = strtok_r(NULL, ",", &saveptr);
- dststr = strtok_r(NULL, ",", &saveptr);
-
- if (hourstr != NULL) {
- override = strcasecmp(hourstr, "off") != 0;
- if (override) {
- tz->hr = atol(hourstr);
- tz->mn = minstr ? atol(minstr) : 0;
- tz->dst = dststr ? atol(dststr) : 0;
- }
- }
-
- tz->override = override;
-
-
- talloc_free(tmp);
- tmp = NULL;
-
- return get_net_timezone(cmd, data);
-
-oom:
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
-}
-
-static int verify_net_timezone(struct ctrl_cmd *cmd, const char *value, void *data)
-{
- char *saveptr, *hourstr, *minstr, *dststr, *tmp;
- int override, tz_hours, tz_mins, tz_dst;
-
- tmp = talloc_strdup(cmd, value);
- if (!tmp)
- return 1;
-
- hourstr = strtok_r(tmp, ",", &saveptr);
- minstr = strtok_r(NULL, ",", &saveptr);
- dststr = strtok_r(NULL, ",", &saveptr);
-
- if (hourstr == NULL)
- goto err;
-
- override = strcasecmp(hourstr, "off") != 0;
-
- if (!override) {
- talloc_free(tmp);
- return 0;
- }
-
- if (minstr == NULL || dststr == NULL)
- goto err;
-
- tz_hours = atol(hourstr);
- tz_mins = atol(minstr);
- tz_dst = atol(dststr);
-
- talloc_free(tmp);
- tmp = NULL;
-
- if ((tz_hours < -19) || (tz_hours > 19) ||
- (tz_mins < 0) || (tz_mins >= 60) || (tz_mins % 15 != 0) ||
- (tz_dst < 0) || (tz_dst > 2))
- goto err;
-
- return 0;
-
-err:
- talloc_free(tmp);
- cmd->reply = talloc_strdup(cmd, "The format is <hours>,<mins>,<dst> or 'off' where -19 <= hours <= 19, mins in {0, 15, 30, 45}, and 0 <= dst <= 2");
- return 1;
-}
-
-CTRL_CMD_DEFINE_WO_NOVRF(net_notification, "notification");
-static int set_net_notification(struct ctrl_cmd *cmd, void *data)
-{
- struct ctrl_cmd *trap;
- struct gsm_network *net;
-
- net = cmd->node;
-
- trap = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
- if (!trap) {
- LOGP(DCTRL, LOGL_ERROR, "Trap creation failed\n");
- goto handled;
- }
-
- trap->id = "0";
- trap->variable = "notification";
- trap->reply = talloc_strdup(trap, cmd->value);
-
- /*
- * This should only be sent to local systems. In the future
- * we might even ask for systems to register to receive
- * the notifications.
- */
- ctrl_cmd_send_to_all(net->ctrl, trap);
- talloc_free(trap);
-
-handled:
- return CTRL_CMD_HANDLED;
-}
-
-CTRL_CMD_DEFINE_WO_NOVRF(net_inform_msc, "inform-msc-v1");
-static int set_net_inform_msc(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net;
- struct bsc_msc_data *msc;
-
- net = cmd->node;
- llist_for_each_entry(msc, &net->mscs, entry) {
- struct ctrl_cmd *trap;
-
- trap = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
- if (!trap) {
- LOGP(DCTRL, LOGL_ERROR, "Trap creation failed\n");
- continue;
- }
-
- trap->id = "0";
- trap->variable = "inform-msc-v1";
- trap->reply = talloc_strdup(trap, cmd->value);
- sccplite_msc_ctrl_cmd_send(msc, trap);
- talloc_free(trap);
- }
-
-
- return CTRL_CMD_HANDLED;
-}
-
-static int msc_signal_handler(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct msc_signal_data *msc;
- struct gsm_network *net;
- struct gsm_bts *bts;
-
- if (subsys != SS_MSC)
- return 0;
- if (signal != S_MSC_AUTHENTICATED)
- return 0;
-
- msc = signal_data;
-
- net = msc->data->network;
- llist_for_each_entry(bts, &net->bts_list, list)
- generate_location_state_trap(bts, msc->data);
-
- return 0;
-}
-
-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);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_timezone);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_MSC, &cmd_msc_connection_status);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_msc0_connection_status);
- if (rc)
- goto end;
- rc = osmo_signal_register_handler(SS_MSC, &msc_connection_status_trap_cb, net);
- if (rc)
- goto end;
- rc = osmo_signal_register_handler(SS_MSC, msc_signal_handler, NULL);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_bts_connection_status);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_notification);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_inform_msc);
- if (rc)
- goto end;
- rc = osmo_signal_register_handler(SS_L_INPUT, &bts_connection_status_trap_cb, net);
-
-end:
- return rc;
-}
To view, visit change 29631. To unsubscribe, or for help writing mail filters, visit settings.