diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/Makefile.am | 2 | ||||
-rw-r--r-- | include/osmocom/core/tdef.h | 172 | ||||
-rw-r--r-- | include/osmocom/vty/tdef_vty.h | 67 |
3 files changed, 241 insertions, 0 deletions
diff --git a/include/Makefile.am b/include/Makefile.am index 25a6d75f..17f7d1ce 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -47,6 +47,7 @@ nobase_include_HEADERS = \ osmocom/core/statistics.h \ osmocom/core/strrb.h \ osmocom/core/talloc.h \ + osmocom/core/tdef.h \ osmocom/core/timer.h \ osmocom/core/timer_compat.h \ osmocom/core/utils.h \ @@ -154,6 +155,7 @@ nobase_include_HEADERS += \ osmocom/vty/vector.h \ osmocom/vty/vty.h \ osmocom/vty/ports.h \ + osmocom/vty/tdef_vty.h \ osmocom/ctrl/control_vty.h endif diff --git a/include/osmocom/core/tdef.h b/include/osmocom/core/tdef.h new file mode 100644 index 00000000..92b71597 --- /dev/null +++ b/include/osmocom/core/tdef.h @@ -0,0 +1,172 @@ +/*! \file tdef.h + * API to define Tnnn timers globally and use for FSM state changes. + */ +/* + * (C) 2018-2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Neels Hofmeyr <neels@hofmeyr.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 Affero 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 + +#include <stdint.h> +#include <osmocom/core/utils.h> + +struct osmo_fsm_inst; + +/*! \defgroup Tdef Tnnn timer configuration + * @{ + * \file tdef.h + */ + +enum osmo_tdef_unit { + OSMO_TDEF_S = 0, /*!< most T are in seconds, keep 0 as default. */ + OSMO_TDEF_MS, /*!< milliseconds */ + OSMO_TDEF_M, /*!< minutes */ + OSMO_TDEF_CUSTOM, /*!< unspecified unit, explained in osmo_tdef.desc. */ +}; + +extern const struct value_string osmo_tdef_unit_names[]; +/*! \return enum osmo_tdef_unit value as human readable unit letter, or "custom-unit". */ +static inline const char *osmo_tdef_unit_name(enum osmo_tdef_unit val) +{ return get_value_string(osmo_tdef_unit_names, val); } + +/*! Define a GSM timer of the form Tnnn, with unit, default value and doc string. + * Typically used as an array with the last entry being left zero-initialized, e.g.: + * + * struct osmo_tdef tdefs[] = { + * { .T=10, .default_val=6, .desc="RR Assignment" }, + * { .T=101, .default_val=10, .desc="inter-BSC Handover MT, HO Request to HO Accept" }, + * { .T=3101, .default_val=3, .desc="RR Immediate Assignment" }, + * {} + * }; + * + * Program initialization should call osmo_tdefs_reset() so that all timers return the default_val, until e.g. the VTY + * configuration sets user-defined values (see osmo_tdef_vty_init()). + */ +struct osmo_tdef { + /*! T1234 number; type corresponds to struct osmo_fsm_inst.T. Negative and zero T numbers are actually possible, + * but be aware that osmo_tdef_fsm_inst_state_chg() interprets T == 0 as "no timer". */ + const int T; + /*! Timeout duration (according to unit), default value; type corresponds to osmo_fsm_inst_state_chg()'s + * timeout_secs argument. Note that osmo_fsm_inst_state_chg() clamps the range. */ + const unsigned long default_val; + const enum osmo_tdef_unit unit; + /*! Human readable description. For unit == OSMO_TDEF_CUSTOM, this should include an explanation of the value's + * unit. Best keep this a short one-liner (e.g. for VTY output). */ + const char *desc; + /*! Currently active timeout value, e.g. set by user config. This is the only mutable member: a user may + * configure the timeout value, but neither unit nor any other field. */ + unsigned long val; +}; + +/*! Iterate an array of struct osmo_tdef, the last item should be fully zero, i.e. "{}". + * Example: + * + * struct osmo_tdef *t; + * osmo_tdef_for_each(t, tdefs) { + * printf("%lu %s %s\n", t->val, osmo_tdef_unit_name(t->unit), t->desc); + * } + * + * \param[inout] t A struct osmo_tdef *t used for iteration, will point at the current entry inside the loop scope. + * \param[in] tdefs Array of struct osmo_tdef to iterate, zero-terminated. + */ +#define osmo_tdef_for_each(t, tdefs) \ + for (t = tdefs; t && (t->T || t->default_val || t->desc); t++) + +void osmo_tdefs_reset(struct osmo_tdef *tdefs); +unsigned long osmo_tdef_get(const struct osmo_tdef *tdefs, int T, enum osmo_tdef_unit as_unit, + unsigned long val_if_not_present); +struct osmo_tdef *osmo_tdef_get_entry(struct osmo_tdef *tdefs, int T); + +/*! Using osmo_tdef for osmo_fsm_inst: array entry for a mapping of state numbers to timeout definitions. + * For a usage example, see osmo_tdef_get_state_timeout() and test_tdef_state_timeout() in tdef_test.c. */ +struct osmo_tdef_state_timeout { + /*! Timer number to match struct osmo_tdef.T, and to pass to osmo_fsm_inst_state_chg(). */ + int T; + /*! If true, call osmo_fsm_inst_state_chg_keep_timer(). + * If T == 0, keep previous T number, otherwise also set fi->T. */ + bool keep_timer; +}; + +const struct osmo_tdef_state_timeout *osmo_tdef_get_state_timeout(uint32_t state, + const struct osmo_tdef_state_timeout *timeouts_array); + +/*! Call osmo_fsm_inst_state_chg() or osmo_fsm_inst_state_chg_keep_timer(), depending on the timeouts_array, tdefs and + * default_timeout. + * + * A T timer configured in sub-second precision is rounded up to the next full second. A timer in unit = + * OSMO_TDEF_CUSTOM is applied as if the unit is in seconds (i.e. this macro does not make sense for custom units!). + * + * See osmo_tdef_get_state_timeout() and osmo_tdef_get(). + * + * If no T timer is defined for the given state (T == 0), invoke the state change without a timeout. + * + * Should a T number be defined in timeouts_array that is not defined in tdefs, use default_timeout (in seconds). If + * default_timeout is negative, a missing T definition in tdefs instead causes a program abort. + * + * This is best used by wrapping this function call in a macro suitable for a specific FSM implementation, which can + * become as short as: my_fsm_state_chg(fi, NEXT_STATE): + * + * #define my_fsm_state_chg(fi, NEXT_STATE) \ + * osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, my_fsm_timeouts, global_T_defs, 5) + * + * my_fsm_state_chg(fi, MY_FSM_STATE_1); + * // -> No timeout configured, will enter state without timeout. + * + * my_fsm_state_chg(fi, MY_FSM_STATE_3); + * // T423 configured for this state, will look up T423 in tdefs, or use 5 seconds if unset. + * + * my_fsm_state_chg(fi, MY_FSM_STATE_8); + * // keep_timer == true for this state, will invoke osmo_fsm_inst_state_chg_keep_timer(). + * + * \param[inout] fi osmo_fsm_inst to transition to another state. + * \param[in] state State number to transition to. + * \param[in] timeouts_array Array of struct osmo_tdef_state_timeout[32] to look up state in. + * \param[in] tdefs Array of struct osmo_tdef (last entry zero initialized) to look up T in. + * \param[in] default_timeout If a T is set in timeouts_array, but no timeout value is configured for T, then use this + * default timeout value as fallback, or pass -1 to abort the program. + * \return Return value from osmo_fsm_inst_state_chg() or osmo_fsm_inst_state_chg_keep_timer(). + */ +#define osmo_tdef_fsm_inst_state_chg(fi, state, timeouts_array, tdefs, default_timeout) \ + _osmo_tdef_fsm_inst_state_chg(fi, state, timeouts_array, tdefs, default_timeout, \ + __FILE__, __LINE__) +int _osmo_tdef_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t state, + const struct osmo_tdef_state_timeout *timeouts_array, + const struct osmo_tdef *tdefs, unsigned long default_timeout, + const char *file, int line); + +/*! Manage timer definitions in named groups. + * This should be defined as an array with the final element kept fully zero-initialized, + * to be compatible with osmo_tdef_vty* API. There must not be any tdefs == NULL entries except on the final + * zero-initialized entry. */ +struct osmo_tdef_group { + const char *name; + const char *desc; + struct osmo_tdef *tdefs; +}; + +/*! Iterate an array of struct osmo_tdef_group, the last item should be fully zero, i.e. "{}". + * \param[inout] g A struct osmo_tdef_group *g used for iteration, will point at the current entry inside the loop scope. + * \param[in] tdefs Array of struct osmo_tdef_group to iterate, zero-terminated. + */ +#define osmo_tdef_groups_for_each(g, tdef_groups) \ + for (g = tdef_groups; g && g->tdefs; g++) + +/*! @} */ diff --git a/include/osmocom/vty/tdef_vty.h b/include/osmocom/vty/tdef_vty.h new file mode 100644 index 00000000..f55239ad --- /dev/null +++ b/include/osmocom/vty/tdef_vty.h @@ -0,0 +1,67 @@ +/*! \file tdef_vty.h + * API to configure osmo_tdef Tnnn timers from VTY configuration. + */ +/* (C) 2018-2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * Author: Neels Hofmeyr <neels@hofmeyr.de> + * + * 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 Affero 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 + +#include <stdint.h> + +struct vty; + +/*! \defgroup Tdef_VTY Tnnn timer VTY configuration + * @{ + * \file tdef_vty.h + */ + +struct osmo_tdef; +struct osmo_tdef_group; + +#define OSMO_TDEF_VTY_ARG_T "TNNNN" +#define OSMO_TDEF_VTY_DOC_T "T-number, optionally preceded by 't' or 'T'.\n" +#define OSMO_TDEF_VTY_ARG_T_OPTIONAL "[" OSMO_TDEF_VTY_ARG_T "]" + +#define OSMO_TDEF_VTY_ARG_VAL "(<0-2147483647>|default)" +#define OSMO_TDEF_VTY_DOC_VAL "New timer value\n" "Set to default timer value\n" +#define OSMO_TDEF_VTY_ARG_VAL_OPTIONAL "[" OSMO_TDEF_VTY_ARG_VAL "]" + +#define OSMO_TDEF_VTY_ARG_SET OSMO_TDEF_VTY_ARG_T " " OSMO_TDEF_VTY_ARG_VAL +#define OSMO_TDEF_VTY_DOC_SET OSMO_TDEF_VTY_DOC_T OSMO_TDEF_VTY_DOC_VAL +#define OSMO_TDEF_VTY_ARG_SET_OPTIONAL OSMO_TDEF_VTY_ARG_T_OPTIONAL " " OSMO_TDEF_VTY_ARG_VAL_OPTIONAL + +int osmo_tdef_vty_set_cmd(struct vty *vty, struct osmo_tdef *tdefs, const char **args); +int osmo_tdef_vty_show_cmd(struct vty *vty, struct osmo_tdef *tdefs, const char *T_arg, + const char *prefix_fmt, ...); +void osmo_tdef_vty_write(struct vty *vty, struct osmo_tdef *tdefs, + const char *prefix_fmt, ...); + +void osmo_tdef_vty_out_one(struct vty *vty, struct osmo_tdef *t, const char *prefix_fmt, ...); +void osmo_tdef_vty_out_all(struct vty *vty, struct osmo_tdef *tdefs, const char *prefix_fmt, ...); + +void osmo_tdef_vty_out_one_va(struct vty *vty, struct osmo_tdef *t, const char *prefix_fmt, va_list va); +void osmo_tdef_vty_out_all_va(struct vty *vty, struct osmo_tdef *tdefs, const char *prefix_fmt, va_list va); + +struct osmo_tdef *osmo_tdef_vty_parse_T_arg(struct vty *vty, struct osmo_tdef *tdefs, const char *osmo_tdef_str); +unsigned long osmo_tdef_vty_parse_val_arg(const char *val_arg, unsigned long default_val); + +void osmo_tdef_vty_groups_init(enum node_type parent_node, struct osmo_tdef_group *groups); +void osmo_tdef_vty_groups_write(struct vty *vty, const char *indent); + +/*! @} */ |