diff options
-rw-r--r-- | src/osmo-bts-sysmo/Makefile.am | 4 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/misc/sysmobts_mgr.c | 208 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/misc/sysmobts_mgr.h | 54 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/misc/sysmobts_mgr_2050.c | 47 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/misc/sysmobts_mgr_nl.c | 196 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/misc/sysmobts_mgr_temp.c | 228 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/misc/sysmobts_mgr_vty.c | 229 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/misc/sysmobts_misc.c | 4 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/misc/sysmobts_misc.h | 13 |
9 files changed, 780 insertions, 203 deletions
diff --git a/src/osmo-bts-sysmo/Makefile.am b/src/osmo-bts-sysmo/Makefile.am index fe318549..cc669339 100644 --- a/src/osmo-bts-sysmo/Makefile.am +++ b/src/osmo-bts-sysmo/Makefile.am @@ -24,7 +24,9 @@ sysmobts_mgr_SOURCES = \ misc/sysmobts_mgr.c misc/sysmobts_misc.c \ misc/sysmobts_par.c misc/sysmobts_nl.c \ misc/sysmobts_mgr_2050.c \ - misc/sysmobts_mgr_vty.c + misc/sysmobts_mgr_vty.c \ + misc/sysmobts_mgr_nl.c \ + misc/sysmobts_mgr_temp.c sysmobts_mgr_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) sysmobts_util_SOURCES = misc/sysmobts_util.c misc/sysmobts_par.c diff --git a/src/osmo-bts-sysmo/misc/sysmobts_mgr.c b/src/osmo-bts-sysmo/misc/sysmobts_mgr.c index f8b3302e..484e08ff 100644 --- a/src/osmo-bts-sysmo/misc/sysmobts_mgr.c +++ b/src/osmo-bts-sysmo/misc/sysmobts_mgr.c @@ -26,22 +26,18 @@ #include <errno.h> #include <getopt.h> #include <limits.h> -#include <arpa/inet.h> #include <sys/signal.h> -#include <sys/types.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/core/serial.h> #include <osmocom/vty/telnet_interface.h> #include <osmocom/vty/logging.h> #include "misc/sysmobts_misc.h" #include "misc/sysmobts_mgr.h" -#include "misc/sysmobts_nl.h" #include "misc/sysmobts_par.h" static int bts_type; @@ -49,9 +45,38 @@ static int trx_number; static int no_eeprom_write = 0; static int daemonize = 0; -static const char *cfgfile = "sysmobts-mgr.cfg"; void *tall_mgr_ctx; +/* every 6 hours means 365*4 = 1460 EEprom writes per year (max) */ +#define TEMP_TIMER_SECS (6 * 3600) + +/* every 1 hours means 365*24 = 8760 EEprom writes per year (max) */ +#define HOURS_TIMER_SECS (1 * 3600) + +/* the initial state */ +static struct sysmobts_mgr_instance manager = { + .config_file = "sysmobts-mgr.cfg", + .rf_limit = { + .thresh_warn = 60, + .thresh_crit = 78, + }, + .digital_limit = { + .thresh_warn = 60, + .thresh_crit = 78, + }, + .board_limit = { + .thresh_warn = 60, + .thresh_crit = 78, + }, + .pa_limit = { + .thresh_warn = 60, + .thresh_crit = 100, + }, + .action_warn = 0, + .action_crit = TEMP_ACT_PA_OFF, + .state = STATE_NORMAL, +}; + static int classify_bts(void) { @@ -72,6 +97,16 @@ static int classify_bts(void) return 0; } +int sysmobts_bts_type(void) +{ + return bts_type; +} + +int sysmobts_trx_number(void) +{ + return trx_number; +} + int is_sbts2050(void) { return bts_type == 2050; @@ -139,7 +174,7 @@ static int parse_options(int argc, char **argv) daemonize = 1; break; case 'c': - cfgfile = optarg; + manager.config_file = optarg; break; default: return -1; @@ -169,17 +204,6 @@ static void signal_handler(int signal) } } -#include <osmocom/core/logging.h> -#include <osmocom/core/application.h> -#include <osmocom/core/utils.h> -#include <osmocom/core/socket.h> - -#include <osmocom/gsm/protocol/ipaccess.h> -#include <osmocom/gsm/tlv.h> - -#include <osmo-bts/bts.h> -#include <osmo-bts/logging.h> - static struct log_info_cat mgr_log_info_cat[] = { [DTEMP] = { .name = "DTEMP", @@ -212,146 +236,8 @@ static int mgr_log_init(void) return 0; } -/* - * 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] = { }; - static char *model_name; - - 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) { - uint8_t mac[6]; - - /* fetch the MAC */ - sysmobts_par_get_buf(SYSMOBTS_PAR_MAC, mac, sizeof(mac)); - snprintf(mac_str, sizeof(mac_str), "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", - mac[0], mac[1], mac[2], - mac[3], mac[4], mac[5]); - - /* fetch the model and trx number */ - switch(bts_type) { - case 0: - case 0xffff: - case 1002: - model_name = "sysmoBTS 1002"; - break; - case 2050: - if (trx_number == 0) - model_name = "sysmoBTS 2050 (master)"; - else if (trx_number == 1) - model_name = "sysmoBTS 2050 (slave)"; - else - model_name = "sysmoBTS 2050 (unknown)"; - break; - default: - model_name = "Unknown"; - break; - } - - - 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); - - /* 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 main(int argc, char **argv) { - struct osmo_fd fd; void *tall_msgb_ctx; int rc; @@ -375,7 +261,7 @@ int main(int argc, char **argv) sysmobts_mgr_vty_init(); logging_vty_add_cmds(&mgr_log_info); - rc = sysmobts_mgr_parse_config(cfgfile); + rc = sysmobts_mgr_parse_config(&manager); if (rc < 0) { LOGP(DFIND, LOGL_FATAL, "Cannot parse config file\n"); exit(1); @@ -399,13 +285,11 @@ int main(int argc, char **argv) sbts2050_uc_initialize(); /* handle broadcast messages for ipaccess-find */ - fd.cb = ipaccess_bcast; - rc = osmo_sock_init_ofd(&fd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, - "0.0.0.0", 3006, OSMO_SOCK_F_BIND); - if (rc < 0) { - perror("Socket creation"); + if (sysmobts_mgr_nl_init() != 0) exit(3); - } + + /* Initialize the temperature control */ + sysmobts_mgr_temp_init(&manager); if (daemonize) { rc = osmo_daemonize(); diff --git a/src/osmo-bts-sysmo/misc/sysmobts_mgr.h b/src/osmo-bts-sysmo/misc/sysmobts_mgr.h index aaa43736..fd6f2bcb 100644 --- a/src/osmo-bts-sysmo/misc/sysmobts_mgr.h +++ b/src/osmo-bts-sysmo/misc/sysmobts_mgr.h @@ -10,14 +10,62 @@ enum { DFIND, }; + +enum { + TEMP_ACT_PWR_CONTRL = 0x1, + TEMP_ACT_MASTER_OFF = 0x2, + TEMP_ACT_SLAVE_OFF = 0x4, + TEMP_ACT_PA_OFF = 0x8, +}; + +enum sysmobts_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 sysmobts_temp_limit { + int thresh_warn; + int thresh_crit; +}; + enum mgr_vty_node { MGR_NODE = _LAST_OSMOVTY_NODE + 1, -}; -int sysmobts_mgr_vty_init(void); -int sysmobts_mgr_parse_config(const char *config_file); + ACT_WARN_NODE, + ACT_CRIT_NODE, + LIMIT_RF_NODE, + LIMIT_DIGITAL_NODE, + LIMIT_BOARD_NODE, + LIMIT_PA_NODE, +}; struct sysmobts_mgr_instance { const char *config_file; + + struct sysmobts_temp_limit rf_limit; + struct sysmobts_temp_limit digital_limit; + + /* Only available on sysmobts 2050 */ + struct sysmobts_temp_limit board_limit; + struct sysmobts_temp_limit pa_limit; + + int action_warn; + int action_crit; + + enum sysmobts_temp_state state; }; + +int sysmobts_mgr_vty_init(void); +int sysmobts_mgr_parse_config(struct sysmobts_mgr_instance *mgr); +int sysmobts_mgr_nl_init(void); +int sysmobts_mgr_temp_init(struct sysmobts_mgr_instance *mgr); +const char *sysmobts_mgr_temp_get_state(enum sysmobts_temp_state state); + #endif diff --git a/src/osmo-bts-sysmo/misc/sysmobts_mgr_2050.c b/src/osmo-bts-sysmo/misc/sysmobts_mgr_2050.c index daad7647..3064319f 100644 --- a/src/osmo-bts-sysmo/misc/sysmobts_mgr_2050.c +++ b/src/osmo-bts-sysmo/misc/sysmobts_mgr_2050.c @@ -25,6 +25,7 @@ #include <osmocom/core/logging.h> #include <osmocom/core/msgb.h> #include <osmocom/core/timer.h> +#include <osmocom/core/serial.h> #include <errno.h> #include <unistd.h> @@ -246,7 +247,7 @@ int sbts2050_uc_get_status(struct sbts2050_power_status *status) /********************************************************************** * Uc Power Switching handling *********************************************************************/ -void sbts2050_uc_set_power(int pmaster, int pslave, int ppa) +int sbts2050_uc_set_power(int pmaster, int pslave, int ppa) { struct msgb *msg; const struct ucinfo info = { @@ -260,7 +261,7 @@ void sbts2050_uc_set_power(int pmaster, int pslave, int ppa) if (msg == NULL) { LOGP(DTEMP, LOGL_ERROR, "Error switching off some unit.\n"); - return; + return -1; } LOGP(DTEMP, LOGL_DEBUG, "Switch off/on success:\n" @@ -272,12 +273,13 @@ void sbts2050_uc_set_power(int pmaster, int pslave, int ppa) ppa ? "ON" : "OFF"); msgb_free(msg); + return 0; } /********************************************************************** * Uc temperature handling *********************************************************************/ -void sbts2050_uc_check_temp(int *temp_pa, int *temp_board) +int sbts2050_uc_check_temp(int *temp_pa, int *temp_board) { rsppkt_t *response; struct msgb *msg; @@ -289,7 +291,7 @@ void sbts2050_uc_check_temp(int *temp_pa, int *temp_board) if (msg == NULL) { LOGP(DTEMP, LOGL_ERROR, "Error reading temperature\n"); - return; + return -1; } response = (rsppkt_t *)msg->data; @@ -297,21 +299,12 @@ void sbts2050_uc_check_temp(int *temp_pa, int *temp_board) *temp_board = response->rsp.tempGet.i8BrdTemp; *temp_pa = response->rsp.tempGet.i8PaTemp; - LOGP(DTEMP, LOGL_DEBUG, "Temperature Board: %+3d C\n" + LOGP(DTEMP, LOGL_DEBUG, "Temperature Board: %+3d C, " "Tempeture PA: %+3d C\n", response->rsp.tempGet.i8BrdTemp, response->rsp.tempGet.i8PaTemp); msgb_free(msg); -} - -static struct osmo_timer_list temp_uc_timer; -static void check_uctemp_timer_cb(void *data) -{ - int temp_pa = 0, temp_board = 0; - - sbts2050_uc_check_temp(&temp_pa, &temp_board); - - osmo_timer_schedule(&temp_uc_timer, TEMP_TIMER_SECS, 0); + return 0; } void sbts2050_uc_initialize(void) @@ -326,8 +319,19 @@ void sbts2050_uc_initialize(void) return; } - temp_uc_timer.cb = check_uctemp_timer_cb; - check_uctemp_timer_cb(NULL); + LOGP(DTEMP, LOGL_NOTICE, "Going to enable the PA.\n"); + sbts2050_uc_set_pa_power(1); +} + +int sbts2050_uc_set_pa_power(int on_off) +{ + struct sbts2050_power_status status; + if (sbts2050_uc_get_status(&status) != 0) { + LOGP(DTEMP, LOGL_ERROR, "Failed to read current power status.\n"); + return -1; + } + + return sbts2050_uc_set_power(status.master_enabled, status.slave_enabled, on_off); } #else void sbts2050_uc_initialize(void) @@ -335,10 +339,11 @@ void sbts2050_uc_initialize(void) LOGP(DTEMP, LOGL_NOTICE, "sysmoBTS2050 was not enabled at compile time.\n"); } -void sbts2050_uc_check_temp(int *temp_pa, int *temp_board) +int sbts2050_uc_check_temp(int *temp_pa, int *temp_board) { LOGP(DTEMP, LOGL_ERROR, "sysmoBTS2050 compiled without temp support.\n"); *temp_pa = *temp_board = 99999; + return -1; } int sbts2050_uc_get_status(struct sbts2050_power_status *status) @@ -348,4 +353,10 @@ int sbts2050_uc_get_status(struct sbts2050_power_status *status) return -1; } +int sbts2050_uc_set_pa_power(int on_off) +{ + LOGP(DTEMP, LOGL_ERROR, "sysmoBTS2050 compiled without PA support.\n"); + return -1; +} + #endif diff --git a/src/osmo-bts-sysmo/misc/sysmobts_mgr_nl.c b/src/osmo-bts-sysmo/misc/sysmobts_mgr_nl.c new file mode 100644 index 00000000..4bbc7193 --- /dev/null +++ b/src/osmo-bts-sysmo/misc/sysmobts_mgr_nl.c @@ -0,0 +1,196 @@ +/* NetworkListen for SysmoBTS management daemon */ + +/* + * (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/sysmobts_mgr.h" +#include "misc/sysmobts_misc.h" +#include "misc/sysmobts_nl.h" +#include "misc/sysmobts_par.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> + +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] = { }; + static char *model_name; + + 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) { + uint8_t mac[6]; + + /* fetch the MAC */ + sysmobts_par_get_buf(SYSMOBTS_PAR_MAC, mac, sizeof(mac)); + snprintf(mac_str, sizeof(mac_str), "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", + mac[0], mac[1], mac[2], + mac[3], mac[4], mac[5]); + + /* fetch the model and trx number */ + switch(sysmobts_bts_type()) { + case 0: + case 0xffff: + case 1002: + model_name = "sysmoBTS 1002"; + break; + case 2050: + if (sysmobts_trx_number() == 0) + model_name = "sysmoBTS 2050 (master)"; + else if (sysmobts_trx_number() == 1) + model_name = "sysmoBTS 2050 (slave)"; + else + model_name = "sysmoBTS 2050 (unknown)"; + break; + default: + model_name = "Unknown"; + break; + } + + + 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); + + /* 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 sysmobts_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-sysmo/misc/sysmobts_mgr_temp.c b/src/osmo-bts-sysmo/misc/sysmobts_mgr_temp.c new file mode 100644 index 00000000..dac226f5 --- /dev/null +++ b/src/osmo-bts-sysmo/misc/sysmobts_mgr_temp.c @@ -0,0 +1,228 @@ +/* Temperature control for SysmoBTS management daemon */ + +/* + * (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/sysmobts_mgr.h" +#include "misc/sysmobts_misc.h" + +#include <osmo-bts/logging.h> + +#include <osmocom/core/timer.h> +#include <osmocom/core/utils.h> + +static struct sysmobts_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 *sysmobts_mgr_temp_get_state(enum sysmobts_temp_state state) +{ + return get_value_string(state_names, state); +} + +static int next_state(enum sysmobts_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; +} + +/** + * Go back to normal! Undo everything we did in the other states. For + * reducint the transmit power, the question is if we should slowly set + * it back to normal, let the BTS slowly increase it.. or handle it here + * as well? + */ +static void execute_normal_act(struct sysmobts_mgr_instance *manager) +{ + LOGP(DTEMP, LOGL_NOTICE, "System is back to normal temperature.\n"); +} + +static void execute_warning_act(struct sysmobts_mgr_instance *manager) +{ + LOGP(DTEMP, LOGL_NOTICE, "System has reached temperature warning.\n"); +} + +static void execute_critical_act(struct sysmobts_mgr_instance *manager) +{ + LOGP(DTEMP, LOGL_NOTICE, "System has reached critical warning.\n"); + + /* switch off the PA */ + if (manager->action_crit & TEMP_ACT_PA_OFF) { + if (!is_sbts2050_master()) { + LOGP(DTEMP, LOGL_NOTICE, + "PA can only be switched-off on the master\n"); + } else if (sbts2050_uc_set_pa_power(0) != 0) { + LOGP(DTEMP, LOGL_ERROR, + "Failed to switch off the PA. Stop BTS?\n"); + } else { + LOGP(DTEMP, LOGL_NOTICE, + "Switched off the PA due temperature.\n"); + } + /* + * TODO: remember we switched off things so we could switch + * it back on. But we would need to make sure that the BTS + * will not transmit with full power at that time. This + * requires the control protocol. + */ + } +} + +static void sysmobts_mgr_temp_handle(struct sysmobts_mgr_instance *manager, + int critical, int warning) +{ + int new_state = next_state(manager->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->state), + get_value_string(state_names, new_state)); + manager->state = new_state; + switch (manager->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 digital temperature */ + rc = sysmobts_temp_get(SYSMOBTS_TEMP_DIGITAL, SYSMOBTS_TEMP_INPUT); + if (rc < 0) { + LOGP(DTEMP, LOGL_ERROR, + "Failed to read the digital temperature. rc=%d\n", rc); + warn_thresh_passed = crit_thresh_passed = 1; + } else { + int temp = rc / 1000; + if (temp > s_mgr->digital_limit.thresh_warn) + warn_thresh_passed = 1; + if (temp > s_mgr->digital_limit.thresh_crit) + crit_thresh_passed = 1; + LOGP(DTEMP, LOGL_DEBUG, "Digital temperature is: %d\n", temp); + } + + /* Read the current RF temperature */ + rc = sysmobts_temp_get(SYSMOBTS_TEMP_RF, SYSMOBTS_TEMP_INPUT); + if (rc < 0) { + LOGP(DTEMP, LOGL_ERROR, + "Failed to read the RF temperature. rc=%d\n", rc); + warn_thresh_passed = crit_thresh_passed = 1; + } else { + int temp = rc / 1000; + if (temp > s_mgr->rf_limit.thresh_warn) + warn_thresh_passed = 1; + if (temp > s_mgr->rf_limit.thresh_crit) + crit_thresh_passed = 1; + LOGP(DTEMP, LOGL_DEBUG, "RF temperature is: %d\n", temp); + } + + if (is_sbts2050_master()) { + int temp_pa, temp_board; + + rc = sbts2050_uc_check_temp(&temp_pa, &temp_board); + if (rc != 0) { + /* XXX what do here? */ + LOGP(DTEMP, LOGL_ERROR, + "Failed to read the temperature! Reboot?!\n"); + warn_thresh_passed = 1; + crit_thresh_passed = 1; + } else { + LOGP(DTEMP, LOGL_DEBUG, "SBTS2050 board(%d) PA(%d)\n", + temp_board, temp_pa); + if (temp_pa > s_mgr->pa_limit.thresh_warn) + warn_thresh_passed = 1; + if (temp_pa > s_mgr->pa_limit.thresh_crit) + crit_thresh_passed = 1; + if (temp_board > s_mgr->board_limit.thresh_warn) + warn_thresh_passed = 1; + if (temp_board > s_mgr->board_limit.thresh_crit) + crit_thresh_passed = 1; + } + } + + sysmobts_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 sysmobts_mgr_temp_init(struct sysmobts_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-sysmo/misc/sysmobts_mgr_vty.c b/src/osmo-bts-sysmo/misc/sysmobts_mgr_vty.c index 1cabe44a..16373748 100644 --- a/src/osmo-bts-sysmo/misc/sysmobts_mgr_vty.c +++ b/src/osmo-bts-sysmo/misc/sysmobts_mgr_vty.c @@ -39,6 +39,8 @@ #include "sysmobts_mgr.h" #include "btsconfig.h" +static struct sysmobts_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" @@ -52,6 +54,14 @@ static enum node_type go_to_parent(struct vty *vty) case MGR_NODE: vty->node = CONFIG_NODE; break; + case ACT_WARN_NODE: + case ACT_CRIT_NODE: + case LIMIT_RF_NODE: + case LIMIT_DIGITAL_NODE: + case LIMIT_BOARD_NODE: + case LIMIT_PA_NODE: + vty->node = MGR_NODE; + break; default: vty->node = CONFIG_NODE; } @@ -62,6 +72,12 @@ static int is_config_node(struct vty *vty, int node) { switch (node) { case MGR_NODE: + case ACT_WARN_NODE: + case ACT_CRIT_NODE: + case LIMIT_RF_NODE: + case LIMIT_DIGITAL_NODE: + case LIMIT_BOARD_NODE: + case LIMIT_PA_NODE: return 1; default: return 0; @@ -85,6 +101,42 @@ static struct cmd_node mgr_node = { 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_rf_node = { + LIMIT_RF_NODE, + "%s(limit-rf)# ", + 1, +}; + +static struct cmd_node limit_digital_node = { + LIMIT_DIGITAL_NODE, + "%s(limit-digital)# ", + 1, +}; + +static struct cmd_node limit_board_node = { + LIMIT_BOARD_NODE, + "%s(limit-board)# ", + 1, +}; + +static struct cmd_node limit_pa_node = { + LIMIT_PA_NODE, + "%s(limit-pa)# ", + 1, +}; + DEFUN(cfg_mgr, cfg_mgr_cmd, "sysmobts-mgr", MGR_STR) @@ -93,9 +145,123 @@ DEFUN(cfg_mgr, cfg_mgr_cmd, return CMD_SUCCESS; } +static void write_temp_limit(struct vty *vty, const char *name, + struct sysmobts_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_action(struct vty *vty, const char *name, int actions) +{ + vty_out(vty, " %s%s", name, VTY_NEWLINE); +#if 0 + vty_out(vty, " %spower-control%s", + (actions & TEMP_ACT_PWR_CONTRL) ? "" : "no ", VTY_NEWLINE); + + /* only on the sysmobts 2050 */ + vty_out(vty, " %smaster-off%s", + (actions & TEMP_ACT_MASTER_OFF) ? "" : "no ", VTY_NEWLINE); + vty_out(vty, " %sslave-off%s", + (actions & TEMP_ACT_MASTER_OFF) ? "" : "no ", VTY_NEWLINE); +#endif + vty_out(vty, " %spa-off%s", + (actions & TEMP_ACT_PA_OFF) ? "" : "no ", VTY_NEWLINE); +} + +static int config_write_mgr(struct vty *vty) +{ + vty_out(vty, "sysmobts-mgr%s", VTY_NEWLINE); + + write_temp_limit(vty, "limits rf", &s_mgr->rf_limit); + write_temp_limit(vty, "limits digital", &s_mgr->digital_limit); + write_temp_limit(vty, "limits board", &s_mgr->board_limit); + write_temp_limit(vty, "limits pa", &s_mgr->pa_limit); + + write_action(vty, "actions warn", s_mgr->action_warn); + write_action(vty, "actions critical", s_mgr->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->variable; \ + return CMD_SUCCESS; \ +} + +CFG_LIMIT(rf, "RF\n", LIMIT_RF_NODE, rf_limit) +CFG_LIMIT(digital, "Digital\n", LIMIT_DIGITAL_NODE, digital_limit) +CFG_LIMIT(board, "Board\n", LIMIT_BOARD_NODE, board_limit) +CFG_LIMIT(pa, "Power Amplifier\n", LIMIT_PA_NODE, pa_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 sysmobts_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 sysmobts_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->variable; \ + return CMD_SUCCESS; \ +} +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_pa_off, cfg_action_pa_off_cmd, + "pa-off", + "Switch the Power Amplifier off\n") +{ + int *action = vty->index; + *action |= TEMP_ACT_PA_OFF; + return CMD_SUCCESS; +} + +DEFUN(cfg_no_action_pa_off, cfg_no_action_pa_off_cmd, + "no pa-off", + NO_STR "Do not switch off the Power Amplifier\n") +{ + int *action = vty->index; + *action &= ~TEMP_ACT_PA_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", + sysmobts_mgr_temp_get_state(s_mgr->state), VTY_NEWLINE); vty_out(vty, "Current Temperatures%s", VTY_NEWLINE); vty_out(vty, " Digital: %f Celcius%s", sysmobts_temp_get(SYSMOBTS_TEMP_DIGITAL, @@ -111,7 +277,7 @@ DEFUN(show_mgr, show_mgr_cmd, "show manager", sbts2050_uc_check_temp(&temp_pa, &temp_board); vty_out(vty, " sysmoBTS 2050 PA: %d Celcius%s", temp_pa, VTY_NEWLINE); - vty_out(vty, " sysmoBTS 2050 PA: %d CelciusC%s", temp_board, VTY_NEWLINE); + vty_out(vty, " sysmoBTS 2050 PA: %d Celcius%s", temp_board, VTY_NEWLINE); sbts2050_uc_get_status(&status); vty_out(vty, "Power Status%s", VTY_NEWLINE); @@ -138,10 +304,26 @@ DEFUN(show_mgr, show_mgr_cmd, "show manager", return CMD_SUCCESS; } -static int config_write_mgr(struct vty *vty) +static void register_limit(int limit) { - vty_out(vty, "sysmobts-mgr%s", VTY_NEWLINE); - return CMD_SUCCESS; + install_element(limit, &cfg_thresh_warning_cmd); + install_element(limit, &cfg_thresh_crit_cmd); +} + +static void register_action(int act) +{ +#if 0 + install_element(act, &cfg_action_pwr_contrl_cmd); + install_element(act, &cfg_no_action_pwr_contrl_cmd); + + /* these only work on the sysmobts 2050 */ + install_element(act, &cfg_action_master_off_cmd); + install_element(act, &cfg_no_action_master_off_cmd); + install_element(act, &cfg_action_slave_off_cmd); + install_element(act, &cfg_no_action_slave_off_cmd); +#endif + install_element(act, &cfg_action_pa_off_cmd); + install_element(act, &cfg_no_action_pa_off_cmd); } int sysmobts_mgr_vty_init(void) @@ -154,17 +336,50 @@ int sysmobts_mgr_vty_init(void) install_element(CONFIG_NODE, &cfg_mgr_cmd); vty_install_default(MGR_NODE); + /* install the limit nodes */ + install_node(&limit_rf_node, config_write_dummy); + install_element(MGR_NODE, &cfg_limit_rf_cmd); + register_limit(LIMIT_RF_NODE); + vty_install_default(LIMIT_RF_NODE); + + install_node(&limit_digital_node, config_write_dummy); + install_element(MGR_NODE, &cfg_limit_digital_cmd); + register_limit(LIMIT_DIGITAL_NODE); + vty_install_default(LIMIT_DIGITAL_NODE); + + install_node(&limit_board_node, config_write_dummy); + install_element(MGR_NODE, &cfg_limit_board_cmd); + register_limit(LIMIT_BOARD_NODE); + vty_install_default(LIMIT_BOARD_NODE); + + install_node(&limit_pa_node, config_write_dummy); + install_element(MGR_NODE, &cfg_limit_pa_cmd); + register_limit(LIMIT_PA_NODE); + vty_install_default(LIMIT_PA_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 sysmobts_mgr_parse_config(const char *config_file) +int sysmobts_mgr_parse_config(struct sysmobts_mgr_instance *manager) { int rc; - rc = vty_read_config_file(config_file, NULL); + 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", - config_file); + s_mgr->config_file); return rc; } diff --git a/src/osmo-bts-sysmo/misc/sysmobts_misc.c b/src/osmo-bts-sysmo/misc/sysmobts_misc.c index 94f73857..d996d644 100644 --- a/src/osmo-bts-sysmo/misc/sysmobts_misc.c +++ b/src/osmo-bts-sysmo/misc/sysmobts_misc.c @@ -42,10 +42,6 @@ #include "sysmobts_par.h" #include "sysmobts_mgr.h" -#ifdef BUILD_SBTS2050 -#include <sysmocom/femtobts/sbts2050_header.h> -#endif - /********************************************************************* * Temperature handling *********************************************************************/ diff --git a/src/osmo-bts-sysmo/misc/sysmobts_misc.h b/src/osmo-bts-sysmo/misc/sysmobts_misc.h index 9d1bb47b..8a6337e2 100644 --- a/src/osmo-bts-sysmo/misc/sysmobts_misc.h +++ b/src/osmo-bts-sysmo/misc/sysmobts_misc.h @@ -3,12 +3,6 @@ #include <stdint.h> -/* every 6 hours means 365*4 = 1460 EEprom writes per year (max) */ -#define TEMP_TIMER_SECS (6 * 3600) - -/* every 1 hours means 365*24 = 8760 EEprom writes per year (max) */ -#define HOURS_TIMER_SECS (1 * 3600) - enum sysmobts_temp_sensor { SYSMOBTS_TEMP_DIGITAL = 1, SYSMOBTS_TEMP_RF = 2, @@ -37,6 +31,8 @@ enum sysmobts_firmware_type { int sysmobts_firmware_reload(enum sysmobts_firmware_type type); +int sysmobts_bts_type(); +int sysmobts_trx_number(); int is_sbts2050(void); int is_sbts2050_trx(int); int is_sbts2050_master(void); @@ -59,9 +55,10 @@ struct sbts2050_power_status { float pa_bias_voltage; }; -void sbts2050_uc_check_temp(int *temp_pa, int *temp_board); -void sbts2050_uc_set_power(int pmaster, int pslave, int ppa); +int sbts2050_uc_check_temp(int *temp_pa, int *temp_board); +int sbts2050_uc_set_power(int pmaster, int pslave, int ppa); int sbts2050_uc_get_status(struct sbts2050_power_status *status); +int sbts2050_uc_set_pa_power(int on_off); void sbts2050_uc_initialize(); #endif |