Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 1 | #pragma once |
| 2 | |
| 3 | #include <stdint.h> |
| 4 | #include <stdbool.h> |
| 5 | |
| 6 | #include <osmocom/core/linuxlist.h> |
| 7 | #include <osmocom/core/timer.h> |
| 8 | #include <osmocom/core/utils.h> |
| 9 | |
| 10 | /*! \defgroup fsm Finite State Machine abstraction |
| 11 | * @{ |
| 12 | */ |
| 13 | |
| 14 | /*! \file fsm.h |
| 15 | * \brief Finite State Machine |
| 16 | */ |
| 17 | |
| 18 | struct osmo_fsm_inst; |
| 19 | |
| 20 | enum osmo_fsm_term_cause { |
| 21 | /*! \brief terminate because parent terminated */ |
| 22 | OSMO_FSM_TERM_PARENT, |
| 23 | /*! \brief terminate on explicit user request */ |
| 24 | OSMO_FSM_TERM_REQUEST, |
| 25 | /*! \brief regular termination of process */ |
| 26 | OSMO_FSM_TERM_REGULAR, |
| 27 | /*! \brief erroneous termination of process */ |
| 28 | OSMO_FSM_TERM_ERROR, |
Harald Welte | f627c0f | 2016-06-18 10:36:25 +0200 | [diff] [blame] | 29 | /*! \brief termination due to time-out */ |
| 30 | OSMO_FSM_TERM_TIMEOUT, |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 31 | }; |
| 32 | |
Neels Hofmeyr | 5c5c78a | 2016-12-14 18:35:47 +0100 | [diff] [blame] | 33 | extern const struct value_string osmo_fsm_term_cause_names[]; |
| 34 | static inline const char *osmo_fsm_term_cause_name(enum osmo_fsm_term_cause cause) |
| 35 | { |
| 36 | return get_value_string(osmo_fsm_term_cause_names, cause); |
| 37 | } |
| 38 | |
| 39 | |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 40 | /*! \brief description of a rule in the FSM */ |
| 41 | struct osmo_fsm_state { |
| 42 | /*! \brief bit-mask of permitted input events for this state */ |
| 43 | uint32_t in_event_mask; |
| 44 | /*! \brief bit-mask to which other states this state may transiton */ |
| 45 | uint32_t out_state_mask; |
| 46 | /*! \brief human-readable name of this state */ |
| 47 | const char *name; |
| 48 | /*! \brief function to be called for events arriving in this state */ |
| 49 | void (*action)(struct osmo_fsm_inst *fi, uint32_t event, void *data); |
| 50 | /*! \brief function to be called just after entering the state */ |
| 51 | void (*onenter)(struct osmo_fsm_inst *fi, uint32_t prev_state); |
| 52 | /*! \brief function to be called just before leaving the state */ |
| 53 | void (*onleave)(struct osmo_fsm_inst *fi, uint32_t next_state); |
| 54 | }; |
| 55 | |
| 56 | /*! \brief a description of an osmocom finite state machine */ |
| 57 | struct osmo_fsm { |
| 58 | /*! \brief global list */ |
| 59 | struct llist_head list; |
| 60 | /*! \brief list of instances of this FSM */ |
| 61 | struct llist_head instances; |
| 62 | /*! \brief human readable name */ |
| 63 | const char *name; |
| 64 | /*! \brief table of state transition rules */ |
| 65 | const struct osmo_fsm_state *states; |
| 66 | /*! \brief number of entries in \ref states */ |
| 67 | unsigned int num_states; |
| 68 | /*! \brief bit-mask of events permitted in all states */ |
| 69 | uint32_t allstate_event_mask; |
| 70 | /*! \brief function pointer to be called for allstate events */ |
| 71 | void (*allstate_action)(struct osmo_fsm_inst *fi, uint32_t event, void *data); |
Neels Hofmeyr | 42b59c1 | 2016-12-20 12:06:05 +0100 | [diff] [blame] | 72 | /*! \brief clean-up function, called during termination */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 73 | void (*cleanup)(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause); |
Neels Hofmeyr | dda5e79 | 2016-12-09 16:10:34 +0100 | [diff] [blame] | 74 | /*! \brief timer call-back for states with time-out. |
| 75 | * \returns 1 to request termination, 0 to keep running. */ |
Harald Welte | f627c0f | 2016-06-18 10:36:25 +0200 | [diff] [blame] | 76 | int (*timer_cb)(struct osmo_fsm_inst *fi); |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 77 | /*! \brief logging sub-system for this FSM */ |
| 78 | int log_subsys; |
| 79 | /*! \brief human-readable names of events */ |
| 80 | const struct value_string *event_names; |
| 81 | }; |
| 82 | |
| 83 | /*! \brief a single instanceof an osmocom finite state machine */ |
| 84 | struct osmo_fsm_inst { |
| 85 | /*! \brief member in the fsm->instances list */ |
| 86 | struct llist_head list; |
| 87 | /*! \brief back-pointer to the FSM of which we are an instance */ |
| 88 | struct osmo_fsm *fsm; |
| 89 | /*! \brief human readable identifier */ |
| 90 | const char *id; |
| 91 | /*! \brief human readable fully-qualified name */ |
| 92 | const char *name; |
| 93 | /*! \brief some private data of this instance */ |
| 94 | void *priv; |
| 95 | /*! \brief logging level for this FSM */ |
| 96 | int log_level; |
| 97 | /*! \brief current state of the FSM */ |
| 98 | uint32_t state; |
| 99 | |
| 100 | /*! \brief timer number for states with time-out */ |
| 101 | int T; |
| 102 | /*! \brief timer back-end for states with time-out */ |
| 103 | struct osmo_timer_list timer; |
| 104 | |
| 105 | /*! \brief support for fsm-based procedures */ |
| 106 | struct { |
| 107 | /*! \brief the parent FSM that has created us */ |
| 108 | struct osmo_fsm_inst *parent; |
| 109 | /*! \brief the event we should send upon termination */ |
| 110 | uint32_t parent_term_event; |
| 111 | /*! \brief a list of children processes */ |
| 112 | struct llist_head children; |
| 113 | /*! \brief \ref llist_head linked to parent->proc.children */ |
| 114 | struct llist_head child; |
| 115 | } proc; |
| 116 | }; |
| 117 | |
| 118 | void osmo_fsm_log_addr(bool log_addr); |
| 119 | |
Neels Hofmeyr | 6a13e7f | 2016-12-14 17:37:34 +0100 | [diff] [blame] | 120 | #define LOGPFSML(fi, level, fmt, args...) \ |
| 121 | LOGP((fi)->fsm->log_subsys, level, "%s{%s}: " fmt, \ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 122 | osmo_fsm_inst_name(fi), \ |
| 123 | osmo_fsm_state_name((fi)->fsm, (fi)->state), ## args) |
| 124 | |
Neels Hofmeyr | 6a13e7f | 2016-12-14 17:37:34 +0100 | [diff] [blame] | 125 | #define LOGPFSM(fi, fmt, args...) \ |
| 126 | LOGPFSML(fi, (fi)->log_level, fmt, ## args) |
| 127 | |
Neels Hofmeyr | eeacf90 | 2016-12-23 01:00:13 +0100 | [diff] [blame] | 128 | #define LOGPFSMLSRC(fi, level, caller_file, caller_line, fmt, args...) \ |
| 129 | LOGPSRC((fi)->fsm->log_subsys, level, \ |
| 130 | caller_file, caller_line, \ |
| 131 | "%s{%s}: " fmt, \ |
| 132 | osmo_fsm_inst_name(fi), \ |
| 133 | osmo_fsm_state_name((fi)->fsm, (fi)->state), \ |
| 134 | ## args) |
| 135 | |
| 136 | #define LOGPFSMSRC(fi, caller_file, caller_line, fmt, args...) \ |
| 137 | LOGPFSMLSRC(fi, (fi)->log_level, \ |
| 138 | caller_file, caller_line, \ |
| 139 | fmt, ## args) |
| 140 | |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 141 | int osmo_fsm_register(struct osmo_fsm *fsm); |
Max | 8b25a3f | 2016-11-01 11:02:17 +0100 | [diff] [blame] | 142 | void osmo_fsm_unregister(struct osmo_fsm *fsm); |
Harald Welte | 8808bb4 | 2017-01-07 11:11:03 +0100 | [diff] [blame] | 143 | struct osmo_fsm *osmo_fsm_find_by_name(const char *name); |
Harald Welte | 4585e67 | 2017-04-16 17:23:56 +0200 | [diff] [blame] | 144 | struct osmo_fsm_inst *osmo_fsm_inst_find_by_name(const struct osmo_fsm *fsm, |
| 145 | const char *name); |
| 146 | struct osmo_fsm_inst *osmo_fsm_inst_find_by_id(const struct osmo_fsm *fsm, |
| 147 | const char *id); |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 148 | struct osmo_fsm_inst *osmo_fsm_inst_alloc(struct osmo_fsm *fsm, void *ctx, void *priv, |
| 149 | int log_level, const char *id); |
| 150 | struct osmo_fsm_inst *osmo_fsm_inst_alloc_child(struct osmo_fsm *fsm, |
| 151 | struct osmo_fsm_inst *parent, |
| 152 | uint32_t parent_term_event); |
| 153 | void osmo_fsm_inst_free(struct osmo_fsm_inst *fi); |
| 154 | |
| 155 | const char *osmo_fsm_event_name(struct osmo_fsm *fsm, uint32_t event); |
| 156 | const char *osmo_fsm_inst_name(struct osmo_fsm_inst *fi); |
| 157 | const char *osmo_fsm_state_name(struct osmo_fsm *fsm, uint32_t state); |
| 158 | |
Neels Hofmeyr | fca04bb | 2017-02-28 03:38:38 +0100 | [diff] [blame] | 159 | /*! \brief return the name of the state the FSM instance is currently in. */ |
| 160 | static inline const char *osmo_fsm_inst_state_name(struct osmo_fsm_inst *fi) |
| 161 | { return osmo_fsm_state_name(fi->fsm, fi->state); } |
| 162 | |
Neels Hofmeyr | 725698a | 2016-12-14 17:24:54 +0100 | [diff] [blame] | 163 | /*! \brief perform a state change of the given FSM instance |
| 164 | * |
| 165 | * This is a macro that calls _osmo_fsm_inst_state_chg() with the given |
| 166 | * parameters as well as the caller's source file and line number for logging |
| 167 | * purposes. See there for documentation. |
| 168 | */ |
| 169 | #define osmo_fsm_inst_state_chg(fi, new_state, timeout_secs, T) \ |
| 170 | _osmo_fsm_inst_state_chg(fi, new_state, timeout_secs, T, \ |
| 171 | __BASE_FILE__, __LINE__) |
| 172 | int _osmo_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t new_state, |
| 173 | unsigned long timeout_secs, int T, |
| 174 | const char *file, int line); |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 175 | |
Neels Hofmeyr | 725698a | 2016-12-14 17:24:54 +0100 | [diff] [blame] | 176 | /*! \brief dispatch an event to an osmocom finite state machine instance |
| 177 | * |
| 178 | * This is a macro that calls _osmo_fsm_inst_dispatch() with the given |
| 179 | * parameters as well as the caller's source file and line number for logging |
| 180 | * purposes. See there for documentation. |
| 181 | */ |
| 182 | #define osmo_fsm_inst_dispatch(fi, event, data) \ |
| 183 | _osmo_fsm_inst_dispatch(fi, event, data, __BASE_FILE__, __LINE__) |
| 184 | int _osmo_fsm_inst_dispatch(struct osmo_fsm_inst *fi, uint32_t event, void *data, |
| 185 | const char *file, int line); |
| 186 | |
| 187 | /*! \brief Terminate FSM instance with given cause |
| 188 | * |
| 189 | * This is a macro that calls _osmo_fsm_inst_term() with the given parameters |
| 190 | * as well as the caller's source file and line number for logging purposes. |
| 191 | * See there for documentation. |
| 192 | */ |
| 193 | #define osmo_fsm_inst_term(fi, cause, data) \ |
| 194 | _osmo_fsm_inst_term(fi, cause, data, __BASE_FILE__, __LINE__) |
| 195 | void _osmo_fsm_inst_term(struct osmo_fsm_inst *fi, |
| 196 | enum osmo_fsm_term_cause cause, void *data, |
| 197 | const char *file, int line); |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 198 | |
Neels Hofmeyr | c014f60 | 2016-12-23 04:26:39 +0100 | [diff] [blame] | 199 | /*! \brief Terminate all child FSM instances of an FSM instance. |
| 200 | * |
| 201 | * This is a macro that calls _osmo_fsm_inst_term_children() with the given |
| 202 | * parameters as well as the caller's source file and line number for logging |
| 203 | * purposes. See there for documentation. |
| 204 | */ |
| 205 | #define osmo_fsm_inst_term_children(fi, cause, data) \ |
| 206 | _osmo_fsm_inst_term_children(fi, cause, data, __BASE_FILE__, __LINE__) |
| 207 | void _osmo_fsm_inst_term_children(struct osmo_fsm_inst *fi, |
| 208 | enum osmo_fsm_term_cause cause, |
| 209 | void *data, |
| 210 | const char *file, int line); |
| 211 | |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 212 | /*! @} */ |