bts: Add some simple dependency between different BTS

E.g. for the sysmoBTS2050 we have the requirement that the first
board connects before the second due clocking. The easiest point
to enforce this is the BSC. Add a simple bitmask based system to
allow to express dependencies for IP based systems.
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index e237ea2..89db48b 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -436,4 +436,9 @@
 int bsc_base_ctrl_cmds_install(void);
 int msc_ctrl_cmds_install(void);
 
+/* dependency handling */
+void bts_depend_mark(struct gsm_bts *bts, int dep);
+void bts_depend_clear(struct gsm_bts *bts, int dep);
+int bts_depend_check(struct gsm_bts *bts);
+
 #endif /* _GSM_DATA_H */
diff --git a/openbsc/include/openbsc/gsm_data_shared.h b/openbsc/include/openbsc/gsm_data_shared.h
index 001a526..5d84969 100644
--- a/openbsc/include/openbsc/gsm_data_shared.h
+++ b/openbsc/include/openbsc/gsm_data_shared.h
@@ -720,6 +720,9 @@
 
 	/* supported codecs beside FR */
 	struct bts_codec_conf codec;
+
+	/* BTS dependencies bit field */
+	uint32_t depends_on[256/(8*4)];
 #endif /* ROLE_BSC */
 	void *role;
 };
diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
index 08f9a8e..d6d66c6 100644
--- a/openbsc/src/libbsc/bsc_vty.c
+++ b/openbsc/src/libbsc/bsc_vty.c
@@ -650,6 +650,23 @@
 	vty_out(vty, "  %sforce-combined-si%s",
 		bts->force_combined_si ? "" : "no ", VTY_NEWLINE);
 
+	for (i = 0; i < ARRAY_SIZE(bts->depends_on); ++i) {
+		int j;
+
+		if (bts->depends_on[i] == 0)
+			continue;
+
+		for (j = 0; j < sizeof(bts->depends_on[i]) * 8; ++j) {
+			int bts_nr;
+
+			if ((bts->depends_on[i] & (1<<j)) == 0)
+				continue;
+
+			bts_nr = (i * sizeof(bts->depends_on[i]) * 8) + j;
+			vty_out(vty, "  depends-on-bts %d%s", bts_nr, VTY_NEWLINE);
+		}
+	}
+
 	config_write_bts_model(vty, bts);
 }
 
@@ -2772,6 +2789,50 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_bts_depends_on, cfg_bts_depends_on_cmd,
+	"depends-on-bts <0-255>",
+	"This BTS can only be started if another one is up\n" "BTS Number\n")
+{
+	struct gsm_bts *bts = vty->index;
+	struct gsm_bts *other_bts;
+	int dep = atoi(argv[0]);
+
+
+	if (!is_ipaccess_bts(bts)) {
+		vty_out(vty, "This feature is only available for IP systems.%s",
+			VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	other_bts = gsm_bts_num(bts->network, dep);
+	if (!other_bts || !is_ipaccess_bts(other_bts)) {
+		vty_out(vty, "This feature is only available for IP systems.%s",
+			VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	if (dep >= bts->nr) {
+		vty_out(vty, "%%Need to depend on an already declared unit.%s",
+			VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	bts_depend_mark(bts, dep);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_no_depends_on, cfg_bts_no_depends_on_cmd,
+	"depeneds-on-bts <0-255>",
+	NO_STR "This BTS can only be started if another one is up\n"
+	"BTS Number\n")
+{
+	struct gsm_bts *bts = vty->index;
+	int dep = atoi(argv[0]);
+
+	bts_depend_clear(bts, dep);
+	return CMD_SUCCESS;
+}
+
 #define TRX_TEXT "Radio Transceiver\n"
 
 /* per TRX configuration */
@@ -3383,6 +3444,8 @@
 	install_element(BTS_NODE, &cfg_bts_codec2_cmd);
 	install_element(BTS_NODE, &cfg_bts_codec3_cmd);
 	install_element(BTS_NODE, &cfg_bts_codec4_cmd);
+	install_element(BTS_NODE, &cfg_bts_depends_on_cmd);
+	install_element(BTS_NODE, &cfg_bts_no_depends_on_cmd);
 
 	install_element(BTS_NODE, &cfg_trx_cmd);
 	install_node(&trx_node, dummy_config_write);
diff --git a/openbsc/src/libbsc/bts_ipaccess_nanobts.c b/openbsc/src/libbsc/bts_ipaccess_nanobts.c
index 9fa03bf..825a22e 100644
--- a/openbsc/src/libbsc/bts_ipaccess_nanobts.c
+++ b/openbsc/src/libbsc/bts_ipaccess_nanobts.c
@@ -590,6 +590,13 @@
 		/* remove old OML signal link for this BTS. */
 		ipaccess_drop_oml(bts);
 
+		if (!bts_depend_check(bts)) {
+			LOGP(DLINP, LOGL_NOTICE,
+				"Dependency not full-filled for %u/%u/%u\n",
+				dev->site_id, dev->bts_id, dev->trx_id);
+			return NULL;
+		}
+
 		/* create new OML link. */
 		sign_link = bts->oml_link =
 			e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML - 1],
diff --git a/openbsc/src/libcommon/gsm_data.c b/openbsc/src/libcommon/gsm_data.c
index 03e9b85..73041fc 100644
--- a/openbsc/src/libcommon/gsm_data.c
+++ b/openbsc/src/libcommon/gsm_data.c
@@ -364,3 +364,61 @@
 	return ret;
 }
 
+/* Assume there are only 256 possible bts */
+osmo_static_assert(sizeof(((struct gsm_bts *) 0)->nr) == 1, _bts_nr_is_256);
+static void depends_calc_index_bit(int bts_nr, int *idx, int *bit)
+{
+	*idx = bts_nr / (8 * 4);
+	*bit = bts_nr % (8 * 4);
+}
+
+void bts_depend_mark(struct gsm_bts *bts, int dep)
+{
+	int idx, bit;
+	depends_calc_index_bit(dep, &idx, &bit);
+
+	bts->depends_on[idx] |= 1 << bit;
+}
+
+void bts_depend_clear(struct gsm_bts *bts, int dep)
+{
+	int idx, bit;
+	depends_calc_index_bit(dep, &idx, &bit);
+
+	bts->depends_on[idx] &= ~(1 << bit);
+}
+
+static int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other)
+{
+	int idx, bit;
+	depends_calc_index_bit(other->nr, &idx, &bit);
+
+	/* Check if there is a depends bit */
+	return (base->depends_on[idx] & (1 << bit)) > 0;
+}
+
+static int bts_is_online(struct gsm_bts *bts)
+{
+	/* TODO: support E1 BTS too */
+	if (!is_ipaccess_bts(bts))
+		return 1;
+
+	if (!bts->oml_link)
+		return 0;
+
+	return bts->mo.nm_state.operational == NM_OPSTATE_ENABLED;
+}
+
+int bts_depend_check(struct gsm_bts *bts)
+{
+	struct gsm_bts *other_bts;
+
+	llist_for_each_entry(other_bts, &bts->network->bts_list, list) {
+		if (!bts_depend_is_depedency(bts, other_bts))
+			continue;
+		if (bts_is_online(other_bts))
+			continue;
+		return 0;
+	}
+	return 1;
+}