aboutsummaryrefslogtreecommitdiffstats
path: root/src/osmo-bts-sysmo/misc
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2012-07-11 01:31:28 +0200
committerHarald Welte <laforge@gnumonks.org>2012-07-11 01:32:42 +0200
commit38420fb9513db552d80629035ef4e92315e35b6d (patch)
tree1d0542a1b8beb65db377598dff98e2793be339c4 /src/osmo-bts-sysmo/misc
parent3696c6946d9b0e683093dd6e87d16629f236d4f9 (diff)
add new sysmobst-mgr daemon
This daemon is taking care of counting the number of hours in operation and to watch the system temperature as determined by internal temperature sensors. Later, it will export an external interface for firmware reload, as well as a way to raise OML ALARMs in case of temperature issues or other problems.
Diffstat (limited to 'src/osmo-bts-sysmo/misc')
-rw-r--r--src/osmo-bts-sysmo/misc/sysmobts_eeprom.h19
-rw-r--r--src/osmo-bts-sysmo/misc/sysmobts_mgr.c163
-rw-r--r--src/osmo-bts-sysmo/misc/sysmobts_mgr.h9
-rw-r--r--src/osmo-bts-sysmo/misc/sysmobts_misc.c268
-rw-r--r--src/osmo-bts-sysmo/misc/sysmobts_misc.h31
-rw-r--r--src/osmo-bts-sysmo/misc/sysmobts_par.c226
-rw-r--r--src/osmo-bts-sysmo/misc/sysmobts_par.h26
7 files changed, 742 insertions, 0 deletions
diff --git a/src/osmo-bts-sysmo/misc/sysmobts_eeprom.h b/src/osmo-bts-sysmo/misc/sysmobts_eeprom.h
new file mode 100644
index 00000000..4d27d028
--- /dev/null
+++ b/src/osmo-bts-sysmo/misc/sysmobts_eeprom.h
@@ -0,0 +1,19 @@
+#ifndef _SYSMOBTS_EEPROM_H
+#define _SYSMOBTS_EEPROM_H
+
+#include <stdint.h>
+
+struct sysmobts_eeprom { /* offset */
+ uint8_t eth_mac[6]; /* 0-5 */
+ uint8_t _pad0[10]; /* 6-15 */
+ uint16_t clk_cal_fact; /* 16-17 */
+ uint8_t temp1_max; /* 18 */
+ uint8_t temp2_max; /* 19 */
+ uint32_t serial_nr; /* 20-23 */
+ uint32_t operational_hours; /* 24-27 */
+ uint32_t boot_count; /* 28-31 */
+ uint8_t _pad1[89]; /* 32-127 */
+ uint8_t gpg_key[128]; /* 121-249 */
+} __attribute__((packed));
+
+#endif
diff --git a/src/osmo-bts-sysmo/misc/sysmobts_mgr.c b/src/osmo-bts-sysmo/misc/sysmobts_mgr.c
new file mode 100644
index 00000000..01d65ce7
--- /dev/null
+++ b/src/osmo-bts-sysmo/misc/sysmobts_mgr.c
@@ -0,0 +1,163 @@
+/* Main program for SysmoBTS management daemon */
+
+/* (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 <errno.h>
+#include <getopt.h>
+#include <limits.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/vty/telnet_interface.h>
+#include <osmocom/vty/logging.h>
+
+#include "misc/sysmobts_misc.h"
+#include "misc/sysmobts_mgr.h"
+
+static int daemonize = 0;
+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)
+
+static struct osmo_timer_list temp_timer;
+static void check_temp_timer_cb(void *unused)
+{
+ sysmobts_check_temp();
+
+ osmo_timer_schedule(&temp_timer, TEMP_TIMER_SECS, 0);
+}
+
+static struct osmo_timer_list hours_timer;
+static void hours_timer_cb(void *unused)
+{
+ sysmobts_update_hours();
+
+ osmo_timer_schedule(&hours_timer, HOURS_TIMER_SECS, 0);
+}
+
+static void signal_handler(int signal)
+{
+ fprintf(stderr, "signal %u received\n", signal);
+
+ switch (signal) {
+ case SIGINT:
+ sysmobts_check_temp();
+ sysmobts_update_hours();
+ exit(0);
+ break;
+ case SIGABRT:
+ case SIGUSR1:
+ case SIGUSR2:
+ talloc_report_full(tall_mgr_ctx, stderr);
+ break;
+ default:
+ break;
+ }
+}
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/utils.h>
+
+#include <osmo-bts/bts.h>
+#include <osmo-bts/logging.h>
+
+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 = "DSP/FPGA firmware management",
+ .color = "\033[1;36m",
+ .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(const char *category_mask)
+{
+ osmo_init_logging(&mgr_log_info);
+
+ if (category_mask)
+ log_parse_category_mask(osmo_stderr_target, category_mask);
+
+ 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);
+
+ //handle_options(argc, argv);
+
+ mgr_log_init(NULL);
+
+ signal(SIGINT, &signal_handler);
+ //signal(SIGABRT, &signal_handler);
+ signal(SIGUSR1, &signal_handler);
+ signal(SIGUSR2, &signal_handler);
+ osmo_init_ignore_signals();
+
+ if (daemonize) {
+ rc = osmo_daemonize();
+ if (rc < 0) {
+ perror("Error during daemonize");
+ 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);
+
+ while (1) {
+ log_reset_context();
+ osmo_select_main(0);
+ }
+}
diff --git a/src/osmo-bts-sysmo/misc/sysmobts_mgr.h b/src/osmo-bts-sysmo/misc/sysmobts_mgr.h
new file mode 100644
index 00000000..3b948b1c
--- /dev/null
+++ b/src/osmo-bts-sysmo/misc/sysmobts_mgr.h
@@ -0,0 +1,9 @@
+#ifndef _SYSMOBTS_MGR_H
+#define _SYSMOBTS_MGR_H
+
+enum {
+ DTEMP,
+ DFW,
+};
+
+#endif
diff --git a/src/osmo-bts-sysmo/misc/sysmobts_misc.c b/src/osmo-bts-sysmo/misc/sysmobts_misc.c
new file mode 100644
index 00000000..266eb00b
--- /dev/null
+++ b/src/osmo-bts-sysmo/misc/sysmobts_misc.c
@@ -0,0 +1,268 @@
+/* (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/application.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/logging.h>
+
+#include "sysmobts_misc.h"
+#include "sysmobts_par.h"
+#include "sysmobts_mgr.h"
+
+
+/*********************************************************************
+ * Temperature handling
+ *********************************************************************/
+
+#define TEMP_PATH "/sys/class/hwmon/hwmon0/device%u_%s"
+
+static const char *temp_type_str[_NUM_TEMP_TYPES] = {
+ [SYSMOBTS_TEMP_INPUT] = "input",
+ [SYSMOBTS_TEMP_LOWEST] = "lowest",
+ [SYSMOBTS_TEMP_HIGHEST] = "highest",
+};
+
+int sysmobts_temp_get(enum sysmobts_temp_sensor sensor,
+ enum sysmobts_temp_type type)
+{
+ char buf[PATH_MAX];
+ char tempstr[8];
+ int fd, rc;
+
+ if (sensor < SYSMOBTS_TEMP_DIGITAL ||
+ sensor > SYSMOBTS_TEMP_RF)
+ return -EINVAL;
+
+ if (type > _NUM_TEMP_TYPES)
+ return -EINVAL;
+
+ snprintf(buf, sizeof(buf)-1, TEMP_PATH, 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);
+
+ return atoi(tempstr);
+}
+
+static const struct {
+ const char *name;
+ enum sysmobts_temp_sensor sensor;
+ enum sysmobts_par ee_par;
+} temp_data[] = {
+ {
+ .name = "digital",
+ .sensor = SYSMOBTS_TEMP_DIGITAL,
+ .ee_par = SYSMOBTS_PAR_TEMP_DIG_MAX,
+ }, {
+ .name = "rf",
+ .sensor = SYSMOBTS_TEMP_RF,
+ .ee_par = SYSMOBTS_PAR_TEMP_RF_MAX,
+ }
+};
+
+void sysmobts_check_temp(void)
+{
+ 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++) {
+ temp_old[i] = sysmobts_par_get_int(temp_data[i].ee_par) * 1000;
+ temp_hi[i] = sysmobts_temp_get(temp_data[i].sensor,
+ SYSMOBTS_TEMP_HIGHEST);
+ temp_cur[i] = sysmobts_temp_get(temp_data[i].sensor,
+ SYSMOBTS_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");
+ return;
+ }
+
+ 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);
+ rc = sysmobts_par_set_int(SYSMOBTS_PAR_TEMP_DIG_MAX,
+ temp_hi[0]/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 sysmobts_update_hours(void)
+{
+ time_t now = time(NULL);
+ int op_hrs;
+
+ /* first time after start of manager program */
+ if (last_update == 0) {
+ last_update = now;
+
+ op_hrs = sysmobts_par_get_int(SYSMOBTS_PAR_HOURS);
+ if (op_hrs < 0) {
+ LOGP(DTEMP, LOGL_ERROR, "Unable to read "
+ "operational hours: %d (%s)\n", op_hrs,
+ strerror(errno));
+ return op_hrs;
+ }
+
+ LOGP(DTEMP, LOGL_INFO, "Total hours of Operation: %u\n",
+ op_hrs);
+
+ return 0;
+ }
+
+ if (now >= last_update + 3600) {
+ int rc;
+ op_hrs = sysmobts_par_get_int(SYSMOBTS_PAR_HOURS);
+ if (op_hrs < 0) {
+ LOGP(DTEMP, LOGL_ERROR, "Unable to read "
+ "operational hours: %d (%s)\n", op_hrs,
+ strerror(errno));
+ return op_hrs;
+ }
+
+ /* number of hours to increase */
+ op_hrs += (now-last_update)/3600;
+
+ LOGP(DTEMP, LOGL_INFO, "Total hours of Operation: %u\n",
+ op_hrs);
+
+ rc = sysmobts_par_set_int(SYSMOBTS_PAR_HOURS, op_hrs);
+ if (rc < 0)
+ return rc;
+
+ last_update = now;
+ }
+
+ return 0;
+}
+
+/*********************************************************************
+ * Firmware reloading
+ *********************************************************************/
+
+#define SYSMOBTS_FW_PATH "/lib/firmware"
+
+static const char *fw_names[_NUM_FW] = {
+ [SYSMOBTS_FW_FPGA] = "sysmobts-v2.bit",
+ [SYSMOBTS_FW_DSP] = "sysmobts-v2.out",
+};
+static const char *fw_devs[_NUM_FW] = {
+ [SYSMOBTS_FW_FPGA] = "/dev/fpgadl_par0",
+ [SYSMOBTS_FW_DSP] = "/dev/dspdl_dm644x_0",
+};
+
+int sysmobts_firmware_reload(enum sysmobts_firmware_type type)
+{
+ char name[PATH_MAX];
+ uint8_t buf[1024];
+ int fd_in, fd_out, rc;
+
+ if (type >= _NUM_FW)
+ return -EINVAL;
+
+ snprintf(name, sizeof(name)-1, "%s/%s",
+ SYSMOBTS_FW_PATH, fw_names[type]);
+ name[sizeof(name)-1] = '\0';
+
+ fd_in = open(name, O_RDONLY);
+ if (fd_in < 0) {
+ LOGP(DFW, LOGL_ERROR, "unable ot open firmware file %s: %s\n",
+ name, strerror(errno));
+ return fd_in;
+ }
+
+ fd_out = open(fw_devs[type], O_WRONLY);
+ if (fd_out < 0) {
+ LOGP(DFW, LOGL_ERROR, "unable ot open firmware device %s: %s\n",
+ fw_devs[type], strerror(errno));
+ close(fd_in);
+ return fd_out;
+ }
+
+ while ((rc = read(fd_in, buf, sizeof(buf)))) {
+ int written;
+
+ if (rc < 0) {
+ LOGP(DFW, LOGL_ERROR, "error %d during read "
+ "from %s: %s\n", rc, name, strerror(errno));
+ close(fd_in);
+ close(fd_out);
+ return -EIO;
+ }
+
+ written = write(fd_out, buf, rc);
+ if (written < rc) {
+ LOGP(DFW, LOGL_ERROR, "short write during "
+ "fw write to %s\n", fw_devs[type]);
+ close(fd_in);
+ close(fd_out);
+ return -EIO;
+ }
+ }
+
+ close(fd_in);
+ close(fd_out);
+
+ return 0;
+}
diff --git a/src/osmo-bts-sysmo/misc/sysmobts_misc.h b/src/osmo-bts-sysmo/misc/sysmobts_misc.h
new file mode 100644
index 00000000..13d0add3
--- /dev/null
+++ b/src/osmo-bts-sysmo/misc/sysmobts_misc.h
@@ -0,0 +1,31 @@
+#ifndef _SYSMOBTS_MISC_H
+#define _SYSMOBTS_MISC_H
+
+enum sysmobts_temp_sensor {
+ SYSMOBTS_TEMP_DIGITAL = 1,
+ SYSMOBTS_TEMP_RF = 2,
+};
+
+enum sysmobts_temp_type {
+ SYSMOBTS_TEMP_INPUT,
+ SYSMOBTS_TEMP_LOWEST,
+ SYSMOBTS_TEMP_HIGHEST,
+ _NUM_TEMP_TYPES
+};
+
+int sysmobts_temp_get(enum sysmobts_temp_sensor sensor,
+ enum sysmobts_temp_type type);
+
+void sysmobts_check_temp(void);
+
+int sysmobts_update_hours(void);
+
+enum sysmobts_firmware_type {
+ SYSMOBTS_FW_FPGA,
+ SYSMOBTS_FW_DSP,
+ _NUM_FW
+};
+
+int sysmobts_firmware_reload(enum sysmobts_firmware_type type);
+
+#endif
diff --git a/src/osmo-bts-sysmo/misc/sysmobts_par.c b/src/osmo-bts-sysmo/misc/sysmobts_par.c
new file mode 100644
index 00000000..3399e4ff
--- /dev/null
+++ b/src/osmo-bts-sysmo/misc/sysmobts_par.c
@@ -0,0 +1,226 @@
+/* sysmobts - access to hardware related parameters */
+
+/* (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 <sys/types.h>
+#include <sys/stat.h>
+
+#include "sysmobts_eeprom.h"
+#include "sysmobts_par.h"
+
+#define EEPROM_PATH "/sys/devices/platform/i2c_davinci.1/i2c-1/1-0050/eeprom"
+
+
+static struct {
+ int read;
+ struct sysmobts_eeprom ee;
+} g_ee;
+
+static struct sysmobts_eeprom *get_eeprom(int update_rqd)
+{
+ if (update_rqd || g_ee.read == 0) {
+ int fd, rc;
+
+ fd = open(EEPROM_PATH, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ rc = read(fd, &g_ee.ee, sizeof(g_ee.ee));
+
+ close(fd);
+
+ if (rc < sizeof(g_ee.ee))
+ return NULL;
+
+ g_ee.read = 1;
+ }
+
+ return &g_ee.ee;
+}
+
+static int set_eeprom(struct sysmobts_eeprom *ee)
+{
+ int fd, rc;
+
+ memcpy(&g_ee.ee, ee, sizeof(*ee));
+
+ fd = open(EEPROM_PATH, O_WRONLY);
+ if (fd < 0)
+ return fd;
+
+ rc = write(fd, ee, sizeof(*ee));
+ if (rc < sizeof(*ee)) {
+ close(fd);
+ return -EIO;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+int sysmobts_par_get_int(enum sysmobts_par par)
+{
+ int ret;
+ struct sysmobts_eeprom *ee = get_eeprom(0);
+
+ if (!ee)
+ return -EIO;
+
+ if (par >= _NUM_SYSMOBTS_PAR)
+ return -ENODEV;
+
+ switch (par) {
+ case SYSMOBTS_PAR_CLK_FACTORY:
+ ret = ee->clk_cal_fact;
+ break;
+ case SYSMOBTS_PAR_TEMP_DIG_MAX:
+ ret = ee->temp1_max;
+ break;
+ case SYSMOBTS_PAR_TEMP_RF_MAX:
+ ret = ee->temp2_max;
+ break;
+ case SYSMOBTS_PAR_SERNR:
+ ret = ee->serial_nr;
+ break;
+ case SYSMOBTS_PAR_HOURS:
+ ret = ee->operational_hours;
+ break;
+ case SYSMOBTS_PAR_BOOTS:
+ ret = ee->boot_count;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+int sysmobts_par_set_int(enum sysmobts_par par, unsigned int val)
+{
+ struct sysmobts_eeprom *ee = get_eeprom(1);
+
+ if (!ee)
+ return -EIO;
+
+ if (par >= _NUM_SYSMOBTS_PAR)
+ return -ENODEV;
+
+ switch (par) {
+ case SYSMOBTS_PAR_CLK_FACTORY:
+ ee->clk_cal_fact = val;
+ break;
+ case SYSMOBTS_PAR_TEMP_DIG_MAX:
+ ee->temp1_max = val;
+ break;
+ case SYSMOBTS_PAR_TEMP_RF_MAX:
+ ee->temp2_max = val;
+ break;
+ case SYSMOBTS_PAR_SERNR:
+ ee->serial_nr = val;
+ break;
+ case SYSMOBTS_PAR_HOURS:
+ ee->operational_hours = val;
+ break;
+ case SYSMOBTS_PAR_BOOTS:
+ ee->boot_count = val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ set_eeprom(ee);
+
+ return 0;
+}
+
+int sysmobts_par_get_buf(enum sysmobts_par par, uint8_t *buf,
+ unsigned int size)
+{
+ uint8_t *ptr;
+ unsigned int len;
+ struct sysmobts_eeprom *ee = get_eeprom(0);
+
+ if (!ee)
+ return -EIO;
+
+ if (par >= _NUM_SYSMOBTS_PAR)
+ return -ENODEV;
+
+ switch (par) {
+ case SYSMOBTS_PAR_MAC:
+ ptr = ee->eth_mac;
+ len = sizeof(ee->eth_mac);
+ break;
+ case SYSMOBTS_PAR_KEY:
+ ptr = ee->gpg_key;
+ len = sizeof(ee->gpg_key);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (size < len)
+ len = size;
+ memcpy(buf, ptr, len);
+
+ return len;
+}
+
+int sysmobts_par_set_buf(enum sysmobts_par par, const uint8_t *buf,
+ unsigned int size)
+{
+ uint8_t *ptr;
+ unsigned int len;
+ struct sysmobts_eeprom *ee = get_eeprom(0);
+
+ if (!ee)
+ return -EIO;
+
+ if (par >= _NUM_SYSMOBTS_PAR)
+ return -ENODEV;
+
+ switch (par) {
+ case SYSMOBTS_PAR_MAC:
+ ptr = ee->eth_mac;
+ len = sizeof(ee->eth_mac);
+ break;
+ case SYSMOBTS_PAR_KEY:
+ ptr = ee->gpg_key;
+ len = sizeof(ee->gpg_key);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (len < size)
+ size = len;
+
+ memcpy(ptr, buf, size);
+
+ return len;
+}
diff --git a/src/osmo-bts-sysmo/misc/sysmobts_par.h b/src/osmo-bts-sysmo/misc/sysmobts_par.h
new file mode 100644
index 00000000..85a38abc
--- /dev/null
+++ b/src/osmo-bts-sysmo/misc/sysmobts_par.h
@@ -0,0 +1,26 @@
+#ifndef _SYSMOBTS_PAR_H
+#define _SYSMOBTS_PAR_H
+
+#include <stdint.h>
+
+enum sysmobts_par {
+ SYSMOBTS_PAR_MAC,
+ SYSMOBTS_PAR_CLK_FACTORY,
+ SYSMOBTS_PAR_TEMP_DIG_MAX,
+ SYSMOBTS_PAR_TEMP_RF_MAX,
+ SYSMOBTS_PAR_SERNR,
+ SYSMOBTS_PAR_HOURS,
+ SYSMOBTS_PAR_BOOTS,
+ SYSMOBTS_PAR_KEY,
+ _NUM_SYSMOBTS_PAR
+};
+
+
+int sysmobts_par_get_int(enum sysmobts_par par);
+int sysmobts_par_set_int(enum sysmobts_par par, unsigned int val);
+int sysmobts_par_get_buf(enum sysmobts_par par, uint8_t *buf,
+ unsigned int size);
+int sysmobts_par_set_buf(enum sysmobts_par par, const uint8_t *buf,
+ unsigned int size);
+
+#endif