From 136e73764e7f58e52ffb13d01304fef30eb7d291 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 29 May 2016 10:53:17 +0900 Subject: Add Finite State Machine abstraction code This code is supposed to formalize some of the state machine handling in Osmocom code. Change-Id: I0b0965a912598c1f6b84042a99fea9d522642466 Reviewed-on: https://gerrit.osmocom.org/163 Tested-by: Jenkins Builder Reviewed-by: Harald Welte --- include/osmocom/core/fsm.h | 135 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 include/osmocom/core/fsm.h (limited to 'include/osmocom') diff --git a/include/osmocom/core/fsm.h b/include/osmocom/core/fsm.h new file mode 100644 index 00000000..401ee04a --- /dev/null +++ b/include/osmocom/core/fsm.h @@ -0,0 +1,135 @@ +#pragma once + +#include +#include + +#include +#include +#include + +/*! \defgroup fsm Finite State Machine abstraction + * @{ + */ + +/*! \file fsm.h + * \brief Finite State Machine + */ + +struct osmo_fsm_inst; + +enum osmo_fsm_term_cause { + /*! \brief terminate because parent terminated */ + OSMO_FSM_TERM_PARENT, + /*! \brief terminate on explicit user request */ + OSMO_FSM_TERM_REQUEST, + /*! \brief regular termination of process */ + OSMO_FSM_TERM_REGULAR, + /*! \brief erroneous termination of process */ + OSMO_FSM_TERM_ERROR, +}; + +/*! \brief description of a rule in the FSM */ +struct osmo_fsm_state { + /*! \brief bit-mask of permitted input events for this state */ + uint32_t in_event_mask; + /*! \brief bit-mask to which other states this state may transiton */ + uint32_t out_state_mask; + /*! \brief human-readable name of this state */ + const char *name; + /*! \brief function to be called for events arriving in this state */ + void (*action)(struct osmo_fsm_inst *fi, uint32_t event, void *data); + /*! \brief function to be called just after entering the state */ + void (*onenter)(struct osmo_fsm_inst *fi, uint32_t prev_state); + /*! \brief function to be called just before leaving the state */ + void (*onleave)(struct osmo_fsm_inst *fi, uint32_t next_state); +}; + +/*! \brief a description of an osmocom finite state machine */ +struct osmo_fsm { + /*! \brief global list */ + struct llist_head list; + /*! \brief list of instances of this FSM */ + struct llist_head instances; + /*! \brief human readable name */ + const char *name; + /*! \brief table of state transition rules */ + const struct osmo_fsm_state *states; + /*! \brief number of entries in \ref states */ + unsigned int num_states; + /*! \brief bit-mask of events permitted in all states */ + uint32_t allstate_event_mask; + /*! \brief function pointer to be called for allstate events */ + void (*allstate_action)(struct osmo_fsm_inst *fi, uint32_t event, void *data); + /*! \breif clean-up function, called during termination */ + void (*cleanup)(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause); + /*! \brief timer call-back for states with time-out */ + void (*timer_cb)(struct osmo_fsm_inst *fi); + /*! \brief logging sub-system for this FSM */ + int log_subsys; + /*! \brief human-readable names of events */ + const struct value_string *event_names; +}; + +/*! \brief a single instanceof an osmocom finite state machine */ +struct osmo_fsm_inst { + /*! \brief member in the fsm->instances list */ + struct llist_head list; + /*! \brief back-pointer to the FSM of which we are an instance */ + struct osmo_fsm *fsm; + /*! \brief human readable identifier */ + const char *id; + /*! \brief human readable fully-qualified name */ + const char *name; + /*! \brief some private data of this instance */ + void *priv; + /*! \brief logging level for this FSM */ + int log_level; + /*! \brief current state of the FSM */ + uint32_t state; + + /*! \brief timer number for states with time-out */ + int T; + /*! \brief timer back-end for states with time-out */ + struct osmo_timer_list timer; + + /*! \brief support for fsm-based procedures */ + struct { + /*! \brief the parent FSM that has created us */ + struct osmo_fsm_inst *parent; + /*! \brief the event we should send upon termination */ + uint32_t parent_term_event; + /*! \brief a list of children processes */ + struct llist_head children; + /*! \brief \ref llist_head linked to parent->proc.children */ + struct llist_head child; + } proc; +}; + +void osmo_fsm_log_addr(bool log_addr); + +#define LOGPFSM(fi, fmt, args...) \ + LOGP((fi)->fsm->log_subsys, (fi)->log_level, "%s{%s}: " fmt, \ + osmo_fsm_inst_name(fi), \ + osmo_fsm_state_name((fi)->fsm, (fi)->state), ## args) + +int osmo_fsm_register(struct osmo_fsm *fsm); + +struct osmo_fsm_inst *osmo_fsm_inst_alloc(struct osmo_fsm *fsm, void *ctx, void *priv, + int log_level, const char *id); +struct osmo_fsm_inst *osmo_fsm_inst_alloc_child(struct osmo_fsm *fsm, + struct osmo_fsm_inst *parent, + uint32_t parent_term_event); +void osmo_fsm_inst_free(struct osmo_fsm_inst *fi); + +const char *osmo_fsm_event_name(struct osmo_fsm *fsm, uint32_t event); +const char *osmo_fsm_inst_name(struct osmo_fsm_inst *fi); +const char *osmo_fsm_state_name(struct osmo_fsm *fsm, uint32_t state); + +int osmo_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t new_state, + unsigned long timeout_secs, int T); +int osmo_fsm_inst_dispatch(struct osmo_fsm_inst *fi, uint32_t event, void *data); + +void osmo_fsm_inst_term(struct osmo_fsm_inst *fi, + enum osmo_fsm_term_cause cause, void *data); + +/*! @} */ -- cgit v1.2.3