Tnnn timer configuration

Implementation to define Tnnn timers globally and use for FSM state changes. More...

## Files

file | tdef.h
| API to define Tnnn timers globally and use for FSM state changes.
file | tdef.c
| Implementation to define Tnnn timers globally and use for FSM state changes.

## Data Structures

struct | osmo_tdef
| Define a GSM timer of the form Tnnn, with unit, default value and doc string. More...
struct | osmo_tdef_state_timeout
| Using osmo_tdef for osmo_fsm_inst: array entry for a mapping of state numbers to timeout definitions. More...
struct | osmo_tdef_group
| Manage timer definitions in named groups. More...

## Macros

#define | osmo_tdef_for_each(t, tdefs) for (t = tdefs; t && (t->T || t->default_val || t->desc); t++)
| Iterate an array of struct osmo_tdef, the last item should be fully zero, i.e. More...
#define | osmo_tdef_fsm_inst_state_chg(fi, state, timeouts_array, tdefs, default_timeout)
| Call osmo_fsm_inst_state_chg() or osmo_fsm_inst_state_chg_keep_timer(), depending on the timeouts_array, tdefs and default_timeout. More...
#define | osmo_tdef_groups_for_each(g, tdef_groups) for (g = tdef_groups; g && g->tdefs; g++)
| Iterate an array of struct osmo_tdef_group, the last item should be fully zero, i.e. More...

## Enumerations

enum | osmo_tdef_unit { OSMO_TDEF_S = 0 , OSMO_TDEF_MS , OSMO_TDEF_M , OSMO_TDEF_CUSTOM , OSMO_TDEF_US }

## Functions

static const char * | osmo_tdef_unit_name (enum osmo_tdef_unit val)
void | osmo_tdefs_reset (struct osmo_tdef *tdefs)
| Set all osmo_tdef values to the default_val. More...
unsigned long | osmo_tdef_get (const struct osmo_tdef *tdefs, int T, enum osmo_tdef_unit as_unit, long val_if_not_present)
| Return the value of a T timer from a list of osmo_tdef, in the given unit. More...
struct osmo_tdef * | osmo_tdef_get_entry (struct osmo_tdef *tdefs, int T)
| Find tdef entry matching T. More...
int | osmo_tdef_set (struct osmo_tdef *tdefs, int T, unsigned long val, enum osmo_tdef_unit val_unit)
| Set value in entry matching T, converting val from val_unit to unit of T. More...
bool | osmo_tdef_val_in_range (struct osmo_tdef *tdef, unsigned long new_val)
| Check if value new_val is in range of valid possible values for timer entry tdef. More...
int | osmo_tdef_range_str_buf (char *buf, size_t buf_len, struct osmo_tdef *t)
| Write string representation of osmo_tdef range into buf. More...
const struct osmo_tdef_state_timeout * | osmo_tdef_get_state_timeout (uint32_t state, const struct osmo_tdef_state_timeout *timeouts_array)
| Using osmo_tdef for osmo_fsm_inst: find a given state's osmo_tdef_state_timeout entry. More...
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, signed long default_timeout, const char *file, int line)
static unsigned long | osmo_tdef_factor (enum osmo_tdef_unit a, enum osmo_tdef_unit b)
| a = return_val * b. More...
static unsigned long | osmo_tdef_round (unsigned long val, enum osmo_tdef_unit from_unit, enum osmo_tdef_unit to_unit)
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, long default_timeout, const char *file, int line)
| See invocation macro osmo_tdef_fsm_inst_state_chg() instead. More...

## Variables

const struct value_string | osmo_tdef_unit_names []
const struct value_string | osmo_tdef_unit_names []

## Detailed Description

Implementation to define Tnnn timers globally and use for FSM state changes.

See also Tnnn timer VTY configuration

osmo_tdef provides:

- a list of Tnnnn (GSM) timers with description, unit and default value.
- vty UI to allow users to configure non-default timeouts.
- API to tie T timers to osmo_fsm states and set them on state transitions.
- a few standard units (minute, second, millisecond) as well as a custom unit (which relies on the timer's human readable description to indicate the meaning of the value).
- conversion for standard units: for example, some GSM timers are defined in minutes, while our FSM definitions need timeouts in seconds. Conversion is for convenience only and can be easily avoided via the custom unit.

By keeping separate osmo_tdef arrays, several groups of timers can be kept separately. The VTY tests in tests/tdef/ showcase different schemes:

- tests/vty/tdef_vty_test_config_root.c: Keep several timer definitions in separately named groups: showcase the osmo_tdef_vty_groups*() API. Each timer group exists exactly once.
- tests/vty/tdef_vty_test_config_subnode.c: Keep a single list of timers without separate grouping. Put this list on a specific subnode below the CONFIG_NODE. There could be several separate subnodes with timers like this, i.e. continuing from this example, sets of timers could be separated by placing timers in specific config subnodes instead of using the global group name.
- tests/vty/tdef_vty_test_dynamic.c: Dynamically allocate timer definitions per each new created object. Thus there can be an arbitrary number of independent timer definitions, one per allocated object.

osmo_tdef was introduced because:

- without osmo_tdef, each invocation of osmo_fsm_inst_state_chg() needs to be programmed with the right timeout value, for all code paths that invoke this state change. It is a likely source of errors to get one of them wrong. By defining a T timer exactly for an FSM state, the caller can merely invoke the state change and trust on the original state definition to apply the correct timeout.
- it is helpful to have a standardized config file UI to provide user configurable timeouts, instead of inventing new VTY commands for each separate application of T timer numbers. See tdef_vty.h.

## Macro Definition Documentation

## osmo_tdef_for_each

#define osmo_tdef_for_each | ( |  | t,
|  |  | tdefs
| ) |  | for (t = tdefs; t && (t->T || t->default_val || t->desc); t++)

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);
    }

Parameters

  [in,out] | t | A struct osmo_tdef *t used for iteration, will point at the current entry inside the loop scope.
  [in] | tdefs | Array of struct osmo_tdef to iterate, zero-terminated.

## osmo_tdef_fsm_inst_state_chg

#define osmo_tdef_fsm_inst_state_chg | ( |  | fi,
|  |  | state,
|  |  | timeouts_array,
|  |  | tdefs,
|  |  | default_timeout
| ) |  |

Value:
_osmo_tdef_fsm_inst_state_chg(fi, state, timeouts_array, tdefs, default_timeout, \
__FILE__, __LINE__)
tdefs
struct osmo_tdef * tdefs
_osmo_tdef_fsm_inst_state_chg
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, signed long default_timeout, const char *file, int line)

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().

Parameters

  [in,out] | fi | osmo_fsm_inst to transition to another state.
  [in] | state | State number to transition to.
  [in] | timeouts_array | Array of struct osmo_tdef_state_timeout[32] to look up state in.
  [in] | tdefs | Array of struct osmo_tdef (last entry zero initialized) to look up T in.
  [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.

Returns
  Return value from osmo_fsm_inst_state_chg() or osmo_fsm_inst_state_chg_keep_timer().

## osmo_tdef_groups_for_each

#define osmo_tdef_groups_for_each | ( |  | g,
|  |  | tdef_groups
| ) |  | for (g = tdef_groups; g && g->tdefs; g++)

Iterate an array of struct osmo_tdef_group, the last item should be fully zero, i.e.

"{}".

Parameters

  [in,out] | g | A struct osmo_tdef_group *g used for iteration, will point at the current entry inside the loop scope.
  [in] | tdefs | Array of struct osmo_tdef_group to iterate, zero-terminated.

## Enumeration Type Documentation

## osmo_tdef_unit

enum osmo_tdef_unit

Enumerator
OSMO_TDEF_S | 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.
OSMO_TDEF_US | microseconds

## Function Documentation

## _osmo_tdef_fsm_inst_state_chg() [1/2]

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,
|  | long | default_timeout,
|  | const char * | file,
|  | int | line
| ) |  |

See invocation macro osmo_tdef_fsm_inst_state_chg() instead.

Parameters

  [in] | file | Source file name, like FILE.
  [in] | line | Source file line number, like LINE.

References _osmo_fsm_inst_state_chg(), _osmo_fsm_inst_state_chg_keep_or_start_timer(), _osmo_fsm_inst_state_chg_keep_timer(), file(), osmo_tdef_state_timeout::keep_timer, OSMO_ASSERT, osmo_tdef_get(), osmo_tdef_get_state_timeout(), OSMO_TDEF_S, osmo_tdef_state_timeout::T, and tdefs.

## _osmo_tdef_fsm_inst_state_chg() [2/2]

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,
|  | signed long | default_timeout,
|  | const char * | file,
|  | int | line
| ) |  |

## osmo_tdef_factor()

static unsigned long osmo_tdef_factor | ( | enum osmo_tdef_unit | a,
|  | enum osmo_tdef_unit | b
| ) |  |

static

a = return_val * b.

Returns
  0 if factor is below 1.

References OSMO_TDEF_CUSTOM, OSMO_TDEF_M, OSMO_TDEF_MS, OSMO_TDEF_S, and OSMO_TDEF_US.

Referenced by osmo_tdef_round().

## osmo_tdef_get()

unsigned long osmo_tdef_get | ( | const struct osmo_tdef * | tdefs,
|  | int | T,
|  | enum osmo_tdef_unit | as_unit,
|  | long | val_if_not_present
| ) |  |

Return the value of a T timer from a list of osmo_tdef, in the given unit.

If no such timer is defined, return the default value passed, or abort the program if default < 0.

Round up any value match as_unit: 1100 ms as OSMO_TDEF_S becomes 2 seconds, as OSMO_TDEF_M becomes one minute. However, always return a value of zero as zero (0 ms as OSMO_TDEF_M still is 0 m).

Range: even though the value range is unsigned long here, in practice, using ULONG_MAX as value for a timeout in seconds may actually wrap to negative or low timeout values (e.g. in struct timeval). It is recommended to stay below INT_MAX seconds. See also osmo_fsm_inst_state_chg().

Usage example:

        struct osmo_tdef global_T_defs[] = {
                { .T=7, .default_val=50, .desc="Water Boiling Timeout" },  // default is .unit=OSMO_TDEF_S == 0
                { .T=8, .default_val=300, .desc="Tea brewing" },
                { .T=9, .default_val=5, .unit=OSMO_TDEF_M, .desc="Let tea cool down before drinking" },
                { .T=10, .default_val=20, .unit=OSMO_TDEF_M, .desc="Forgot to drink tea while it's warm" },
                {}  //  <-- important! last entry shall be zero
        };
        osmo_tdefs_reset(global_T_defs); // make all values the default
        osmo_tdef_vty_init(global_T_defs, CONFIG_NODE);

        val = osmo_tdef_get(global_T_defs, 7, OSMO_TDEF_S, -1); // -> 50
        sleep(val);

        val = osmo_tdef_get(global_T_defs, 7, OSMO_TDEF_M, -1); // 50 seconds becomes 1 minute -> 1
        sleep_minutes(val);

        val = osmo_tdef_get(global_T_defs, 99, OSMO_TDEF_S, 3); // not defined, returns 3

        val = osmo_tdef_get(global_T_defs, 99, OSMO_TDEF_S, -1); // not defined, program aborts!

Parameters

  [in] | tdefs | Array of timer definitions, last entry must be fully zero initialized.
  [in] | T | Timer number to get the value for.
  [in] | as_unit | Return timeout value in this unit.
  [in] | val_if_not_present | Fallback value to return if no timeout is defined.

Returns
  Timeout value in the unit given by as_unit, rounded up if necessary, or val_if_not_present.

References OSMO_ASSERT, osmo_tdef_get_entry(), osmo_tdef_round(), osmo_tdef::T, tdefs, osmo_tdef::unit, and osmo_tdef::val.

Referenced by _osmo_tdef_fsm_inst_state_chg().

## osmo_tdef_get_entry()

struct osmo_tdef * osmo_tdef_get_entry | ( | struct osmo_tdef * | tdefs,
|  | int | T
| ) |  |

Find tdef entry matching T.

This is useful for manipulation, which is usually limited to the VTY configuration. To retrieve a timeout value, most callers probably should use osmo_tdef_get() instead.

Parameters

  [in] | tdefs | Array of timer definitions, last entry being fully zero.
  [in] | T | Timer number to get the entry for.

Returns
  osmo_tdef entry matching T in given array, or NULL if no match is found.

References osmo_tdef_for_each, osmo_tdef::T, and tdefs.

Referenced by osmo_tdef_get(), and osmo_tdef_set().

## osmo_tdef_get_state_timeout()

const struct osmo_tdef_state_timeout * osmo_tdef_get_state_timeout | ( | uint32_t | state,
|  | const struct osmo_tdef_state_timeout * | timeouts_array
| ) |  |

Using osmo_tdef for osmo_fsm_inst: find a given state's osmo_tdef_state_timeout entry.

The timeouts_array shall contain exactly 32 elements, regardless whether only some of them are actually populated with nonzero values. 32 corresponds to the number of states allowed by the osmo_fsm_* API. Lookup is by array index. Not populated entries imply a state change invocation without timeout.

For example:

        struct osmo_tdef_state_timeout my_fsm_timeouts[32] = {
                [MY_FSM_STATE_3] = { .T = 423 }, // look up timeout configured for T423
                [MY_FSM_STATE_7] = { .keep_timer = true, .T = 235 }, // keep previous timer if running, or start T235
                [MY_FSM_STATE_8] = { .keep_timer = true }, // keep previous state's T number, continue timeout.
                // any state that is omitted will remain zero == no timeout
     };
     osmo_tdef_get_state_timeout(MY_FSM_STATE_0, &my_fsm_timeouts) -> NULL,
     osmo_tdef_get_state_timeout(MY_FSM_STATE_7, &my_fsm_timeouts) -> { .T = 235 }

The intention is then to obtain the timer like osmo_tdef_get(global_T_defs, T=235); see also fsm_inst_state_chg_T() below.

Parameters

  [in] | state | State constant to look up.
  [in] | timeouts_array | Array[32] of struct osmo_tdef_state_timeout defining which timer number to use per state.

Returns
  A struct osmo_tdef_state_timeout entry, or NULL if that entry is zero initialized.

References osmo_tdef_state_timeout::keep_timer, OSMO_ASSERT, and osmo_tdef_state_timeout::T.

Referenced by _osmo_tdef_fsm_inst_state_chg().

## osmo_tdef_range_str_buf()

int osmo_tdef_range_str_buf | ( | char * | buf,
|  | size_t | buf_len,
|  | struct osmo_tdef * | t
| ) |  |

Write string representation of osmo_tdef range into buf.

Parameters

  [in] | buf | The buffer where the string representation is stored.
  [in] | buf_len | Length of buffer in bytes.
  [in] | tdef | Timer entry from a timer definition table.

Returns
  The number of characters printed on success (or number of characters which would have been written to the final string if enough space had been available), negative on error. See snprintf().

References len(), osmo_tdef::max_val, osmo_tdef::min_val, and OSMO_SNPRINTF_RET.

Referenced by osmo_tdefs_reset().

## osmo_tdef_round()

static unsigned long osmo_tdef_round | ( | unsigned long | val,
|  | enum osmo_tdef_unit | from_unit,
|  | enum osmo_tdef_unit | to_unit
| ) |  |

static

Returns
  val in unit to_unit, rounded up to the next integer value and clamped to ULONG_MAX, or 0 if val == 0.

References osmo_tdef_factor().

Referenced by osmo_tdef_get(), and osmo_tdef_set().

## osmo_tdef_set()

int osmo_tdef_set | ( | struct osmo_tdef * | tdefs,
|  | int | T,
|  | unsigned long | val,
|  | enum osmo_tdef_unit | val_unit
| ) |  |

Set value in entry matching T, converting val from val_unit to unit of T.

The converted value is rounded up to the next integer value of T's unit and clamped to ULONG_MAX, or 0 if val == 0.

Parameters

  [in] | tdefs | Array of timer definitions, last entry being fully zero.
  [in] | T | Timer number to set the value for.
  [in] | val | The new timer value to set.
  [in] | val_unit | Units of value in parameter val.

Returns
  0 on success, negative on error.

References osmo_tdef_get_entry(), osmo_tdef_round(), osmo_tdef_val_in_range(), osmo_tdef::T, tdefs, osmo_tdef::unit, and osmo_tdef::val.

## osmo_tdef_unit_name()

static const char* osmo_tdef_unit_name | ( | enum osmo_tdef_unit | val | ) |

inline static

Returns
  enum osmo_tdef_unit value as human readable unit letter, or "custom-unit".

References get_value_string(), and osmo_tdef_unit_names.

## osmo_tdef_val_in_range()

bool osmo_tdef_val_in_range | ( | struct osmo_tdef * | tdef,
|  | unsigned long | new_val
| ) |  |

Check if value new_val is in range of valid possible values for timer entry tdef.

Parameters

  [in] | tdef | Timer entry from a timer definition table.
  [in] | new_val | The value whose validity to check, in units as per this timer entry.

Returns
  true if inside range, false otherwise.

References osmo_tdef::max_val, and osmo_tdef::min_val.

Referenced by osmo_tdef_set(), and osmo_tdefs_reset().

## osmo_tdefs_reset()

void osmo_tdefs_reset | ( | struct osmo_tdef * | tdefs | ) |

Set all osmo_tdef values to the default_val.

It is convenient to define a tdefs array by setting only the default_val, and calling osmo_tdefs_reset() once for program startup. (See also osmo_tdef_vty_init()). During call to this function, default values are verified to be inside valid range; process is aborted otherwise.

Parameters

  [in] | tdefs | Array of timer definitions, last entry being fully zero.

References osmo_tdef::default_val, osmo_panic(), OSMO_T_FMT, OSMO_T_FMT_ARGS, osmo_tdef_for_each, osmo_tdef_range_str_buf(), osmo_tdef_val_in_range(), osmo_tdef::T, tdefs, and osmo_tdef::val.

## Variable Documentation

## osmo_tdef_unit_names [1/2]

const struct value_string osmo_tdef_unit_names[]

extern

Referenced by osmo_tdef_unit_name().

## osmo_tdef_unit_names [2/2]

const struct value_string osmo_tdef_unit_names[]

Initial value:
= {
{ OSMO_TDEF_S, "s" },
{ OSMO_TDEF_MS, "ms" },
{ OSMO_TDEF_M, "m" },
{ OSMO_TDEF_CUSTOM, "custom-unit" },
{ OSMO_TDEF_US, "us" },
{}
}
OSMO_TDEF_CUSTOM
@ OSMO_TDEF_CUSTOM
unspecified unit, explained in osmo_tdef.desc.
Definition: tdef.h:42
OSMO_TDEF_US
@ OSMO_TDEF_US
microseconds
Definition: tdef.h:43
OSMO_TDEF_M
@ OSMO_TDEF_M
minutes
Definition: tdef.h:41
OSMO_TDEF_S
@ OSMO_TDEF_S
most T are in seconds, keep 0 as default.
Definition: tdef.h:39
OSMO_TDEF_MS
@ OSMO_TDEF_MS
milliseconds
Definition: tdef.h:40

Referenced by osmo_tdef_unit_name().