aboutsummaryrefslogtreecommitdiffstats
path: root/src/osmo-bts-litecell15/misc
diff options
context:
space:
mode:
authorYves Godin <support@nuranwireless.com>2015-11-12 08:32:07 -0500
committerHarald Welte <laforge@gnumonks.org>2016-02-15 14:26:33 +0100
commit2a711887b7e91893555891e5c033189d6705eec3 (patch)
tree00abee90a3e8d04f01f34df54f7d7f1c3494fd50 /src/osmo-bts-litecell15/misc
parent5a945dad0cb34dc351427b33a3ce0ed9dd0e394f (diff)
LC15: Add initial support for the NuRAN Wireless Litecell 1.5
This commit adds basic support for the Litecell 1.5. Multi-TRX is not supported yet. Instead, multiple instances of the BTS can be launched using command line parameter -n <HW_TRX_NR> to specify if TRX 1 or 2 must be used by the bts. Note that only TRX 1 opens a connection to the PCU. Full support for GPRS on both TRX will come at the same time than the multi-TRX support. The BTS manager has been adapted to match the new hardware but otherwise it has not been improved or changed compared to the one used on the SuperFemto/Litecell (sysmobts).
Diffstat (limited to 'src/osmo-bts-litecell15/misc')
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_bid.c139
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_bid.h51
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_clock.c283
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_clock.h16
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_mgr.c297
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_mgr.h111
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_mgr_calib.c246
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_mgr_nl.c210
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_mgr_temp.c353
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_mgr_vty.c602
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_misc.c249
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_misc.h16
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_nl.c123
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_nl.h27
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_par.c181
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_par.h33
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_power.c167
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_power.h29
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_temp.c117
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_temp.h27
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_util.c158
21 files changed, 3435 insertions, 0 deletions
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_bid.c b/src/osmo-bts-litecell15/misc/lc15bts_bid.c
new file mode 100644
index 00000000..1fb58514
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_bid.c
@@ -0,0 +1,139 @@
+/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "lc15bts_bid.h"
+
+#define BOARD_REV_SYSFS "/sys/devices/0.lc15/revision"
+#define BOARD_OPT_SYSFS "/sys/devices/0.lc15/option"
+
+static const int option_type_mask[_NUM_OPTION_TYPES] = {
+ [LC15BTS_OPTION_OCXO] = 0x07,
+ [LC15BTS_OPTION_FPGA] = 0x03,
+ [LC15BTS_OPTION_PA] = 0x01,
+ [LC15BTS_OPTION_BAND] = 0x03,
+ [LC15BTS_OPTION_TX_ISO_BYP] = 0x01,
+ [LC15BTS_OPTION_RX_DUP_BYP] = 0x01,
+ [LC15BTS_OPTION_RX_PB_BYP] = 0x01,
+ [LC15BTS_OPTION_RX_DIV] = 0x01,
+ [LC15BTS_OPTION_RX1A] = 0x01,
+ [LC15BTS_OPTION_RX1B] = 0x01,
+ [LC15BTS_OPTION_RX2A] = 0x01,
+ [LC15BTS_OPTION_RX2B] = 0x01,
+ [LC15BTS_OPTION_DDR_32B] = 0x01,
+ [LC15BTS_OPTION_DDR_ECC] = 0x01,
+ [LC15BTS_OPTION_LOG_DET] = 0x01,
+ [LC15BTS_OPTION_DUAL_LOG_DET] = 0x01,
+};
+
+static const int option_type_shift[_NUM_OPTION_TYPES] = {
+ [LC15BTS_OPTION_OCXO] = 0,
+ [LC15BTS_OPTION_FPGA] = 3,
+ [LC15BTS_OPTION_PA] = 5,
+ [LC15BTS_OPTION_BAND] = 6,
+ [LC15BTS_OPTION_TX_ISO_BYP] = 8,
+ [LC15BTS_OPTION_RX_DUP_BYP] = 9,
+ [LC15BTS_OPTION_RX_PB_BYP] = 10,
+ [LC15BTS_OPTION_RX_DIV] = 11,
+ [LC15BTS_OPTION_RX1A] = 12,
+ [LC15BTS_OPTION_RX1B] = 13,
+ [LC15BTS_OPTION_RX2A] = 14,
+ [LC15BTS_OPTION_RX2B] = 15,
+ [LC15BTS_OPTION_DDR_32B] = 16,
+ [LC15BTS_OPTION_DDR_ECC] = 17,
+ [LC15BTS_OPTION_LOG_DET] = 18,
+ [LC15BTS_OPTION_DUAL_LOG_DET] = 19,
+};
+
+
+static int board_rev = -1;
+static int board_option = -1;
+
+
+int lc15bts_rev_get(void)
+{
+ FILE *fp;
+ char rev;
+
+ if (board_rev != -1) {
+ return board_rev;
+ }
+
+ fp = fopen(BOARD_REV_SYSFS, "r");
+ if (fp == NULL) return -1;
+
+ if (fscanf(fp, "%c", &rev) != 1) {
+ fclose( fp );
+ return -1;
+ }
+ fclose(fp);
+
+ board_rev = rev;
+ return board_rev;
+}
+
+int lc15bts_model_get(void)
+{
+ FILE *fp;
+ int opt;
+
+
+ if (board_option == -1) {
+ fp = fopen(BOARD_OPT_SYSFS, "r");
+ if (fp == NULL) {
+ return -1;
+ }
+
+ if (fscanf(fp, "%X", &opt) != 1) {
+ fclose( fp );
+ return -1;
+ }
+ fclose(fp);
+
+ board_option = opt;
+ }
+ return board_option;
+}
+
+int lc15bts_option_get(enum lc15bts_option_type type)
+{
+ int rc;
+ int option;
+
+ if (type >= _NUM_OPTION_TYPES) {
+ return -EINVAL;
+ }
+
+ if (board_option == -1) {
+ rc = lc15bts_model_get();
+ if (rc < 0) return rc;
+ }
+
+ option = (board_option >> option_type_shift[type])
+ & option_type_mask[type];
+
+ return option;
+}
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_bid.h b/src/osmo-bts-litecell15/misc/lc15bts_bid.h
new file mode 100644
index 00000000..b320e117
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_bid.h
@@ -0,0 +1,51 @@
+#ifndef _LC15BTS_BOARD_H
+#define _LC15BTS_BOARD_H
+
+#include <stdint.h>
+
+enum lc15bts_option_type {
+ LC15BTS_OPTION_OCXO,
+ LC15BTS_OPTION_FPGA,
+ LC15BTS_OPTION_PA,
+ LC15BTS_OPTION_BAND,
+ LC15BTS_OPTION_TX_ISO_BYP,
+ LC15BTS_OPTION_RX_DUP_BYP,
+ LC15BTS_OPTION_RX_PB_BYP,
+ LC15BTS_OPTION_RX_DIV,
+ LC15BTS_OPTION_RX1A,
+ LC15BTS_OPTION_RX1B,
+ LC15BTS_OPTION_RX2A,
+ LC15BTS_OPTION_RX2B,
+ LC15BTS_OPTION_DDR_32B,
+ LC15BTS_OPTION_DDR_ECC,
+ LC15BTS_OPTION_LOG_DET,
+ LC15BTS_OPTION_DUAL_LOG_DET,
+ _NUM_OPTION_TYPES
+};
+
+enum lc15bts_ocxo_type {
+ LC15BTS_OCXO_BILAY_NVG45AV2072,
+ LC15BTS_OCXO_TAITIEN_NJ26M003,
+ _NUM_OCXO_TYPES
+};
+
+enum lc15bts_fpga_type {
+ LC15BTS_FPGA_35T,
+ LC15BTS_FPGA_50T,
+ LC15BTS_FPGA_75T,
+ LC15BTS_FPGA_100T,
+ _NUM_FPGA_TYPES
+};
+
+enum lc15bts_gsm_band {
+ LC15BTS_BAND_850,
+ LC15BTS_BAND_900,
+ LC15BTS_BAND_1800,
+ LC15BTS_BAND_1900,
+};
+
+int lc15bts_rev_get(void);
+int lc15bts_model_get(void);
+int lc15bts_option_get(enum lc15bts_option_type type);
+
+#endif
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_clock.c b/src/osmo-bts-litecell15/misc/lc15bts_clock.c
new file mode 100644
index 00000000..90ecb7a8
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_clock.c
@@ -0,0 +1,283 @@
+/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "lc15bts_clock.h"
+
+#define CLKERR_ERR_SYSFS "/sys/devices/5002000.clkerr/clkerr1_average"
+#define CLKERR_ACC_SYSFS "/sys/devices/5002000.clkerr/clkerr1_average_accuracy"
+#define CLKERR_INT_SYSFS "/sys/devices/5002000.clkerr/clkerr1_average_interval"
+#define CLKERR_FLT_SYSFS "/sys/devices/5002000.clkerr/clkerr1_fault"
+#define CLKERR_RFS_SYSFS "/sys/devices/5002000.clkerr/refresh"
+#define CLKERR_RST_SYSFS "/sys/devices/5002000.clkerr/reset"
+
+#define OCXODAC_VAL_SYSFS "/sys/bus/iio/devices/iio:device0/out_voltage0_raw"
+#define OCXODAC_ROM_SYSFS "/sys/bus/iio/devices/iio:device0/store_eeprom"
+
+/* clock error */
+static int clkerr_fd_err = -1;
+static int clkerr_fd_accuracy = -1;
+static int clkerr_fd_interval = -1;
+static int clkerr_fd_fault = -1;
+static int clkerr_fd_refresh = -1;
+static int clkerr_fd_reset = -1;
+
+/* ocxo dac */
+static int ocxodac_fd_value = -1;
+static int ocxodac_fd_save = -1;
+
+
+static int sysfs_read_val(int fd, int *val)
+{
+ int rc;
+ char szVal[32] = {0};
+
+ lseek( fd, 0, SEEK_SET );
+
+ rc = read(fd, szVal, sizeof(szVal) - 1);
+ if (rc < 0) {
+ return -errno;
+ }
+
+ rc = sscanf(szVal, "%d", val);
+ if (rc != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int sysfs_write_val(int fd, int val)
+{
+ int n, rc;
+ char szVal[32] = {0};
+
+ n = sprintf(szVal, "%d", val);
+
+ lseek(fd, 0, SEEK_SET);
+ rc = write(fd, szVal, n+1);
+ if (rc < 0) {
+ return -errno;
+ }
+ return 0;
+}
+
+static int sysfs_write_str(int fd, const char *str)
+{
+ int rc;
+
+ lseek( fd, 0, SEEK_SET );
+ rc = write(fd, str, strlen(str)+1);
+ if (rc < 0) {
+ return -errno;
+ }
+ return 0;
+}
+
+
+int lc15bts_clock_err_open(void)
+{
+ int rc;
+ int fault;
+
+ if (clkerr_fd_err < 0) {
+ clkerr_fd_err = open(CLKERR_ERR_SYSFS, O_RDONLY);
+ if (clkerr_fd_err < 0) {
+ lc15bts_clock_err_close();
+ return clkerr_fd_err;
+ }
+ }
+
+ if (clkerr_fd_accuracy < 0) {
+ clkerr_fd_accuracy = open(CLKERR_ACC_SYSFS, O_RDONLY);
+ if (clkerr_fd_accuracy < 0) {
+ lc15bts_clock_err_close();
+ return clkerr_fd_accuracy;
+ }
+ }
+
+ if (clkerr_fd_interval < 0) {
+ clkerr_fd_interval = open(CLKERR_INT_SYSFS, O_RDONLY);
+ if (clkerr_fd_interval < 0) {
+ lc15bts_clock_err_close();
+ return clkerr_fd_interval;
+ }
+ }
+
+ if (clkerr_fd_fault < 0) {
+ clkerr_fd_fault = open(CLKERR_FLT_SYSFS, O_RDONLY);
+ if (clkerr_fd_fault < 0) {
+ lc15bts_clock_err_close();
+ return clkerr_fd_fault;
+ }
+ }
+
+ if (clkerr_fd_refresh < 0) {
+ clkerr_fd_refresh = open(CLKERR_RFS_SYSFS, O_WRONLY);
+ if (clkerr_fd_refresh < 0) {
+ lc15bts_clock_err_close();
+ return clkerr_fd_refresh;
+ }
+ }
+
+ if (clkerr_fd_reset < 0) {
+ clkerr_fd_reset = open(CLKERR_RST_SYSFS, O_WRONLY);
+ if (clkerr_fd_reset < 0) {
+ lc15bts_clock_err_close();
+ return clkerr_fd_reset;
+ }
+ }
+
+ rc = sysfs_write_str(clkerr_fd_refresh, "once");
+ if (rc < 0) {
+ lc15bts_clock_err_close();
+ return rc;
+ }
+
+ rc = sysfs_read_val(clkerr_fd_fault, &fault);
+ if (rc < 0) {
+ lc15bts_clock_err_close();
+ return rc;
+ }
+
+ if (fault) {
+ rc = sysfs_write_val(clkerr_fd_reset, 1);
+ if (rc < 0) {
+ lc15bts_clock_err_close();
+ return rc;
+ }
+ }
+ return 0;
+}
+
+void lc15bts_clock_err_close(void)
+{
+ if (clkerr_fd_err >= 0) {
+ close(clkerr_fd_err);
+ clkerr_fd_err = -1;
+ }
+
+ if (clkerr_fd_accuracy >= 0) {
+ close(clkerr_fd_accuracy);
+ clkerr_fd_accuracy = -1;
+ }
+
+ if (clkerr_fd_interval >= 0) {
+ close(clkerr_fd_interval);
+ clkerr_fd_interval = -1;
+ }
+
+ if (clkerr_fd_fault >= 0) {
+ close(clkerr_fd_fault);
+ clkerr_fd_fault = -1;
+ }
+
+ if (clkerr_fd_refresh >= 0) {
+ close(clkerr_fd_refresh);
+ clkerr_fd_refresh = -1;
+ }
+
+ if (clkerr_fd_reset >= 0) {
+ close(clkerr_fd_reset);
+ clkerr_fd_reset = -1;
+ }
+}
+
+int lc15bts_clock_err_reset(void)
+{
+ return sysfs_write_val(clkerr_fd_reset, 1);
+}
+
+int lc15bts_clock_err_get(int *fault, int *error_ppt,
+ int *accuracy_ppq, int *interval_sec)
+{
+ int rc;
+
+ rc = sysfs_write_str(clkerr_fd_refresh, "once");
+ if (rc < 0) {
+ return -1;
+ }
+
+ rc = sysfs_read_val(clkerr_fd_fault, fault);
+ rc |= sysfs_read_val(clkerr_fd_err, error_ppt);
+ rc |= sysfs_read_val(clkerr_fd_accuracy, accuracy_ppq);
+ rc |= sysfs_read_val(clkerr_fd_interval, interval_sec);
+ if (rc) {
+ return -1;
+ }
+ return 0;
+}
+
+
+int lc15bts_clock_dac_open(void)
+{
+ if (ocxodac_fd_value < 0) {
+ ocxodac_fd_value = open(OCXODAC_VAL_SYSFS, O_RDWR);
+ if (ocxodac_fd_value < 0) {
+ lc15bts_clock_dac_close();
+ return ocxodac_fd_value;
+ }
+ }
+
+ if (ocxodac_fd_save < 0) {
+ ocxodac_fd_save = open(OCXODAC_ROM_SYSFS, O_WRONLY);
+ if (ocxodac_fd_save < 0) {
+ lc15bts_clock_dac_close();
+ return ocxodac_fd_save;
+ }
+ }
+ return 0;
+}
+
+void lc15bts_clock_dac_close(void)
+{
+ if (ocxodac_fd_value >= 0) {
+ close(ocxodac_fd_value);
+ ocxodac_fd_value = -1;
+ }
+
+ if (ocxodac_fd_save >= 0) {
+ close(ocxodac_fd_save);
+ ocxodac_fd_save = -1;
+ }
+}
+
+int lc15bts_clock_dac_get(int *dac_value)
+{
+ return sysfs_read_val(ocxodac_fd_value, dac_value);
+}
+
+int lc15bts_clock_dac_set(int dac_value)
+{
+ return sysfs_write_val(ocxodac_fd_value, dac_value);
+}
+
+int lc15bts_clock_dac_save(void)
+{
+ return sysfs_write_val(ocxodac_fd_save, 1);
+}
+
+
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_clock.h b/src/osmo-bts-litecell15/misc/lc15bts_clock.h
new file mode 100644
index 00000000..d9673598
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_clock.h
@@ -0,0 +1,16 @@
+#ifndef _LC15BTS_CLOCK_H
+#define _LC15BTS_CLOCK_H
+
+int lc15bts_clock_err_open(void);
+void lc15bts_clock_err_close(void);
+int lc15bts_clock_err_reset(void);
+int lc15bts_clock_err_get(int *fault, int *error_ppt,
+ int *accuracy_ppq, int *interval_sec);
+
+int lc15bts_clock_dac_open(void);
+void lc15bts_clock_dac_close(void);
+int lc15bts_clock_dac_get(int *dac_value);
+int lc15bts_clock_dac_set(int dac_value);
+int lc15bts_clock_dac_save(void);
+
+#endif
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_mgr.c b/src/osmo-bts-litecell15/misc/lc15bts_mgr.c
new file mode 100644
index 00000000..a4c5650b
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_mgr.c
@@ -0,0 +1,297 @@
+/* Main program for NuRAN Wireless Litecell 1.5 BTS management daemon */
+
+/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
+ *
+ * Based on sysmoBTS:
+ * sysmobts_mgr.c
+ * (C) 2012 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2014 by Holger Hans Peter Freyther
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/ports.h>
+
+#include "misc/lc15bts_misc.h"
+#include "misc/lc15bts_mgr.h"
+#include "misc/lc15bts_par.h"
+#include "misc/lc15bts_bid.h"
+#include "misc/lc15bts_power.h"
+
+static int no_rom_write = 0;
+static int daemonize = 0;
+void *tall_mgr_ctx;
+
+/* every 6 hours means 365*4 = 1460 rom writes per year (max) */
+#define TEMP_TIMER_SECS (6 * 3600)
+
+/* every 1 hours means 365*24 = 8760 rom writes per year (max) */
+#define HOURS_TIMER_SECS (1 * 3600)
+
+
+/* the initial state */
+static struct lc15bts_mgr_instance manager = {
+ .config_file = "lc15bts-mgr.cfg",
+ .temp = {
+ .supply_limit = {
+ .thresh_warn = 60,
+ .thresh_crit = 78,
+ },
+ .soc_limit = {
+ .thresh_warn = 60,
+ .thresh_crit = 78,
+ },
+ .fpga_limit = {
+ .thresh_warn = 60,
+ .thresh_crit = 78,
+ },
+ .memory_limit = {
+ .thresh_warn = 60,
+ .thresh_crit = 78,
+ },
+ .tx1_limit = {
+ .thresh_warn = 60,
+ .thresh_crit = 78,
+ },
+ .tx2_limit = {
+ .thresh_warn = 60,
+ .thresh_crit = 78,
+ },
+ .pa1_limit = {
+ .thresh_warn = 60,
+ .thresh_crit = 78,
+ },
+ .pa2_limit = {
+ .thresh_warn = 60,
+ .thresh_crit = 78,
+ },
+ .action_warn = 0,
+ .action_crit = TEMP_ACT_PA1_OFF | TEMP_ACT_PA2_OFF,
+ .state = STATE_NORMAL,
+ }
+};
+
+static struct osmo_timer_list temp_timer;
+static void check_temp_timer_cb(void *unused)
+{
+ lc15bts_check_temp(no_rom_write);
+
+ osmo_timer_schedule(&temp_timer, TEMP_TIMER_SECS, 0);
+}
+
+static struct osmo_timer_list hours_timer;
+static void hours_timer_cb(void *unused)
+{
+ lc15bts_update_hours(no_rom_write);
+
+ osmo_timer_schedule(&hours_timer, HOURS_TIMER_SECS, 0);
+}
+
+static void print_help(void)
+{
+ printf("lc15bts-mgr [-nsD] [-d cat]\n");
+ printf(" -n Do not write to ROM\n");
+ printf(" -s Disable color\n");
+ printf(" -d CAT enable debugging\n");
+ printf(" -D daemonize\n");
+ printf(" -c Specify the filename of the config file\n");
+}
+
+static int parse_options(int argc, char **argv)
+{
+ int opt;
+
+ while ((opt = getopt(argc, argv, "nhsd:c:")) != -1) {
+ switch (opt) {
+ case 'n':
+ no_rom_write = 1;
+ break;
+ case 'h':
+ print_help();
+ return -1;
+ case 's':
+ log_set_use_color(osmo_stderr_target, 0);
+ break;
+ case 'd':
+ log_parse_category_mask(osmo_stderr_target, optarg);
+ break;
+ case 'D':
+ daemonize = 1;
+ break;
+ case 'c':
+ manager.config_file = optarg;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void signal_handler(int signal)
+{
+ fprintf(stderr, "signal %u received\n", signal);
+
+ switch (signal) {
+ case SIGINT:
+ lc15bts_check_temp(no_rom_write);
+ lc15bts_update_hours(no_rom_write);
+ exit(0);
+ break;
+ case SIGABRT:
+ case SIGUSR1:
+ case SIGUSR2:
+ talloc_report_full(tall_mgr_ctx, stderr);
+ break;
+ default:
+ break;
+ }
+}
+
+static struct log_info_cat mgr_log_info_cat[] = {
+ [DTEMP] = {
+ .name = "DTEMP",
+ .description = "Temperature monitoring",
+ .color = "\033[1;35m",
+ .enabled = 1, .loglevel = LOGL_INFO,
+ },
+ [DFW] = {
+ .name = "DFW",
+ .description = "Firmware management",
+ .color = "\033[1;36m",
+ .enabled = 1, .loglevel = LOGL_INFO,
+ },
+ [DFIND] = {
+ .name = "DFIND",
+ .description = "ipaccess-find handling",
+ .color = "\033[1;37m",
+ .enabled = 1, .loglevel = LOGL_INFO,
+ },
+ [DCALIB] = {
+ .name = "DCALIB",
+ .description = "Calibration handling",
+ .color = "\033[1;37m",
+ .enabled = 1, .loglevel = LOGL_INFO,
+ },
+};
+
+static const struct log_info mgr_log_info = {
+ .cat = mgr_log_info_cat,
+ .num_cat = ARRAY_SIZE(mgr_log_info_cat),
+};
+
+static int mgr_log_init(void)
+{
+ osmo_init_logging(&mgr_log_info);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ void *tall_msgb_ctx;
+ int rc;
+
+
+ tall_mgr_ctx = talloc_named_const(NULL, 1, "bts manager");
+ tall_msgb_ctx = talloc_named_const(tall_mgr_ctx, 1, "msgb");
+ msgb_set_talloc_ctx(tall_msgb_ctx);
+
+ mgr_log_init();
+
+ osmo_init_ignore_signals();
+ signal(SIGINT, &signal_handler);
+ signal(SIGUSR1, &signal_handler);
+ signal(SIGUSR2, &signal_handler);
+
+ rc = parse_options(argc, argv);
+ if (rc < 0)
+ exit(2);
+
+ lc15bts_mgr_vty_init();
+ logging_vty_add_cmds(&mgr_log_info);
+ rc = lc15bts_mgr_parse_config(&manager);
+ if (rc < 0) {
+ LOGP(DFIND, LOGL_FATAL, "Cannot parse config file\n");
+ exit(1);
+ }
+
+ rc = telnet_init(tall_msgb_ctx, NULL, OSMO_VTY_PORT_BTSMGR);
+ if (rc < 0) {
+ fprintf(stderr, "Error initializing telnet\n");
+ exit(1);
+ }
+
+ /* start temperature check timer */
+ temp_timer.cb = check_temp_timer_cb;
+ check_temp_timer_cb(NULL);
+
+ /* start operational hours timer */
+ hours_timer.cb = hours_timer_cb;
+ hours_timer_cb(NULL);
+
+ /* Enable the PAs */
+ rc = lc15bts_power_set(LC15BTS_POWER_PA1, 1);
+ if (rc < 0) {
+ exit(3);
+ }
+
+ rc = lc15bts_power_set(LC15BTS_POWER_PA2, 1);
+ if (rc < 0) {
+ exit(3);
+ }
+
+
+ /* handle broadcast messages for ipaccess-find */
+ if (lc15bts_mgr_nl_init() != 0)
+ exit(3);
+
+ /* Initialize the temperature control */
+ lc15bts_mgr_temp_init(&manager);
+
+ if (lc15bts_mgr_calib_init(&manager) != 0)
+ exit(3);
+
+ if (daemonize) {
+ rc = osmo_daemonize();
+ if (rc < 0) {
+ perror("Error during daemonize");
+ exit(1);
+ }
+ }
+
+
+ while (1) {
+ log_reset_context();
+ osmo_select_main(0);
+ }
+}
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_mgr.h b/src/osmo-bts-litecell15/misc/lc15bts_mgr.h
new file mode 100644
index 00000000..466d0b26
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_mgr.h
@@ -0,0 +1,111 @@
+#ifndef _LC15BTS_MGR_H
+#define _LC15BTS_MGR_H
+
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/timer.h>
+
+#include <stdint.h>
+
+enum {
+ DTEMP,
+ DFW,
+ DFIND,
+ DCALIB,
+};
+
+// TODO NTQD: Define new actions like reducing output power, limit ARM core speed, shutdown second TRX/PA, ...
+enum {
+#if 0
+ TEMP_ACT_PWR_CONTRL = 0x1,
+#endif
+ TEMP_ACT_PA1_OFF = 0x2,
+ TEMP_ACT_PA2_OFF = 0x4,
+ TEMP_ACT_BTS_SRV_OFF = 0x10,
+};
+
+/* actions only for normal state */
+enum {
+#if 0
+ TEMP_ACT_NORM_PW_CONTRL = 0x1,
+#endif
+ TEMP_ACT_NORM_PA1_ON = 0x2,
+ TEMP_ACT_NORM_PA2_ON = 0x4,
+ TEMP_ACT_NORM_BTS_SRV_ON= 0x10,
+};
+
+enum lc15bts_temp_state {
+ STATE_NORMAL, /* Everything is fine */
+ STATE_WARNING_HYST, /* Go back to normal next? */
+ STATE_WARNING, /* We are above the warning threshold */
+ STATE_CRITICAL, /* We have an issue. Wait for below warning */
+};
+
+/**
+ * Temperature Limits. We separate from a threshold
+ * that will generate a warning and one that is so
+ * severe that an action will be taken.
+ */
+struct lc15bts_temp_limit {
+ int thresh_warn;
+ int thresh_crit;
+};
+
+enum mgr_vty_node {
+ MGR_NODE = _LAST_OSMOVTY_NODE + 1,
+
+ ACT_NORM_NODE,
+ ACT_WARN_NODE,
+ ACT_CRIT_NODE,
+ LIMIT_SUPPLY_NODE,
+ LIMIT_SOC_NODE,
+ LIMIT_FPGA_NODE,
+ LIMIT_MEMORY_NODE,
+ LIMIT_TX1_NODE,
+ LIMIT_TX2_NODE,
+ LIMIT_PA1_NODE,
+ LIMIT_PA2_NODE,
+};
+
+struct lc15bts_mgr_instance {
+ const char *config_file;
+
+ struct {
+ int action_norm;
+ int action_warn;
+ int action_crit;
+
+ enum lc15bts_temp_state state;
+
+ struct lc15bts_temp_limit supply_limit;
+ struct lc15bts_temp_limit soc_limit;
+ struct lc15bts_temp_limit fpga_limit;
+ struct lc15bts_temp_limit memory_limit;
+ struct lc15bts_temp_limit tx1_limit;
+ struct lc15bts_temp_limit tx2_limit;
+ struct lc15bts_temp_limit pa1_limit;
+ struct lc15bts_temp_limit pa2_limit;
+ } temp;
+
+ struct {
+ int state;
+ int calib_from_loop;
+ struct osmo_timer_list calib_timeout;
+ } calib;
+};
+
+int lc15bts_mgr_vty_init(void);
+int lc15bts_mgr_parse_config(struct lc15bts_mgr_instance *mgr);
+int lc15bts_mgr_nl_init(void);
+int lc15bts_mgr_temp_init(struct lc15bts_mgr_instance *mgr);
+const char *lc15bts_mgr_temp_get_state(enum lc15bts_temp_state state);
+
+
+int lc15bts_mgr_calib_init(struct lc15bts_mgr_instance *mgr);
+int lc15bts_mgr_calib_run(struct lc15bts_mgr_instance *mgr);
+
+extern void *tall_mgr_ctx;
+
+#endif
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_mgr_calib.c b/src/osmo-bts-litecell15/misc/lc15bts_mgr_calib.c
new file mode 100644
index 00000000..fb494770
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_mgr_calib.c
@@ -0,0 +1,246 @@
+/* OCXO calibration control for Litecell 1.5 BTS management daemon */
+
+/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
+ *
+ * Based on sysmoBTS:
+ * sysmobts_mgr_calib.c
+ * (C) 2014,2015 by Holger Hans Peter Freyther
+ * (C) 2014 by Harald Welte for the IPA code from the oml router
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "misc/lc15bts_mgr.h"
+#include "misc/lc15bts_misc.h"
+#include "misc/lc15bts_clock.h"
+#include "osmo-bts/msg_utils.h"
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/select.h>
+
+#include <osmocom/ctrl/control_cmd.h>
+
+#include <osmocom/gsm/ipa.h>
+#include <osmocom/gsm/protocol/ipaccess.h>
+
+#include <osmocom/abis/abis.h>
+#include <osmocom/abis/e1_input.h>
+#include <osmocom/abis/ipa.h>
+
+static void calib_adjust(struct lc15bts_mgr_instance *mgr);
+static void calib_state_reset(struct lc15bts_mgr_instance *mgr, int reason);
+static void calib_loop_run(void *_data);
+
+enum calib_state {
+ CALIB_INITIAL,
+ CALIB_IN_PROGRESS,
+};
+
+enum calib_result {
+ CALIB_FAIL_START,
+ CALIB_FAIL_GPSFIX,
+ CALIB_FAIL_CLKERR,
+ CALIB_FAIL_OCXODAC,
+ CALIB_SUCCESS,
+};
+
+static void calib_start(struct lc15bts_mgr_instance *mgr)
+{
+ int rc;
+
+ rc = lc15bts_clock_err_open();
+ if (rc != 0) {
+ LOGP(DCALIB, LOGL_ERROR, "Failed to open clock error module %d\n", rc);
+ calib_state_reset(mgr, CALIB_FAIL_CLKERR);
+ return;
+ }
+
+ rc = lc15bts_clock_dac_open();
+ if (rc != 0) {
+ LOGP(DCALIB, LOGL_ERROR, "Failed to open OCXO dac module %d\n", rc);
+ calib_state_reset(mgr, CALIB_FAIL_OCXODAC);
+ return;
+ }
+
+ calib_adjust(mgr);
+}
+
+static void calib_adjust(struct lc15bts_mgr_instance *mgr)
+{
+ int rc;
+ int fault;
+ int error_ppt;
+ int accuracy_ppq;
+ int interval_sec;
+ int dac_value;
+ int new_dac_value;
+ double dac_correction;
+
+ rc = lc15bts_clock_err_get(&fault, &error_ppt,
+ &accuracy_ppq, &interval_sec);
+ if (rc < 0) {
+ LOGP(DCALIB, LOGL_ERROR,
+ "Failed to get clock error measurement %d\n", rc);
+ calib_state_reset(mgr, CALIB_FAIL_CLKERR);
+ return;
+ }
+
+ if (fault) {
+ LOGP(DCALIB, LOGL_NOTICE, "GPS has no fix\n");
+ calib_state_reset(mgr, CALIB_FAIL_GPSFIX);
+ return;
+ }
+
+ rc = lc15bts_clock_dac_get(&dac_value);
+ if (rc < 0) {
+ LOGP(DCALIB, LOGL_ERROR,
+ "Failed to get OCXO dac value %d\n", rc);
+ calib_state_reset(mgr, CALIB_FAIL_OCXODAC);
+ return;
+ }
+
+ LOGP(DCALIB, LOGL_NOTICE,
+ "Calibration ERR(%f PPB) ACC(%f PPB) INT(%d) DAC(%d)\n",
+ error_ppt / 1000., accuracy_ppq / 1000000., interval_sec, dac_value);
+
+ /* 1 unit of correction equal about 0.5 - 1 PPB correction */
+ dac_correction = (int)(-error_ppt * 0.00056);
+ new_dac_value = dac_value + dac_correction + 0.5;
+
+ /* We have a fix, make sure the measured error is
+ meaningful (10 times the accuracy) */
+ if ((new_dac_value != dac_value) && ((100l * abs(error_ppt)) > accuracy_ppq)) {
+
+ if (new_dac_value > 4095)
+ dac_value = 4095;
+ else if (new_dac_value < 0)
+ dac_value = 0;
+ else
+ dac_value = new_dac_value;
+
+ LOGP(DCALIB, LOGL_NOTICE,
+ "Going to apply %d as new clock setting.\n",
+ dac_value);
+
+ rc = lc15bts_clock_dac_set(dac_value);
+ if (rc < 0) {
+ LOGP(DCALIB, LOGL_ERROR,
+ "Failed to set OCXO dac value %d\n", rc);
+ calib_state_reset(mgr, CALIB_FAIL_OCXODAC);
+ return;
+ }
+ rc = lc15bts_clock_err_reset();
+ if (rc < 0) {
+ LOGP(DCALIB, LOGL_ERROR,
+ "Failed to set reset clock error module %d\n", rc);
+ calib_state_reset(mgr, CALIB_FAIL_CLKERR);
+ return;
+ }
+ }
+
+ /* Save the correction value in the DAC eeprom if the
+ frequency has been stable for 24 hours */
+ else if (interval_sec >= (24 * 60 * 60)) {
+ rc = lc15bts_clock_dac_save();
+ if (rc < 0) {
+ LOGP(DCALIB, LOGL_ERROR,
+ "Failed to save OCXO dac value %d\n", rc);
+ calib_state_reset(mgr, CALIB_FAIL_OCXODAC);
+ }
+ rc = lc15bts_clock_err_reset();
+ if (rc < 0) {
+ LOGP(DCALIB, LOGL_ERROR,
+ "Failed to set reste clock error module %d\n", rc);
+ calib_state_reset(mgr, CALIB_FAIL_CLKERR);
+ }
+ }
+
+ calib_state_reset(mgr, CALIB_SUCCESS);
+ return;
+}
+
+static void calib_close(struct lc15bts_mgr_instance *mgr)
+{
+ lc15bts_clock_err_close();
+ lc15bts_clock_dac_close();
+}
+
+static void calib_state_reset(struct lc15bts_mgr_instance *mgr, int outcome)
+{
+ if (mgr->calib.calib_from_loop) {
+ /*
+ * In case of success calibrate in two hours again
+ * and in case of a failure in some minutes.
+ *
+ * TODO NTQ: Select timeout based on last error and accuracy
+ */
+ int timeout = 60;
+ //int timeout = 2 * 60 * 60;
+ //if (outcome != CALIB_SUCESS) }
+ // timeout = 5 * 60;
+ //}
+
+ mgr->calib.calib_timeout.data = mgr;
+ mgr->calib.calib_timeout.cb = calib_loop_run;
+ osmo_timer_schedule(&mgr->calib.calib_timeout, timeout, 0);
+ }
+
+ mgr->calib.state = CALIB_INITIAL;
+ calib_close(mgr);
+}
+
+static int calib_run(struct lc15bts_mgr_instance *mgr, int from_loop)
+{
+ if (mgr->calib.state != CALIB_INITIAL) {
+ LOGP(DCALIB, LOGL_ERROR, "Calib is already in progress.\n");
+ return -1;
+ }
+
+ mgr->calib.calib_from_loop = from_loop;
+
+ /* From now on everything will be handled from the failure */
+ mgr->calib.state = CALIB_IN_PROGRESS;
+ calib_start(mgr);
+ return 0;
+}
+
+static void calib_loop_run(void *_data)
+{
+ int rc;
+ struct lc15bts_mgr_instance *mgr = _data;
+
+ LOGP(DCALIB, LOGL_NOTICE, "Going to calibrate the system.\n");
+ rc = calib_run(mgr, 1);
+ if (rc != 0) {
+ calib_state_reset(mgr, CALIB_FAIL_START);
+ }
+}
+
+int lc15bts_mgr_calib_run(struct lc15bts_mgr_instance *mgr)
+{
+ return calib_run(mgr, 0);
+}
+
+int lc15bts_mgr_calib_init(struct lc15bts_mgr_instance *mgr)
+{
+ mgr->calib.state = CALIB_INITIAL;
+ mgr->calib.calib_timeout.data = mgr;
+ mgr->calib.calib_timeout.cb = calib_loop_run;
+ osmo_timer_schedule(&mgr->calib.calib_timeout, 0, 0);
+ return 0;
+}
+
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_mgr_nl.c b/src/osmo-bts-litecell15/misc/lc15bts_mgr_nl.c
new file mode 100644
index 00000000..d2100eb1
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_mgr_nl.c
@@ -0,0 +1,210 @@
+/* NetworkListen for NuRAN Litecell 1.5 BTS management daemon */
+
+/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
+ *
+ * Based on sysmoBTS:
+ * sysmobts_mgr_nl.c
+ * (C) 2014 by Holger Hans Peter Freyther
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "misc/lc15bts_mgr.h"
+#include "misc/lc15bts_misc.h"
+#include "misc/lc15bts_nl.h"
+#include "misc/lc15bts_par.h"
+#include "misc/lc15bts_bid.h"
+
+#include <osmo-bts/logging.h>
+
+#include <osmocom/gsm/protocol/ipaccess.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/select.h>
+
+#include <arpa/inet.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define ETH0_ADDR_SYSFS "/sys/class/net/eth0/address"
+
+static struct osmo_fd nl_fd;
+
+/*
+ * The TLV structure in IPA messages in UDP packages is a bit
+ * weird. First the header appears to have an extra NULL byte
+ * and second the L16 of the L16TV needs to include +1 for the
+ * tag. The default msgb/tlv and libosmo-abis routines do not
+ * provide this.
+ */
+
+static void ipaccess_prepend_header_quirk(struct msgb *msg, int proto)
+{
+ struct ipaccess_head *hh;
+
+ /* prepend the ip.access header */
+ hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh) + 1);
+ hh->len = htons(msg->len - sizeof(*hh) - 1);
+ hh->proto = proto;
+}
+
+static void quirk_l16tv_put(struct msgb *msg, uint16_t len, uint8_t tag,
+ const uint8_t *val)
+{
+ uint8_t *buf = msgb_put(msg, len + 2 + 1);
+
+ *buf++ = (len + 1) >> 8;
+ *buf++ = (len + 1) & 0xff;
+ *buf++ = tag;
+ memcpy(buf, val, len);
+}
+
+/*
+ * We don't look at the content of the request yet and lie
+ * about most of the responses.
+ */
+static void respond_to(struct sockaddr_in *src, struct osmo_fd *fd,
+ uint8_t *data, size_t len)
+{
+ static int fetched_info = 0;
+ static char mac_str[20] = {0, };
+ static char model_name[64] = {0, };
+ static char ser_str[20] = {0, };
+
+ struct sockaddr_in loc_addr;
+ int rc;
+ char loc_ip[INET_ADDRSTRLEN];
+ struct msgb *msg = msgb_alloc_headroom(512, 128, "ipa get response");
+ if (!msg) {
+ LOGP(DFIND, LOGL_ERROR, "Failed to allocate msgb\n");
+ return;
+ }
+
+ if (!fetched_info) {
+ int fd_eth;
+ int serno;
+ int model;
+ int rev;
+
+ /* fetch the MAC */
+ fd_eth = open(ETH0_ADDR_SYSFS, O_RDONLY);
+ if (fd_eth >= 0) {
+ read(fd_eth, mac_str, sizeof(mac_str)-1);
+ mac_str[sizeof(mac_str)-1] = '\0';
+ close(fd_eth);
+ }
+
+ /* fetch the serial number */
+ lc15bts_par_get_int(LC15BTS_PAR_SERNR, &serno);
+ snprintf(ser_str, sizeof(ser_str), "%d", serno);
+
+ /* fetch the model and trx number */
+ snprintf(model_name, sizeof(model_name), "Litecell 1.5 BTS");
+
+ rev = lc15bts_rev_get();
+ if (rev >= 0) {
+ snprintf(model_name, sizeof(model_name), "%s Rev %c",
+ model_name, rev);
+ }
+
+ model = lc15bts_model_get();
+ if (model >= 0) {
+ snprintf(model_name, sizeof(model_name), "%s (%05X)",
+ model_name, model);
+ }
+ fetched_info = 1;
+ }
+
+ if (source_for_dest(&src->sin_addr, &loc_addr.sin_addr) != 0) {
+ LOGP(DFIND, LOGL_ERROR, "Failed to determine local source\n");
+ return;
+ }
+
+ msgb_put_u8(msg, IPAC_MSGT_ID_RESP);
+
+ /* append MAC addr */
+ quirk_l16tv_put(msg, strlen(mac_str) + 1, IPAC_IDTAG_MACADDR, (uint8_t *) mac_str);
+
+ /* append ip address */
+ inet_ntop(AF_INET, &loc_addr.sin_addr, loc_ip, sizeof(loc_ip));
+ quirk_l16tv_put(msg, strlen(loc_ip) + 1, IPAC_IDTAG_IPADDR, (uint8_t *) loc_ip);
+
+ /* append the serial number */
+ quirk_l16tv_put(msg, strlen(ser_str) + 1, IPAC_IDTAG_SERNR, (uint8_t *) ser_str);
+
+ /* abuse some flags */
+ quirk_l16tv_put(msg, strlen(model_name) + 1, IPAC_IDTAG_UNIT, (uint8_t *) model_name);
+
+ /* ip.access nanoBTS would reply to port==3006 */
+ ipaccess_prepend_header_quirk(msg, IPAC_PROTO_IPACCESS);
+ rc = sendto(fd->fd, msg->data, msg->len, 0, (struct sockaddr *)src, sizeof(*src));
+ if (rc != msg->len)
+ LOGP(DFIND, LOGL_ERROR,
+ "Failed to send with rc(%d) errno(%d)\n", rc, errno);
+}
+
+static int ipaccess_bcast(struct osmo_fd *fd, unsigned int what)
+{
+ uint8_t data[2048];
+ char src[INET_ADDRSTRLEN];
+ struct sockaddr_in addr = {};
+ socklen_t len = sizeof(addr);
+ int rc;
+
+ rc = recvfrom(fd->fd, data, sizeof(data), 0,
+ (struct sockaddr *) &addr, &len);
+ if (rc <= 0) {
+ LOGP(DFIND, LOGL_ERROR,
+ "Failed to read from socket errno(%d)\n", errno);
+ return -1;
+ }
+
+ LOGP(DFIND, LOGL_DEBUG,
+ "Received request from: %s size %d\n",
+ inet_ntop(AF_INET, &addr.sin_addr, src, sizeof(src)), rc);
+
+ if (rc < 6)
+ return 0;
+
+ if (data[2] != IPAC_PROTO_IPACCESS || data[4] != IPAC_MSGT_ID_GET)
+ return 0;
+
+ respond_to(&addr, fd, data + 6, rc - 6);
+ return 0;
+}
+
+int lc15bts_mgr_nl_init(void)
+{
+ int rc;
+
+ nl_fd.cb = ipaccess_bcast;
+ rc = osmo_sock_init_ofd(&nl_fd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+ "0.0.0.0", 3006, OSMO_SOCK_F_BIND);
+ if (rc < 0) {
+ perror("Socket creation");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_mgr_temp.c b/src/osmo-bts-litecell15/misc/lc15bts_mgr_temp.c
new file mode 100644
index 00000000..00b8657c
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_mgr_temp.c
@@ -0,0 +1,353 @@
+/* Temperature control for NuRAN Litecell 1.5 BTS management daemon */
+
+/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
+ *
+ * Based on sysmoBTS:
+ * sysmobts_mgr_temp.c
+ * (C) 2014 by Holger Hans Peter Freyther
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "misc/lc15bts_mgr.h"
+#include "misc/lc15bts_misc.h"
+#include "misc/lc15bts_temp.h"
+#include "misc/lc15bts_power.h"
+
+#include <osmo-bts/logging.h>
+
+#include <osmocom/core/timer.h>
+#include <osmocom/core/utils.h>
+
+static struct lc15bts_mgr_instance *s_mgr;
+static struct osmo_timer_list temp_ctrl_timer;
+
+static const struct value_string state_names[] = {
+ { STATE_NORMAL, "NORMAL" },
+ { STATE_WARNING_HYST, "WARNING (HYST)" },
+ { STATE_WARNING, "WARNING" },
+ { STATE_CRITICAL, "CRITICAL" },
+ { 0, NULL }
+};
+
+const char *lc15bts_mgr_temp_get_state(enum lc15bts_temp_state state)
+{
+ return get_value_string(state_names, state);
+}
+
+static int next_state(enum lc15bts_temp_state current_state, int critical, int warning)
+{
+ int next_state = -1;
+ switch (current_state) {
+ case STATE_NORMAL:
+ if (critical)
+ next_state = STATE_CRITICAL;
+ else if (warning)
+ next_state = STATE_WARNING;
+ break;
+ case STATE_WARNING_HYST:
+ if (critical)
+ next_state = STATE_CRITICAL;
+ else if (warning)
+ next_state = STATE_WARNING;
+ else
+ next_state = STATE_NORMAL;
+ break;
+ case STATE_WARNING:
+ if (critical)
+ next_state = STATE_CRITICAL;
+ else if (!warning)
+ next_state = STATE_WARNING_HYST;
+ break;
+ case STATE_CRITICAL:
+ if (!critical && !warning)
+ next_state = STATE_WARNING;
+ break;
+ };
+
+ return next_state;
+}
+
+static void handle_normal_actions(int actions)
+{
+ /* switch on the PA */
+ if (actions & TEMP_ACT_NORM_PA1_ON) {
+ if (lc15bts_power_set(LC15BTS_POWER_PA1, 1) != 0) {
+ LOGP(DTEMP, LOGL_ERROR,
+ "Failed to switch on the PA #1\n");
+ } else {
+ LOGP(DTEMP, LOGL_NOTICE,
+ "Switched on the PA #1 as normal action.\n");
+ }
+ }
+
+ if (actions & TEMP_ACT_NORM_PA2_ON) {
+ if (lc15bts_power_set(LC15BTS_POWER_PA2, 1) != 0) {
+ LOGP(DTEMP, LOGL_ERROR,
+ "Failed to switch on the PA #2\n");
+ } else {
+ LOGP(DTEMP, LOGL_NOTICE,
+ "Switched on the PA #2 as normal action.\n");
+ }
+ }
+
+ if (actions & TEMP_ACT_NORM_BTS_SRV_ON) {
+ LOGP(DTEMP, LOGL_NOTICE,
+ "Going to switch on the BTS service\n");
+ /*
+ * TODO: use/create something like nspawn that serializes
+ * and used SIGCHLD/waitpid to pick up the dead processes
+ * without invoking shell.
+ */
+ system("/bin/systemctl start lc15bts.service");
+ }
+}
+
+static void handle_actions(int actions)
+{
+ /* switch off the PA */
+ if (actions & TEMP_ACT_PA2_OFF) {
+ if (lc15bts_power_set(LC15BTS_POWER_PA2, 0) != 0) {
+ LOGP(DTEMP, LOGL_ERROR,
+ "Failed to switch off the PA #2. Stop BTS?\n");
+ } else {
+ LOGP(DTEMP, LOGL_NOTICE,
+ "Switched off the PA #2 due temperature.\n");
+ }
+ }
+
+ if (actions & TEMP_ACT_PA1_OFF) {
+ if (lc15bts_power_set(LC15BTS_POWER_PA1, 0) != 0) {
+ LOGP(DTEMP, LOGL_ERROR,
+ "Failed to switch off the PA #1. Stop BTS?\n");
+ } else {
+ LOGP(DTEMP, LOGL_NOTICE,
+ "Switched off the PA #1 due temperature.\n");
+ }
+ }
+
+ if (actions & TEMP_ACT_BTS_SRV_OFF) {
+ LOGP(DTEMP, LOGL_NOTICE,
+ "Going to switch off the BTS service\n");
+ /*
+ * TODO: use/create something like nspawn that serializes
+ * and used SIGCHLD/waitpid to pick up the dead processes
+ * without invoking shell.
+ */
+ system("/bin/systemctl stop lc15bts.service");
+ }
+}
+
+/**
+ * Go back to normal! Depending on the configuration execute the normal
+ * actions that could (start to) undo everything we did in the other
+ * states. What is still missing is the power increase/decrease depending
+ * on the state. E.g. starting from WARNING_HYST we might want to slowly
+ * ramp up the output power again.
+ */
+static void execute_normal_act(struct lc15bts_mgr_instance *manager)
+{
+ LOGP(DTEMP, LOGL_NOTICE, "System is back to normal temperature.\n");
+ handle_normal_actions(manager->temp.action_norm);
+}
+
+static void execute_warning_act(struct lc15bts_mgr_instance *manager)
+{
+ LOGP(DTEMP, LOGL_NOTICE, "System has reached temperature warning.\n");
+ handle_actions(manager->temp.action_warn);
+}
+
+static void execute_critical_act(struct lc15bts_mgr_instance *manager)
+{
+ LOGP(DTEMP, LOGL_NOTICE, "System has reached critical warning.\n");
+ handle_actions(manager->temp.action_crit);
+}
+
+static void lc15bts_mgr_temp_handle(struct lc15bts_mgr_instance *manager,
+ int critical, int warning)
+{
+ int new_state = next_state(manager->temp.state, critical, warning);
+
+ /* Nothing changed */
+ if (new_state < 0)
+ return;
+
+ LOGP(DTEMP, LOGL_NOTICE, "Moving from state %s to %s.\n",
+ get_value_string(state_names, manager->temp.state),
+ get_value_string(state_names, new_state));
+ manager->temp.state = new_state;
+ switch (manager->temp.state) {
+ case STATE_NORMAL:
+ execute_normal_act(manager);
+ break;
+ case STATE_WARNING_HYST:
+ /* do nothing? Maybe start to increase transmit power? */
+ break;
+ case STATE_WARNING:
+ execute_warning_act(manager);
+ break;
+ case STATE_CRITICAL:
+ execute_critical_act(manager);
+ break;
+ };
+}
+
+static void temp_ctrl_check()
+{
+ int rc;
+ int warn_thresh_passed = 0;
+ int crit_thresh_passed = 0;
+
+ LOGP(DTEMP, LOGL_DEBUG, "Going to check the temperature.\n");
+
+ /* Read the current supply temperature */
+ rc = lc15bts_temp_get(LC15BTS_TEMP_SUPPLY, LC15BTS_TEMP_INPUT);
+ if (rc < 0) {
+ LOGP(DTEMP, LOGL_ERROR,
+ "Failed to read the supply temperature. rc=%d\n", rc);
+ warn_thresh_passed = crit_thresh_passed = 1;
+ } else {
+ int temp = rc / 1000;
+ if (temp > s_mgr->temp.supply_limit.thresh_warn)
+ warn_thresh_passed = 1;
+ if (temp > s_mgr->temp.supply_limit.thresh_crit)
+ crit_thresh_passed = 1;
+ LOGP(DTEMP, LOGL_DEBUG, "Supply temperature is: %d\n", temp);
+ }
+
+ /* Read the current SoC temperature */
+ rc = lc15bts_temp_get(LC15BTS_TEMP_SOC, LC15BTS_TEMP_INPUT);
+ if (rc < 0) {
+ LOGP(DTEMP, LOGL_ERROR,
+ "Failed to read the SoC temperature. rc=%d\n", rc);
+ warn_thresh_passed = crit_thresh_passed = 1;
+ } else {
+ int temp = rc / 1000;
+ if (temp > s_mgr->temp.soc_limit.thresh_warn)
+ warn_thresh_passed = 1;
+ if (temp > s_mgr->temp.soc_limit.thresh_crit)
+ crit_thresh_passed = 1;
+ LOGP(DTEMP, LOGL_DEBUG, "SoC temperature is: %d\n", temp);
+ }
+
+ /* Read the current fpga temperature */
+ rc = lc15bts_temp_get(LC15BTS_TEMP_FPGA, LC15BTS_TEMP_INPUT);
+ if (rc < 0) {
+ LOGP(DTEMP, LOGL_ERROR,
+ "Failed to read the fpga temperature. rc=%d\n", rc);
+ warn_thresh_passed = crit_thresh_passed = 1;
+ } else {
+ int temp = rc / 1000;
+ if (temp > s_mgr->temp.fpga_limit.thresh_warn)
+ warn_thresh_passed = 1;
+ if (temp > s_mgr->temp.fpga_limit.thresh_crit)
+ crit_thresh_passed = 1;
+ LOGP(DTEMP, LOGL_DEBUG, "FPGA temperature is: %d\n", temp);
+ }
+
+ /* Read the current memory temperature */
+ rc = lc15bts_temp_get(LC15BTS_TEMP_MEMORY, LC15BTS_TEMP_INPUT);
+ if (rc < 0) {
+ LOGP(DTEMP, LOGL_ERROR,
+ "Failed to read the memory temperature. rc=%d\n", rc);
+ warn_thresh_passed = crit_thresh_passed = 1;
+ } else {
+ int temp = rc / 1000;
+ if (temp > s_mgr->temp.memory_limit.thresh_warn)
+ warn_thresh_passed = 1;
+ if (temp > s_mgr->temp.memory_limit.thresh_crit)
+ crit_thresh_passed = 1;
+ LOGP(DTEMP, LOGL_DEBUG, "Memory temperature is: %d\n", temp);
+ }
+
+ /* Read the current TX #1 temperature */
+ rc = lc15bts_temp_get(LC15BTS_TEMP_TX1, LC15BTS_TEMP_INPUT);
+ if (rc < 0) {
+ LOGP(DTEMP, LOGL_ERROR,
+ "Failed to read the TX #1 temperature. rc=%d\n", rc);
+ warn_thresh_passed = crit_thresh_passed = 1;
+ } else {
+ int temp = rc / 1000;
+ if (temp > s_mgr->temp.tx1_limit.thresh_warn)
+ warn_thresh_passed = 1;
+ if (temp > s_mgr->temp.tx1_limit.thresh_crit)
+ crit_thresh_passed = 1;
+ LOGP(DTEMP, LOGL_DEBUG, "TX #1 temperature is: %d\n", temp);
+ }
+
+ /* Read the current TX #2 temperature */
+ rc = lc15bts_temp_get(LC15BTS_TEMP_TX2, LC15BTS_TEMP_INPUT);
+ if (rc < 0) {
+ LOGP(DTEMP, LOGL_ERROR,
+ "Failed to read the TX #2 temperature. rc=%d\n", rc);
+ warn_thresh_passed = crit_thresh_passed = 1;
+ } else {
+ int temp = rc / 1000;
+ if (temp > s_mgr->temp.tx2_limit.thresh_warn)
+ warn_thresh_passed = 1;
+ if (temp > s_mgr->temp.tx2_limit.thresh_crit)
+ crit_thresh_passed = 1;
+ LOGP(DTEMP, LOGL_DEBUG, "TX #2 temperature is: %d\n", temp);
+ }
+
+ /* Read the current PA #1 temperature */
+ rc = lc15bts_temp_get(LC15BTS_TEMP_PA1, LC15BTS_TEMP_INPUT);
+ if (rc < 0) {
+ LOGP(DTEMP, LOGL_ERROR,
+ "Failed to read the PA #1 temperature. rc=%d\n", rc);
+ warn_thresh_passed = crit_thresh_passed = 1;
+ } else {
+ int temp = rc / 1000;
+ if (temp > s_mgr->temp.pa1_limit.thresh_warn)
+ warn_thresh_passed = 1;
+ if (temp > s_mgr->temp.pa1_limit.thresh_crit)
+ crit_thresh_passed = 1;
+ LOGP(DTEMP, LOGL_DEBUG, "PA #1 temperature is: %d\n", temp);
+ }
+
+ /* Read the current PA #2 temperature */
+ rc = lc15bts_temp_get(LC15BTS_TEMP_PA2, LC15BTS_TEMP_INPUT);
+ if (rc < 0) {
+ LOGP(DTEMP, LOGL_ERROR,
+ "Failed to read the PA #2 temperature. rc=%d\n", rc);
+ warn_thresh_passed = crit_thresh_passed = 1;
+ } else {
+ int temp = rc / 1000;
+ if (temp > s_mgr->temp.pa2_limit.thresh_warn)
+ warn_thresh_passed = 1;
+ if (temp > s_mgr->temp.pa2_limit.thresh_crit)
+ crit_thresh_passed = 1;
+ LOGP(DTEMP, LOGL_DEBUG, "PA #2 temperature is: %d\n", temp);
+ }
+
+ lc15bts_mgr_temp_handle(s_mgr, crit_thresh_passed, warn_thresh_passed);
+}
+
+static void temp_ctrl_check_cb(void *unused)
+{
+ temp_ctrl_check();
+ /* Check every two minutes? XXX make it configurable! */
+ osmo_timer_schedule(&temp_ctrl_timer, 2 * 60, 0);
+}
+
+int lc15bts_mgr_temp_init(struct lc15bts_mgr_instance *mgr)
+{
+ s_mgr = mgr;
+ temp_ctrl_timer.cb = temp_ctrl_check_cb;
+ temp_ctrl_check_cb(NULL);
+ return 0;
+}
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_mgr_vty.c b/src/osmo-bts-litecell15/misc/lc15bts_mgr_vty.c
new file mode 100644
index 00000000..cfc6e129
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_mgr_vty.c
@@ -0,0 +1,602 @@
+/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
+ *
+ * Based on sysmoBTS:
+ * sysmobts_mgr_vty.c
+ * (C) 2014 by lc15com - s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * Author: Alvaro Neira Ayuso <anayuso@lc15com.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/misc.h>
+
+#include <osmo-bts/logging.h>
+
+#include "lc15bts_misc.h"
+#include "lc15bts_mgr.h"
+#include "lc15bts_temp.h"
+#include "lc15bts_power.h"
+#include "btsconfig.h"
+
+static struct lc15bts_mgr_instance *s_mgr;
+
+static const char copyright[] =
+ "(C) 2012 by Harald Welte <laforge@gnumonks.org>\r\n"
+ "(C) 2014 by Holger Hans Peter Freyther\r\n"
+ "(C) 2015 by Yves Godin <support@nuranwireless.com>\r\n"
+ "License AGPLv3+: GNU AGPL version 2 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
+ "This is free software: you are free to change and redistribute it.\r\n"
+ "There is NO WARRANTY, to the extent permitted by law.\r\n";
+
+static enum node_type go_to_parent(struct vty *vty)
+{
+ switch (vty->node) {
+ case MGR_NODE:
+ vty->node = CONFIG_NODE;
+ break;
+ case ACT_NORM_NODE:
+ case ACT_WARN_NODE:
+ case ACT_CRIT_NODE:
+ case LIMIT_SUPPLY_NODE:
+ case LIMIT_SOC_NODE:
+ case LIMIT_FPGA_NODE:
+ case LIMIT_MEMORY_NODE:
+ case LIMIT_TX1_NODE:
+ case LIMIT_TX2_NODE:
+ case LIMIT_PA1_NODE:
+ case LIMIT_PA2_NODE:
+ vty->node = MGR_NODE;
+ break;
+ default:
+ vty->node = CONFIG_NODE;
+ }
+ return vty->node;
+}
+
+static int is_config_node(struct vty *vty, int node)
+{
+ switch (node) {
+ case MGR_NODE:
+ case ACT_NORM_NODE:
+ case ACT_WARN_NODE:
+ case ACT_CRIT_NODE:
+ case LIMIT_SUPPLY_NODE:
+ case LIMIT_SOC_NODE:
+ case LIMIT_FPGA_NODE:
+ case LIMIT_MEMORY_NODE:
+ case LIMIT_TX1_NODE:
+ case LIMIT_TX2_NODE:
+ case LIMIT_PA1_NODE:
+ case LIMIT_PA2_NODE:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static struct vty_app_info vty_info = {
+ .name = "lc15bts-mgr",
+ .version = PACKAGE_VERSION,
+ .go_parent_cb = go_to_parent,
+ .is_config_node = is_config_node,
+ .copyright = copyright,
+};
+
+
+#define MGR_STR "Configure lc15bts-mgr\n"
+
+static struct cmd_node mgr_node = {
+ MGR_NODE,
+ "%s(lc15bts-mgr)# ",
+ 1,
+};
+
+static struct cmd_node act_norm_node = {
+ ACT_NORM_NODE,
+ "%s(action-normal)# ",
+ 1,
+};
+
+static struct cmd_node act_warn_node = {
+ ACT_WARN_NODE,
+ "%s(action-warn)# ",
+ 1,
+};
+
+static struct cmd_node act_crit_node = {
+ ACT_CRIT_NODE,
+ "%s(action-critical)# ",
+ 1,
+};
+
+static struct cmd_node limit_supply_node = {
+ LIMIT_SUPPLY_NODE,
+ "%s(limit-supply)# ",
+ 1,
+};
+
+static struct cmd_node limit_soc_node = {
+ LIMIT_SOC_NODE,
+ "%s(limit-soc)# ",
+ 1,
+};
+
+static struct cmd_node limit_fpga_node = {
+ LIMIT_FPGA_NODE,
+ "%s(limit-fpga)# ",
+ 1,
+};
+
+static struct cmd_node limit_memory_node = {
+ LIMIT_MEMORY_NODE,
+ "%s(limit-memory)# ",
+ 1,
+};
+
+static struct cmd_node limit_tx1_node = {
+ LIMIT_TX1_NODE,
+ "%s(limit-tx1)# ",
+ 1,
+};
+static struct cmd_node limit_tx2_node = {
+ LIMIT_TX2_NODE,
+ "%s(limit-tx2)# ",
+ 1,
+};
+static struct cmd_node limit_pa1_node = {
+ LIMIT_PA1_NODE,
+ "%s(limit-pa1)# ",
+ 1,
+};
+static struct cmd_node limit_pa2_node = {
+ LIMIT_PA2_NODE,
+ "%s(limit-pa2)# ",
+ 1,
+};
+
+DEFUN(cfg_mgr, cfg_mgr_cmd,
+ "lc15bts-mgr",
+ MGR_STR)
+{
+ vty->node = MGR_NODE;
+ return CMD_SUCCESS;
+}
+
+static void write_temp_limit(struct vty *vty, const char *name,
+ struct lc15bts_temp_limit *limit)
+{
+ vty_out(vty, " %s%s", name, VTY_NEWLINE);
+ vty_out(vty, " threshold warning %d%s",
+ limit->thresh_warn, VTY_NEWLINE);
+ vty_out(vty, " threshold critical %d%s",
+ limit->thresh_crit, VTY_NEWLINE);
+}
+
+static void write_norm_action(struct vty *vty, const char *name, int actions)
+{
+ vty_out(vty, " %s%s", name, VTY_NEWLINE);
+ vty_out(vty, " %spa1-on%s",
+ (actions & TEMP_ACT_NORM_PA1_ON) ? "" : "no ", VTY_NEWLINE);
+ vty_out(vty, " %spa2-on%s",
+ (actions & TEMP_ACT_NORM_PA2_ON) ? "" : "no ", VTY_NEWLINE);
+ vty_out(vty, " %sbts-service-on%s",
+ (actions & TEMP_ACT_NORM_BTS_SRV_ON) ? "" : "no ", VTY_NEWLINE);
+}
+
+static void write_action(struct vty *vty, const char *name, int actions)
+{
+ vty_out(vty, " %s%s", name, VTY_NEWLINE);
+ vty_out(vty, " %spa1-off%s",
+ (actions & TEMP_ACT_PA1_OFF) ? "" : "no ", VTY_NEWLINE);
+ vty_out(vty, " %spa2-off%s",
+ (actions & TEMP_ACT_PA2_OFF) ? "" : "no ", VTY_NEWLINE);
+ vty_out(vty, " %sbts-service-off%s",
+ (actions & TEMP_ACT_BTS_SRV_OFF) ? "" : "no ", VTY_NEWLINE);
+}
+
+static int config_write_mgr(struct vty *vty)
+{
+ vty_out(vty, "lc15bts-mgr%s", VTY_NEWLINE);
+
+ write_temp_limit(vty, "limits supply", &s_mgr->temp.supply_limit);
+ write_temp_limit(vty, "limits soc", &s_mgr->temp.soc_limit);
+ write_temp_limit(vty, "limits fpga", &s_mgr->temp.fpga_limit);
+ write_temp_limit(vty, "limits memory", &s_mgr->temp.memory_limit);
+ write_temp_limit(vty, "limits tx1", &s_mgr->temp.tx1_limit);
+ write_temp_limit(vty, "limits tx2", &s_mgr->temp.tx2_limit);
+ write_temp_limit(vty, "limits pa1", &s_mgr->temp.pa1_limit);
+ write_temp_limit(vty, "limits pa2", &s_mgr->temp.pa2_limit);
+
+ write_norm_action(vty, "actions normal", s_mgr->temp.action_norm);
+ write_action(vty, "actions warn", s_mgr->temp.action_warn);
+ write_action(vty, "actions critical", s_mgr->temp.action_crit);
+
+ return CMD_SUCCESS;
+}
+
+static int config_write_dummy(struct vty *vty)
+{
+ return CMD_SUCCESS;
+}
+
+#define CFG_LIMIT(name, expl, switch_to, variable) \
+DEFUN(cfg_limit_##name, cfg_limit_##name##_cmd, \
+ "limits " #name, \
+ "Configure Limits\n" expl) \
+{ \
+ vty->node = switch_to; \
+ vty->index = &s_mgr->temp.variable; \
+ return CMD_SUCCESS; \
+}
+
+CFG_LIMIT(supply, "SUPPLY\n", LIMIT_SUPPLY_NODE, supply_limit)
+CFG_LIMIT(soc, "SOC\n", LIMIT_SOC_NODE, soc_limit)
+CFG_LIMIT(fpga, "FPGA\n", LIMIT_FPGA_NODE, fpga_limit)
+CFG_LIMIT(memory, "MEMORY\n", LIMIT_MEMORY_NODE, memory_limit)
+CFG_LIMIT(tx1, "TX1\n", LIMIT_TX1_NODE, tx1_limit)
+CFG_LIMIT(tx2, "TX2\n", LIMIT_TX2_NODE, tx2_limit)
+CFG_LIMIT(pa1, "PA1\n", LIMIT_PA1_NODE, pa1_limit)
+CFG_LIMIT(pa2, "PA2\n", LIMIT_PA2_NODE, pa2_limit)
+#undef CFG_LIMIT
+
+DEFUN(cfg_limit_warning, cfg_thresh_warning_cmd,
+ "threshold warning <0-200>",
+ "Threshold to reach\n" "Warning level\n" "Range\n")
+{
+ struct lc15bts_temp_limit *limit = vty->index;
+ limit->thresh_warn = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_limit_crit, cfg_thresh_crit_cmd,
+ "threshold critical <0-200>",
+ "Threshold to reach\n" "Severe level\n" "Range\n")
+{
+ struct lc15bts_temp_limit *limit = vty->index;
+ limit->thresh_crit = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+#define CFG_ACTION(name, expl, switch_to, variable) \
+DEFUN(cfg_action_##name, cfg_action_##name##_cmd, \
+ "actions " #name, \
+ "Configure Actions\n" expl) \
+{ \
+ vty->node = switch_to; \
+ vty->index = &s_mgr->temp.variable; \
+ return CMD_SUCCESS; \
+}
+CFG_ACTION(normal, "Normal Actions\n", ACT_NORM_NODE, action_norm)
+CFG_ACTION(warn, "Warning Actions\n", ACT_WARN_NODE, action_warn)
+CFG_ACTION(critical, "Critical Actions\n", ACT_CRIT_NODE, action_crit)
+#undef CFG_ACTION
+
+DEFUN(cfg_action_pa1_on, cfg_action_pa1_on_cmd,
+ "pa1-on",
+ "Switch the Power Amplifier #1 on\n")
+{
+ int *action = vty->index;
+ *action |= TEMP_ACT_NORM_PA1_ON;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_action_pa1_on, cfg_no_action_pa1_on_cmd,
+ "no pa1-on",
+ NO_STR "Switch the Power Amplifieri #1 on\n")
+{
+ int *action = vty->index;
+ *action &= ~TEMP_ACT_NORM_PA1_ON;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_action_pa2_on, cfg_action_pa2_on_cmd,
+ "pa2-on",
+ "Switch the Power Amplifier #2 on\n")
+{
+ int *action = vty->index;
+ *action |= TEMP_ACT_NORM_PA2_ON;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_action_pa2_on, cfg_no_action_pa2_on_cmd,
+ "no pa2-on",
+ NO_STR "Switch the Power Amplifieri #2 on\n")
+{
+ int *action = vty->index;
+ *action &= ~TEMP_ACT_NORM_PA2_ON;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_action_bts_srv_on, cfg_action_bts_srv_on_cmd,
+ "bts-service-on",
+ "Start the systemd lc15bts.service\n")
+{
+ int *action = vty->index;
+ *action |= TEMP_ACT_NORM_BTS_SRV_ON;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_action_bts_srv_on, cfg_no_action_bts_srv_on_cmd,
+ "no bts-service-on",
+ NO_STR "Start the systemd lc15bts.service\n")
+{
+ int *action = vty->index;
+ *action &= ~TEMP_ACT_NORM_BTS_SRV_ON;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_action_pa1_off, cfg_action_pa1_off_cmd,
+ "pa1-off",
+ "Switch the Power Amplifier #1 off\n")
+{
+ int *action = vty->index;
+ *action |= TEMP_ACT_PA1_OFF;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_action_pa1_off, cfg_no_action_pa1_off_cmd,
+ "no pa1-off",
+ NO_STR "Do not switch off the Power Amplifier #1\n")
+{
+ int *action = vty->index;
+ *action &= ~TEMP_ACT_PA1_OFF;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_action_pa2_off, cfg_action_pa2_off_cmd,
+ "pa2-off",
+ "Switch the Power Amplifier #2 off\n")
+{
+ int *action = vty->index;
+ *action |= TEMP_ACT_PA2_OFF;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_action_pa2_off, cfg_no_action_pa2_off_cmd,
+ "no pa2-off",
+ NO_STR "Do not switch off the Power Amplifier #2\n")
+{
+ int *action = vty->index;
+ *action &= ~TEMP_ACT_PA2_OFF;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_action_bts_srv_off, cfg_action_bts_srv_off_cmd,
+ "bts-service-off",
+ "Stop the systemd lc15bts.service\n")
+{
+ int *action = vty->index;
+ *action |= TEMP_ACT_BTS_SRV_OFF;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_action_bts_srv_off, cfg_no_action_bts_srv_off_cmd,
+ "no bts-service-off",
+ NO_STR "Stop the systemd lc15bts.service\n")
+{
+ int *action = vty->index;
+ *action &= ~TEMP_ACT_BTS_SRV_OFF;
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_mgr, show_mgr_cmd, "show manager",
+ SHOW_STR "Display information about the manager")
+{
+ vty_out(vty, "Temperature control state: %s%s",
+ lc15bts_mgr_temp_get_state(s_mgr->temp.state), VTY_NEWLINE);
+ vty_out(vty, "Current Temperatures%s", VTY_NEWLINE);
+ vty_out(vty, " Main Supply : %f Celcius%s",
+ lc15bts_temp_get(LC15BTS_TEMP_SUPPLY,
+ LC15BTS_TEMP_INPUT) / 1000.0f,
+ VTY_NEWLINE);
+ vty_out(vty, " SoC : %f Celcius%s",
+ lc15bts_temp_get(LC15BTS_TEMP_SOC,
+ LC15BTS_TEMP_INPUT) / 1000.0f,
+ VTY_NEWLINE);
+ vty_out(vty, " FPGA : %f Celcius%s",
+ lc15bts_temp_get(LC15BTS_TEMP_FPGA,
+ LC15BTS_TEMP_INPUT) / 1000.0f,
+ VTY_NEWLINE);
+ vty_out(vty, " Memory (DDR): %f Celcius%s",
+ lc15bts_temp_get(LC15BTS_TEMP_MEMORY,
+ LC15BTS_TEMP_INPUT) / 1000.0f,
+ VTY_NEWLINE);
+ vty_out(vty, " TX 1 : %f Celcius%s",
+ lc15bts_temp_get(LC15BTS_TEMP_TX1,
+ LC15BTS_TEMP_INPUT) / 1000.0f,
+ VTY_NEWLINE);
+ vty_out(vty, " TX 2 : %f Celcius%s",
+ lc15bts_temp_get(LC15BTS_TEMP_TX2,
+ LC15BTS_TEMP_INPUT) / 1000.0f,
+ VTY_NEWLINE);
+ vty_out(vty, " Power Amp #1: %f Celcius%s",
+ lc15bts_temp_get(LC15BTS_TEMP_PA1,
+ LC15BTS_TEMP_INPUT) / 1000.0f,
+ VTY_NEWLINE);
+ vty_out(vty, " Power Amp #2: %f Celcius%s",
+ lc15bts_temp_get(LC15BTS_TEMP_PA2,
+ LC15BTS_TEMP_INPUT) / 1000.0f,
+ VTY_NEWLINE);
+
+ vty_out(vty, "Power Status%s", VTY_NEWLINE);
+ vty_out(vty, " Main Supply : (ON) [%6.2f Vdc, %4.2f A, %6.2f W]%s",
+ lc15bts_power_sensor_get(LC15BTS_POWER_SUPPLY,
+ LC15BTS_POWER_VOLTAGE)/1000.0f,
+ lc15bts_power_sensor_get(LC15BTS_POWER_SUPPLY,
+ LC15BTS_POWER_CURRENT)/1000.0f,
+ lc15bts_power_sensor_get(LC15BTS_POWER_SUPPLY,
+ LC15BTS_POWER_POWER)/1000000.0f,
+ VTY_NEWLINE);
+ vty_out(vty, " Power Amp #1: %s [%6.2f Vdc, %4.2f A, %6.2f W]%s",
+ lc15bts_power_get(LC15BTS_POWER_PA1) ? "ON " : "OFF",
+ lc15bts_power_sensor_get(LC15BTS_POWER_PA1,
+ LC15BTS_POWER_VOLTAGE)/1000.0f,
+ lc15bts_power_sensor_get(LC15BTS_POWER_PA1,
+ LC15BTS_POWER_CURRENT)/1000.0f,
+ lc15bts_power_sensor_get(LC15BTS_POWER_PA1,
+ LC15BTS_POWER_POWER)/1000000.0f,
+ VTY_NEWLINE);
+ vty_out(vty, " Power Amp #2: %s [%6.2f Vdc, %4.2f A, %6.2f W]%s",
+ lc15bts_power_get(LC15BTS_POWER_PA2) ? "ON " : "OFF",
+ lc15bts_power_sensor_get(LC15BTS_POWER_PA2,
+ LC15BTS_POWER_VOLTAGE)/1000.0f,
+ lc15bts_power_sensor_get(LC15BTS_POWER_PA2,
+ LC15BTS_POWER_CURRENT)/1000.0f,
+ lc15bts_power_sensor_get(LC15BTS_POWER_PA2,
+ LC15BTS_POWER_POWER)/1000000.0f,
+ VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(calibrate_clock, calibrate_clock_cmd,
+ "calibrate clock",
+ "Calibration commands\n"
+ "Calibrate clock against GPS PPS\n")
+{
+ if (lc15bts_mgr_calib_run(s_mgr) < 0) {
+ vty_out(vty, "%%Failed to start calibration.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+static void register_limit(int limit)
+{
+ install_element(limit, &cfg_thresh_warning_cmd);
+ install_element(limit, &cfg_thresh_crit_cmd);
+}
+
+static void register_normal_action(int act)
+{
+ install_element(act, &cfg_action_pa1_on_cmd);
+ install_element(act, &cfg_no_action_pa1_on_cmd);
+ install_element(act, &cfg_action_pa2_on_cmd);
+ install_element(act, &cfg_no_action_pa2_on_cmd);
+ install_element(act, &cfg_action_bts_srv_on_cmd);
+ install_element(act, &cfg_no_action_bts_srv_on_cmd);
+}
+
+static void register_action(int act)
+{
+ install_element(act, &cfg_action_pa1_off_cmd);
+ install_element(act, &cfg_no_action_pa1_off_cmd);
+ install_element(act, &cfg_action_pa2_off_cmd);
+ install_element(act, &cfg_no_action_pa2_off_cmd);
+ install_element(act, &cfg_action_bts_srv_off_cmd);
+ install_element(act, &cfg_no_action_bts_srv_off_cmd);
+}
+
+int lc15bts_mgr_vty_init(void)
+{
+ vty_init(&vty_info);
+
+ install_element_ve(&show_mgr_cmd);
+
+ install_element(ENABLE_NODE, &calibrate_clock_cmd);
+
+ install_node(&mgr_node, config_write_mgr);
+ install_element(CONFIG_NODE, &cfg_mgr_cmd);
+ vty_install_default(MGR_NODE);
+
+ /* install the limit nodes */
+ install_node(&limit_supply_node, config_write_dummy);
+ install_element(MGR_NODE, &cfg_limit_supply_cmd);
+ register_limit(LIMIT_SUPPLY_NODE);
+ vty_install_default(LIMIT_SUPPLY_NODE);
+
+ install_node(&limit_soc_node, config_write_dummy);
+ install_element(MGR_NODE, &cfg_limit_soc_cmd);
+ register_limit(LIMIT_SOC_NODE);
+ vty_install_default(LIMIT_SOC_NODE);
+
+ install_node(&limit_fpga_node, config_write_dummy);
+ install_element(MGR_NODE, &cfg_limit_fpga_cmd);
+ register_limit(LIMIT_FPGA_NODE);
+ vty_install_default(LIMIT_FPGA_NODE);
+
+ install_node(&limit_memory_node, config_write_dummy);
+ install_element(MGR_NODE, &cfg_limit_memory_cmd);
+ register_limit(LIMIT_MEMORY_NODE);
+ vty_install_default(LIMIT_MEMORY_NODE);
+
+ install_node(&limit_tx1_node, config_write_dummy);
+ install_element(MGR_NODE, &cfg_limit_tx1_cmd);
+ register_limit(LIMIT_TX1_NODE);
+ vty_install_default(LIMIT_TX1_NODE);
+
+ install_node(&limit_tx2_node, config_write_dummy);
+ install_element(MGR_NODE, &cfg_limit_tx2_cmd);
+ register_limit(LIMIT_TX2_NODE);
+ vty_install_default(LIMIT_TX2_NODE);
+
+ install_node(&limit_pa1_node, config_write_dummy);
+ install_element(MGR_NODE, &cfg_limit_pa1_cmd);
+ register_limit(LIMIT_PA1_NODE);
+ vty_install_default(LIMIT_PA1_NODE);
+
+ install_node(&limit_pa2_node, config_write_dummy);
+ install_element(MGR_NODE, &cfg_limit_pa2_cmd);
+ register_limit(LIMIT_PA2_NODE);
+ vty_install_default(LIMIT_PA2_NODE);
+
+ /* install the normal node */
+ install_node(&act_norm_node, config_write_dummy);
+ install_element(MGR_NODE, &cfg_action_normal_cmd);
+ register_normal_action(ACT_NORM_NODE);
+
+ /* install the warning and critical node */
+ install_node(&act_warn_node, config_write_dummy);
+ install_element(MGR_NODE, &cfg_action_warn_cmd);
+ register_action(ACT_WARN_NODE);
+ vty_install_default(ACT_WARN_NODE);
+
+ install_node(&act_crit_node, config_write_dummy);
+ install_element(MGR_NODE, &cfg_action_critical_cmd);
+ register_action(ACT_CRIT_NODE);
+ vty_install_default(ACT_CRIT_NODE);
+
+ return 0;
+}
+
+int lc15bts_mgr_parse_config(struct lc15bts_mgr_instance *manager)
+{
+ int rc;
+
+ s_mgr = manager;
+ rc = vty_read_config_file(s_mgr->config_file, NULL);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to parse the config file: '%s'\n",
+ s_mgr->config_file);
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_misc.c b/src/osmo-bts-litecell15/misc/lc15bts_misc.c
new file mode 100644
index 00000000..e0602c82
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_misc.c
@@ -0,0 +1,249 @@
+/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
+ *
+ * Based on sysmoBTS:
+ * sysmobts_misc.c
+ * (C) 2012 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <time.h>
+#include <sys/signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/application.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/logging.h>
+
+#include "btsconfig.h"
+#include "lc15bts_misc.h"
+#include "lc15bts_par.h"
+#include "lc15bts_mgr.h"
+#include "lc15bts_temp.h"
+
+/*********************************************************************
+ * Temperature handling
+ *********************************************************************/
+
+static const struct {
+ const char *name;
+ int has_max;
+ enum lc15bts_temp_sensor sensor;
+ enum lc15bts_par ee_par;
+} temp_data[] = {
+ {
+ .name = "supply",
+ .has_max = 1,
+ .sensor = LC15BTS_TEMP_SUPPLY,
+ .ee_par = LC15BTS_PAR_TEMP_SUPPLY_MAX,
+ }, {
+ .name = "soc",
+ .has_max = 0,
+ .sensor = LC15BTS_TEMP_SOC,
+ .ee_par = LC15BTS_PAR_TEMP_SOC_MAX,
+ }, {
+ .name = "fpga",
+ .has_max = 0,
+ .sensor = LC15BTS_TEMP_FPGA,
+ .ee_par = LC15BTS_PAR_TEMP_FPGA_MAX,
+
+ }, {
+ .name = "memory",
+ .has_max = 1,
+ .sensor = LC15BTS_TEMP_MEMORY,
+ .ee_par = LC15BTS_PAR_TEMP_MEMORY_MAX,
+ }, {
+ .name = "tx1",
+ .has_max = 0,
+ .sensor = LC15BTS_TEMP_TX1,
+ .ee_par = LC15BTS_PAR_TEMP_TX1_MAX,
+ }, {
+ .name = "tx2",
+ .has_max = 0,
+ .sensor = LC15BTS_TEMP_TX2,
+ .ee_par = LC15BTS_PAR_TEMP_TX2_MAX,
+ }, {
+ .name = "pa1",
+ .has_max = 1,
+ .sensor = LC15BTS_TEMP_PA1,
+ .ee_par = LC15BTS_PAR_TEMP_PA1_MAX,
+ }, {
+ .name = "pa2",
+ .has_max = 1,
+ .sensor = LC15BTS_TEMP_PA2,
+ .ee_par = LC15BTS_PAR_TEMP_PA2_MAX,
+ }
+};
+
+void lc15bts_check_temp(int no_rom_write)
+{
+ int temp_old[ARRAY_SIZE(temp_data)];
+ int temp_hi[ARRAY_SIZE(temp_data)];
+ int temp_cur[ARRAY_SIZE(temp_data)];
+ int i, rc;
+
+ for (i = 0; i < ARRAY_SIZE(temp_data); i++) {
+ int ret;
+ rc = lc15bts_par_get_int(temp_data[i].ee_par, &ret);
+ temp_old[i] = ret * 1000;
+ if (temp_data[i].has_max) {
+ temp_hi[i] = lc15bts_temp_get(temp_data[i].sensor,
+ LC15BTS_TEMP_HIGHEST);
+ temp_cur[i] = lc15bts_temp_get(temp_data[i].sensor,
+ LC15BTS_TEMP_INPUT);
+
+ if ((temp_cur[i] < 0 && temp_cur[i] > -1000) ||
+ (temp_hi[i] < 0 && temp_hi[i] > -1000)) {
+ LOGP(DTEMP, LOGL_ERROR, "Error reading temperature\n");
+ continue;
+ }
+ }
+ else {
+ temp_cur[i] = lc15bts_temp_get(temp_data[i].sensor,
+ LC15BTS_TEMP_INPUT);
+
+ if (temp_cur[i] < 0 && temp_cur[i] > -1000) {
+ LOGP(DTEMP, LOGL_ERROR, "Error reading temperature\n");
+ continue;
+ }
+ temp_hi[i] = temp_cur[i];
+ }
+
+ LOGP(DTEMP, LOGL_DEBUG, "Current %s temperature: %d.%d C\n",
+ temp_data[i].name, temp_cur[i]/1000, temp_cur[i]%1000);
+
+ if (temp_hi[i] > temp_old[i]) {
+ LOGP(DTEMP, LOGL_NOTICE, "New maximum %s "
+ "temperature: %d.%d C\n", temp_data[i].name,
+ temp_hi[i]/1000, temp_hi[i]%1000);
+
+ if (!no_rom_write) {
+ rc = lc15bts_par_set_int(temp_data[i].ee_par,
+ temp_hi[i]/1000);
+ if (rc < 0)
+ LOGP(DTEMP, LOGL_ERROR, "error writing new %s "
+ "max temp %d (%s)\n", temp_data[i].name,
+ rc, strerror(errno));
+ }
+ }
+ }
+}
+
+/*********************************************************************
+ * Hours handling
+ *********************************************************************/
+static time_t last_update;
+
+int lc15bts_update_hours(int no_rom_write)
+{
+ time_t now = time(NULL);
+ int rc, op_hrs;
+
+ /* first time after start of manager program */
+ if (last_update == 0) {
+ last_update = now;
+
+ rc = lc15bts_par_get_int(LC15BTS_PAR_HOURS, &op_hrs);
+ if (rc < 0) {
+ LOGP(DTEMP, LOGL_ERROR, "Unable to read "
+ "operational hours: %d (%s)\n", rc,
+ strerror(errno));
+ return rc;
+ }
+
+ LOGP(DTEMP, LOGL_INFO, "Total hours of Operation: %u\n",
+ op_hrs);
+
+ return 0;
+ }
+
+ if (now >= last_update + 3600) {
+ rc = lc15bts_par_get_int(LC15BTS_PAR_HOURS, &op_hrs);
+ if (rc < 0) {
+ LOGP(DTEMP, LOGL_ERROR, "Unable to read "
+ "operational hours: %d (%s)\n", rc,
+ strerror(errno));
+ return rc;
+ }
+
+ /* number of hours to increase */
+ op_hrs += (now-last_update)/3600;
+
+ LOGP(DTEMP, LOGL_INFO, "Total hours of Operation: %u\n",
+ op_hrs);
+
+ if (!no_rom_write) {
+ rc = lc15bts_par_set_int(LC15BTS_PAR_HOURS, op_hrs);
+ if (rc < 0)
+ return rc;
+ }
+
+ last_update = now;
+ }
+
+ return 0;
+}
+
+/*********************************************************************
+ * Firmware reloading
+ *********************************************************************/
+
+static const char *fw_sysfs[_NUM_FW] = {
+ [LC15BTS_FW_DSP0] = "/sys/kernel/debug/remoteproc/remoteproc0/recovery",
+ [LC15BTS_FW_DSP1] = "/sys/kernel/debug/remoteproc/remoteproc0/recovery",
+};
+
+int lc15bts_firmware_reload(enum lc15bts_firmware_type type)
+{
+ int fd;
+ int rc;
+
+ switch (type) {
+ case LC15BTS_FW_DSP0:
+ case LC15BTS_FW_DSP1:
+ fd = open(fw_sysfs[type], O_WRONLY);
+ if (fd < 0) {
+ LOGP(DFW, LOGL_ERROR, "unable ot open firmware device %s: %s\n",
+ fw_sysfs[type], strerror(errno));
+ close(fd);
+ return fd;
+ }
+ rc = write(fd, "restart", 8);
+ if (rc < 8) {
+ LOGP(DFW, LOGL_ERROR, "short write during "
+ "fw write to %s\n", fw_sysfs[type]);
+ close(fd);
+ return -EIO;
+ }
+ close(fd);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_misc.h b/src/osmo-bts-litecell15/misc/lc15bts_misc.h
new file mode 100644
index 00000000..4c3a862a
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_misc.h
@@ -0,0 +1,16 @@
+#ifndef _LC15BTS_MISC_H
+#define _LC15BTS_MISC_H
+
+#include <stdint.h>
+
+void lc15bts_check_temp(int no_rom_write);
+
+int lc15bts_update_hours(int no_rom_write);
+
+enum lc15bts_firmware_type {
+ LC15BTS_FW_DSP0,
+ LC15BTS_FW_DSP1,
+ _NUM_FW
+};
+
+#endif
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_nl.c b/src/osmo-bts-litecell15/misc/lc15bts_nl.c
new file mode 100644
index 00000000..39f64aae
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_nl.c
@@ -0,0 +1,123 @@
+/* Helper for netlink */
+
+/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
+ *
+ * Based on sysmoBTS:
+ * sysmobts_nl.c
+ * (C) 2014 by Holger Hans Peter Freyther
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+
+#include <sys/socket.h>
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+/**
+ * In case one binds to 0.0.0.0/INADDR_ANY and wants to know which source
+ * address will be used when sending a message this function can be used.
+ * It will ask the routing code of the kernel for the PREFSRC
+ */
+int source_for_dest(const struct in_addr *dest, struct in_addr *loc_source)
+{
+ int fd, rc;
+ struct rtmsg *r;
+ struct rtattr *rta;
+ struct {
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
+ } req;
+
+ memset(&req, 0, sizeof(req));
+
+ fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE);
+ if (fd < 0) {
+ perror("nl socket");
+ return -1;
+ }
+
+ /* Send a rtmsg and ask for a response */
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ req.n.nlmsg_type = RTM_GETROUTE;
+ req.n.nlmsg_seq = 1;
+
+ /* Prepare the routing request */
+ req.r.rtm_family = AF_INET;
+
+ /* set the dest */
+ rta = NLMSG_TAIL(&req.n);
+ rta->rta_type = RTA_DST;
+ rta->rta_len = RTA_LENGTH(sizeof(*dest));
+ memcpy(RTA_DATA(rta), dest, sizeof(*dest));
+
+ /* update sizes for dest */
+ req.r.rtm_dst_len = sizeof(*dest) * 8;
+ req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_ALIGN(rta->rta_len);
+
+ rc = send(fd, &req, req.n.nlmsg_len, 0);
+ if (rc != req.n.nlmsg_len) {
+ perror("short write");
+ close(fd);
+ return -2;
+ }
+
+
+ /* now receive a response and parse it */
+ rc = recv(fd, &req, sizeof(req), 0);
+ if (rc <= 0) {
+ perror("short read");
+ close(fd);
+ return -3;
+ }
+
+ if (!NLMSG_OK(&req.n, rc) || req.n.nlmsg_type != RTM_NEWROUTE) {
+ close(fd);
+ return -4;
+ }
+
+ r = NLMSG_DATA(&req.n);
+ rc -= NLMSG_LENGTH(sizeof(*r));
+ rta = RTM_RTA(r);
+ while (RTA_OK(rta, rc)) {
+ if (rta->rta_type != RTA_PREFSRC) {
+ rta = RTA_NEXT(rta, rc);
+ continue;
+ }
+
+ /* we are done */
+ memcpy(loc_source, RTA_DATA(rta), RTA_PAYLOAD(rta));
+ close(fd);
+ return 0;
+ }
+
+ close(fd);
+ return -5;
+}
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_nl.h b/src/osmo-bts-litecell15/misc/lc15bts_nl.h
new file mode 100644
index 00000000..340cf117
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_nl.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
+ *
+ * Based on sysmoBTS:
+ * sysmobts_nl.h
+ * (C) 2014 by Holger Hans Peter Freyther
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+struct in_addr;
+
+int source_for_dest(const struct in_addr *dest, struct in_addr *loc_source);
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_par.c b/src/osmo-bts-litecell15/misc/lc15bts_par.c
new file mode 100644
index 00000000..71544261
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_par.c
@@ -0,0 +1,181 @@
+/* lc15bts - access to hardware related parameters */
+
+/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
+ *
+ * Based on sysmoBTS:
+ * sysmobts_par.c
+ * (C) 2012 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <osmocom/core/utils.h>
+
+#include "lc15bts_par.h"
+
+
+#define FACTORY_ROM_PATH "/mnt/rom/factory"
+#define USER_ROM_PATH "/mnt/rom/user"
+
+const struct value_string lc15bts_par_names[_NUM_LC15BTS_PAR+1] = {
+ { LC15BTS_PAR_TEMP_SUPPLY_MAX, "temp-supply-max" },
+ { LC15BTS_PAR_TEMP_SOC_MAX, "temp-soc-max" },
+ { LC15BTS_PAR_TEMP_FPGA_MAX, "temp-fpga-max" },
+ { LC15BTS_PAR_TEMP_MEMORY_MAX, "temp-memory-max" },
+ { LC15BTS_PAR_TEMP_TX1_MAX, "temp-tx1-max" },
+ { LC15BTS_PAR_TEMP_TX2_MAX, "temp-tx2-max" },
+ { LC15BTS_PAR_TEMP_PA1_MAX, "temp-pa1-max" },
+ { LC15BTS_PAR_TEMP_PA2_MAX, "temp-pa2-max" },
+ { LC15BTS_PAR_SERNR, "serial-nr" },
+ { LC15BTS_PAR_HOURS, "hours-running" },
+ { LC15BTS_PAR_BOOTS, "boot-count" },
+ { LC15BTS_PAR_KEY, "key" },
+ { 0, NULL }
+};
+
+int lc15bts_par_is_int(enum lc15bts_par par)
+{
+ switch (par) {
+ case LC15BTS_PAR_TEMP_SUPPLY_MAX:
+ case LC15BTS_PAR_TEMP_SOC_MAX:
+ case LC15BTS_PAR_TEMP_FPGA_MAX:
+ case LC15BTS_PAR_TEMP_MEMORY_MAX:
+ case LC15BTS_PAR_TEMP_TX1_MAX:
+ case LC15BTS_PAR_TEMP_TX2_MAX:
+ case LC15BTS_PAR_TEMP_PA1_MAX:
+ case LC15BTS_PAR_TEMP_PA2_MAX:
+ case LC15BTS_PAR_SERNR:
+ case LC15BTS_PAR_HOURS:
+ case LC15BTS_PAR_BOOTS:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int lc15bts_par_get_int(enum lc15bts_par par, int *ret)
+{
+ char fpath[PATH_MAX];
+ FILE *fp;
+ int rc;
+
+ if (par >= _NUM_LC15BTS_PAR)
+ return -ENODEV;
+
+ snprintf(fpath, sizeof(fpath)-1, "%s/%s", USER_ROM_PATH, get_value_string(lc15bts_par_names, par));
+ fpath[sizeof(fpath)-1] = '\0';
+
+ fp = fopen(fpath, "r");
+ if (fp == NULL) {
+ return -errno;
+ }
+
+ rc = fscanf(fp, "%d", ret);
+ if (rc != 1) {
+ fclose(fp);
+ return -EIO;
+ }
+ fclose(fp);
+ return 0;
+}
+
+int lc15bts_par_set_int(enum lc15bts_par par, int val)
+{
+ char fpath[PATH_MAX];
+ FILE *fp;
+ int rc;
+
+ if (par >= _NUM_LC15BTS_PAR)
+ return -ENODEV;
+
+ snprintf(fpath, sizeof(fpath)-1, "%s/%s", USER_ROM_PATH, get_value_string(lc15bts_par_names, par));
+ fpath[sizeof(fpath)-1] = '\0';
+
+ fp = fopen(fpath, "w");
+ if (fp == NULL) {
+ return -errno;
+ }
+
+ rc = fprintf(fp, "%d", val);
+ if (rc < 0) {
+ fclose(fp);
+ return -EIO;
+ }
+ fclose(fp);
+ return 0;
+}
+
+int lc15bts_par_get_buf(enum lc15bts_par par, uint8_t *buf,
+ unsigned int size)
+{
+ char fpath[PATH_MAX];
+ FILE *fp;
+ int rc;
+
+ if (par >= _NUM_LC15BTS_PAR)
+ return -ENODEV;
+
+ snprintf(fpath, sizeof(fpath)-1, "%s/%s", USER_ROM_PATH, get_value_string(lc15bts_par_names, par));
+ fpath[sizeof(fpath)-1] = '\0';
+
+ fp = fopen(fpath, "rb");
+ if (fp == NULL) {
+ return -errno;
+ }
+
+ rc = fread(buf, 1, size, fp);
+
+ fclose(fp);
+
+ return rc;
+}
+
+int lc15bts_par_set_buf(enum lc15bts_par par, const uint8_t *buf,
+ unsigned int size)
+{
+ char fpath[PATH_MAX];
+ FILE *fp;
+ int rc;
+
+ if (par >= _NUM_LC15BTS_PAR)
+ return -ENODEV;
+
+ snprintf(fpath, sizeof(fpath)-1, "%s/%s", USER_ROM_PATH, get_value_string(lc15bts_par_names, par));
+ fpath[sizeof(fpath)-1] = '\0';
+
+ fp = fopen(fpath, "wb");
+ if (fp == NULL) {
+ return -errno;
+ }
+
+ rc = fwrite(buf, 1, size, fp);
+
+ fclose(fp);
+
+ return rc;
+}
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_par.h b/src/osmo-bts-litecell15/misc/lc15bts_par.h
new file mode 100644
index 00000000..7c182715
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_par.h
@@ -0,0 +1,33 @@
+#ifndef _LC15BTS_PAR_H
+#define _LC15BTS_PAR_H
+
+#include <osmocom/core/utils.h>
+
+enum lc15bts_par {
+ LC15BTS_PAR_TEMP_SUPPLY_MAX,
+ LC15BTS_PAR_TEMP_SOC_MAX,
+ LC15BTS_PAR_TEMP_FPGA_MAX,
+ LC15BTS_PAR_TEMP_MEMORY_MAX,
+ LC15BTS_PAR_TEMP_TX1_MAX,
+ LC15BTS_PAR_TEMP_TX2_MAX,
+ LC15BTS_PAR_TEMP_PA1_MAX,
+ LC15BTS_PAR_TEMP_PA2_MAX,
+ LC15BTS_PAR_SERNR,
+ LC15BTS_PAR_HOURS,
+ LC15BTS_PAR_BOOTS,
+ LC15BTS_PAR_KEY,
+ _NUM_LC15BTS_PAR
+};
+
+extern const struct value_string lc15bts_par_names[_NUM_LC15BTS_PAR+1];
+
+int lc15bts_par_get_int(enum lc15bts_par par, int *ret);
+int lc15bts_par_set_int(enum lc15bts_par par, int val);
+int lc15bts_par_get_buf(enum lc15bts_par par, uint8_t *buf,
+ unsigned int size);
+int lc15bts_par_set_buf(enum lc15bts_par par, const uint8_t *buf,
+ unsigned int size);
+
+int lc15bts_par_is_int(enum lc15bts_par par);
+
+#endif
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_power.c b/src/osmo-bts-litecell15/misc/lc15bts_power.c
new file mode 100644
index 00000000..a2997ee2
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_power.c
@@ -0,0 +1,167 @@
+/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include "lc15bts_power.h"
+
+#define LC15BTS_PA_VOLTAGE 24000000
+
+#define PA_SUPPLY_MIN_SYSFS "/sys/devices/0.pa-supply/min_microvolts"
+#define PA_SUPPLY_MAX_SYSFS "/sys/devices/0.pa-supply/max_microvolts"
+
+static const char *power_enable_devs[_NUM_POWER_SOURCES] = {
+ [LC15BTS_POWER_PA1] = "/sys/devices/0.pa1/state",
+ [LC15BTS_POWER_PA2] = "/sys/devices/0.pa2/state",
+};
+
+static const char *power_sensor_devs[_NUM_POWER_SOURCES] = {
+ [LC15BTS_POWER_SUPPLY] = "/sys/bus/i2c/devices/2-0040/hwmon/hwmon6/",
+ [LC15BTS_POWER_PA1] = "/sys/bus/i2c/devices/2-0044/hwmon/hwmon7/",
+ [LC15BTS_POWER_PA2] = "/sys/bus/i2c/devices/2-0045/hwmon/hwmon8/",
+};
+
+static const char *power_sensor_type_str[_NUM_POWER_TYPES] = {
+ [LC15BTS_POWER_POWER] = "power1_input",
+ [LC15BTS_POWER_VOLTAGE] = "in1_input",
+ [LC15BTS_POWER_CURRENT] = "curr1_input",
+};
+
+int lc15bts_power_sensor_get(
+ enum lc15bts_power_source source,
+ enum lc15bts_power_type type)
+{
+ char buf[PATH_MAX];
+ char pwrstr[10];
+ int fd, rc;
+
+ if (source >= _NUM_POWER_SOURCES)
+ return -EINVAL;
+
+ if (type >= _NUM_POWER_TYPES)
+ return -EINVAL;
+
+ snprintf(buf, sizeof(buf)-1, "%s%s", power_sensor_devs[source], power_sensor_type_str[type]);
+ buf[sizeof(buf)-1] = '\0';
+
+ fd = open(buf, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ rc = read(fd, pwrstr, sizeof(pwrstr));
+ pwrstr[sizeof(pwrstr)-1] = '\0';
+ if (rc < 0) {
+ close(fd);
+ return rc;
+ }
+ if (rc == 0) {
+ close(fd);
+ return -EIO;
+ }
+ close(fd);
+
+ return atoi(pwrstr);
+}
+
+
+int lc15bts_power_set(
+ enum lc15bts_power_source source,
+ int en)
+{
+ int fd;
+ int rc;
+
+ if ((source != LC15BTS_POWER_PA1)
+ && (source != LC15BTS_POWER_PA2) ) {
+ return -EINVAL;
+ }
+
+ fd = open(PA_SUPPLY_MAX_SYSFS, O_WRONLY);
+ if (fd < 0) {
+ return fd;
+ }
+ rc = write(fd, "32000000", 9);
+ close( fd );
+
+ if (rc != 9) {
+ return -1;
+ }
+
+ fd = open(PA_SUPPLY_MIN_SYSFS, O_WRONLY);
+ if (fd < 0) {
+ return fd;
+ }
+
+ /* TODO NTQ: Make the voltage configurable */
+ rc = write(fd, "24000000", 9);
+ close( fd );
+
+ if (rc != 9) {
+ return -1;
+ }
+
+ fd = open(power_enable_devs[source], O_WRONLY);
+ if (fd < 0) {
+ return fd;
+ }
+ rc = write(fd, en?"1":"0", 2);
+ close( fd );
+
+ if (rc != 2) {
+ return -1;
+ }
+
+ if (en) usleep(50*1000);
+
+ return 0;
+}
+
+int lc15bts_power_get(
+ enum lc15bts_power_source source)
+{
+ int fd;
+ int rc;
+ char enstr[10];
+
+ fd = open(power_enable_devs[source], O_RDONLY);
+ if (fd < 0) {
+ return fd;
+ }
+
+ rc = read(fd, enstr, sizeof(enstr));
+ enstr[sizeof(enstr)-1] = '\0';
+
+ close(fd);
+
+ if (rc < 0) {
+ return rc;
+ }
+ if (rc == 0) {
+ return -EIO;
+ }
+
+ return atoi(enstr);
+}
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_power.h b/src/osmo-bts-litecell15/misc/lc15bts_power.h
new file mode 100644
index 00000000..4bb27486
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_power.h
@@ -0,0 +1,29 @@
+#ifndef _LC15BTS_POWER_H
+#define _LC15BTS_POWER_H
+
+enum lc15bts_power_source {
+ LC15BTS_POWER_SUPPLY,
+ LC15BTS_POWER_PA1,
+ LC15BTS_POWER_PA2,
+ _NUM_POWER_SOURCES
+};
+
+enum lc15bts_power_type {
+ LC15BTS_POWER_POWER,
+ LC15BTS_POWER_VOLTAGE,
+ LC15BTS_POWER_CURRENT,
+ _NUM_POWER_TYPES
+};
+
+int lc15bts_power_sensor_get(
+ enum lc15bts_power_source source,
+ enum lc15bts_power_type type);
+
+int lc15bts_power_set(
+ enum lc15bts_power_source source,
+ int en);
+
+int lc15bts_power_get(
+ enum lc15bts_power_source source);
+
+#endif
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_temp.c b/src/osmo-bts-litecell15/misc/lc15bts_temp.c
new file mode 100644
index 00000000..fa6300e7
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_temp.c
@@ -0,0 +1,117 @@
+/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include <osmocom/core/utils.h>
+
+#include "lc15bts_temp.h"
+
+
+static const char *temp_devs[_NUM_TEMP_SENSORS] = {
+ [LC15BTS_TEMP_SUPPLY] = "/sys/bus/i2c/devices/2-004d/hwmon/hwmon5/temp1_",
+ [LC15BTS_TEMP_SOC] = "/sys/class/hwmon/hwmon1/temp1_",
+ [LC15BTS_TEMP_FPGA] = "/sys/devices/0.iio_hwmon/temp1_",
+ [LC15BTS_TEMP_MEMORY] = "/sys/bus/i2c/devices/2-004c/hwmon/hwmon4/temp1_",
+ [LC15BTS_TEMP_TX1] = "/sys/devices/0.ncp15xh103_tx1/temp1_",
+ [LC15BTS_TEMP_TX2] = "/sys/devices/0.ncp15xh103_tx2/temp1_",
+ [LC15BTS_TEMP_PA1] = "/sys/bus/i2c/devices/2-004d/hwmon/hwmon5/temp2_",
+ [LC15BTS_TEMP_PA2] = "/sys/bus/i2c/devices/2-004c/hwmon/hwmon4/temp2_",
+};
+
+static const int temp_has_fault[_NUM_TEMP_SENSORS] = {
+ [LC15BTS_TEMP_PA1] = 1,
+ [LC15BTS_TEMP_PA2] = 1,
+};
+
+static const char *temp_type_str[_NUM_TEMP_TYPES] = {
+ [LC15BTS_TEMP_INPUT] = "input",
+ [LC15BTS_TEMP_LOWEST] = "lowest",
+ [LC15BTS_TEMP_HIGHEST] = "highest",
+ [LC15BTS_TEMP_FAULT] = "fault",
+};
+
+int lc15bts_temp_get(enum lc15bts_temp_sensor sensor,
+ enum lc15bts_temp_type type)
+{
+ char buf[PATH_MAX];
+ char tempstr[8];
+ char faultstr[8];
+ int fd, rc;
+
+ if (sensor < 0 || sensor >= _NUM_TEMP_SENSORS)
+ return -EINVAL;
+
+ if (type >= ARRAY_SIZE(temp_type_str))
+ return -EINVAL;
+
+ snprintf(buf, sizeof(buf)-1, "%s%s", temp_devs[sensor], temp_type_str[type]);
+ buf[sizeof(buf)-1] = '\0';
+
+ fd = open(buf, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ rc = read(fd, tempstr, sizeof(tempstr));
+ tempstr[sizeof(tempstr)-1] = '\0';
+ if (rc < 0) {
+ close(fd);
+ return rc;
+ }
+ if (rc == 0) {
+ close(fd);
+ return -EIO;
+ }
+ close(fd);
+
+ // Check fault
+ if (type == LC15BTS_TEMP_FAULT || !temp_has_fault[sensor])
+ return atoi(tempstr);
+
+ snprintf(buf, sizeof(buf)-1, "%s%s", temp_devs[sensor], temp_type_str[LC15BTS_TEMP_FAULT]);
+ buf[sizeof(buf)-1] = '\0';
+
+ fd = open(buf, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ rc = read(fd, faultstr, sizeof(faultstr));
+ tempstr[sizeof(faultstr)-1] = '\0';
+ if (rc < 0) {
+ close(fd);
+ return rc;
+ }
+ if (rc == 0) {
+ close(fd);
+ return -EIO;
+ }
+ close(fd);
+
+ if (atoi(faultstr))
+ return -EIO;
+
+ return atoi(tempstr);
+}
+
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_temp.h b/src/osmo-bts-litecell15/misc/lc15bts_temp.h
new file mode 100644
index 00000000..4b70cb8b
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_temp.h
@@ -0,0 +1,27 @@
+#ifndef _LC15BTS_TEMP_H
+#define _LC15BTS_TEMP_H
+
+enum lc15bts_temp_sensor {
+ LC15BTS_TEMP_SUPPLY,
+ LC15BTS_TEMP_SOC,
+ LC15BTS_TEMP_FPGA,
+ LC15BTS_TEMP_MEMORY,
+ LC15BTS_TEMP_TX1,
+ LC15BTS_TEMP_TX2,
+ LC15BTS_TEMP_PA1,
+ LC15BTS_TEMP_PA2,
+ _NUM_TEMP_SENSORS
+};
+
+enum lc15bts_temp_type {
+ LC15BTS_TEMP_INPUT,
+ LC15BTS_TEMP_LOWEST,
+ LC15BTS_TEMP_HIGHEST,
+ LC15BTS_TEMP_FAULT,
+ _NUM_TEMP_TYPES
+};
+
+int lc15bts_temp_get(enum lc15bts_temp_sensor sensor,
+ enum lc15bts_temp_type type);
+
+#endif
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_util.c b/src/osmo-bts-litecell15/misc/lc15bts_util.c
new file mode 100644
index 00000000..33f9e4ef
--- /dev/null
+++ b/src/osmo-bts-litecell15/misc/lc15bts_util.c
@@ -0,0 +1,158 @@
+/* lc15bts-util - access to hardware related parameters */
+
+/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
+ *
+ * Based on sysmoBTS:
+ * sysmobts_misc.c
+ * (C) 2012-2013 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+
+
+#include "lc15bts_par.h"
+
+enum act {
+ ACT_GET,
+ ACT_SET,
+};
+
+static enum act action;
+static char *write_arg;
+static int void_warranty;
+
+static void print_help()
+{
+ const struct value_string *par = lc15bts_par_names;
+
+ printf("lc15bts-util [--void-warranty -r | -w value] param_name\n");
+ printf("Possible param names:\n");
+
+ for (; par->str != NULL; par += 1) {
+ if (!lc15bts_par_is_int(par->value))
+ continue;
+ printf(" %s\n", par->str);
+ }
+}
+
+static int parse_options(int argc, char **argv)
+{
+ while (1) {
+ int option_idx = 0, c;
+ static const struct option long_options[] = {
+ { "help", 0, 0, 'h' },
+ { "read", 0, 0, 'r' },
+ { "void-warranty", 0, 0, 1000},
+ { "write", 1, 0, 'w' },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "rw:h",
+ long_options, &option_idx);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'r':
+ action = ACT_GET;
+ break;
+ case 'w':
+ action = ACT_SET;
+ write_arg = optarg;
+ break;
+ case 'h':
+ print_help();
+ return -1;
+ break;
+ case 1000:
+ printf("Will void warranty on write.\n");
+ void_warranty = 1;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ const char *parname;
+ enum lc15bts_par par;
+ int rc, val;
+
+ rc = parse_options(argc, argv);
+ if (rc < 0)
+ exit(2);
+
+ if (optind >= argc) {
+ fprintf(stderr, "You must specify the parameter name\n");
+ exit(2);
+ }
+ parname = argv[optind];
+
+ rc = get_string_value(lc15bts_par_names, parname);
+ if (rc < 0) {
+ fprintf(stderr, "`%s' is not a valid parameter\n", parname);
+ exit(2);
+ } else
+ par = rc;
+
+ switch (action) {
+ case ACT_GET:
+ rc = lc15bts_par_get_int(par, &val);
+ if (rc < 0) {
+ fprintf(stderr, "Error %d\n", rc);
+ goto err;
+ }
+ printf("%d\n", val);
+ break;
+ case ACT_SET:
+ rc = lc15bts_par_get_int(par, &val);
+ if (rc < 0) {
+ fprintf(stderr, "Error %d\n", rc);
+ goto err;
+ }
+ if (val != 0xFFFF && val != 0xFF && val != 0xFFFFFFFF && !void_warranty) {
+ fprintf(stderr, "Parameter is already set!\r\n");
+ goto err;
+ }
+ rc = lc15bts_par_set_int(par, atoi(write_arg));
+ if (rc < 0) {
+ fprintf(stderr, "Error %d\n", rc);
+ goto err;
+ }
+ printf("Success setting %s=%d\n", parname,
+ atoi(write_arg));
+ break;
+ default:
+ fprintf(stderr, "Unsupported action\n");
+ goto err;
+ }
+
+ exit(0);
+
+err:
+ exit(1);
+}
+