diff options
Diffstat (limited to 'src/osmo-bts-litecell15/misc')
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); +} + |