1 /*
2  * Copyright 2021 The Chromium OS Authors
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  *
10  * @brief State Machine Framework header file
11  */
12 
13 #ifndef ZEPHYR_INCLUDE_SMF_H_
14 #define ZEPHYR_INCLUDE_SMF_H_
15 
16 #include <zephyr/sys/util.h>
17 
18 /**
19  * @brief State Machine Framework API
20  * @defgroup smf State Machine Framework API
21  * @version 0.1.0
22  * @ingroup os_services
23  * @{
24  */
25 
26 /**
27  * @brief Macro to create a hierarchical state with initial transitions.
28  *
29  * @param _entry   State entry function or NULL
30  * @param _run     State run function or NULL
31  * @param _exit    State exit function or NULL
32  * @param _parent  State parent object or NULL
33  * @param _initial State initial transition object or NULL
34  */
35 #define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial)           \
36 {                                                                          \
37 	.entry   = _entry,                                                 \
38 	.run     = _run,                                                   \
39 	.exit    = _exit,                                                  \
40 	IF_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT, (.parent = _parent,))      \
41 	IF_ENABLED(CONFIG_SMF_INITIAL_TRANSITION, (.initial = _initial,))  \
42 }
43 
44 /**
45  * @brief Macro to cast user defined object to state machine
46  *        context.
47  *
48  * @param o A pointer to the user defined object
49  */
50 #define SMF_CTX(o) ((struct smf_ctx *)o)
51 
52 #ifdef __cplusplus
53 extern "C" {
54 #endif
55 
56 #include <zephyr/kernel.h>
57 
58 /**
59  * @brief Function pointer that implements a portion of a state
60  *
61  * @param obj pointer user defined object
62  */
63 typedef void (*state_execution)(void *obj);
64 
65 /** General state that can be used in multiple state machines. */
66 struct smf_state {
67 	/** Optional method that will be run when this state is entered */
68 	const state_execution entry;
69 	/**
70 	 * Optional method that will be run repeatedly during state machine
71 	 * loop.
72 	 */
73 	const state_execution run;
74 	/** Optional method that will be run when this state exists */
75 	const state_execution exit;
76 #ifdef CONFIG_SMF_ANCESTOR_SUPPORT
77 	/**
78 	 * Optional parent state that contains common entry/run/exit
79 	 *	implementation among various child states.
80 	 *	entry: Parent function executes BEFORE child function.
81 	 *	run:   Parent function executes AFTER child function.
82 	 *	exit:  Parent function executes AFTER child function.
83 	 *
84 	 *	Note: When transitioning between two child states with a shared
85 	 *      parent,	that parent's exit and entry functions do not execute.
86 	 */
87 	const struct smf_state *parent;
88 
89 #ifdef CONFIG_SMF_INITIAL_TRANSITION
90 	/**
91 	 * Optional initial transition state. NULL for leaf states.
92 	 */
93 	const struct smf_state *initial;
94 #endif /* CONFIG_SMF_INITIAL_TRANSITION */
95 #endif /* CONFIG_SMF_ANCESTOR_SUPPORT */
96 };
97 
98 /** Defines the current context of the state machine. */
99 struct smf_ctx {
100 	/** Current state the state machine is executing. */
101 	const struct smf_state *current;
102 	/** Previous state the state machine executed */
103 	const struct smf_state *previous;
104 
105 #ifdef CONFIG_SMF_ANCESTOR_SUPPORT
106 	/** Currently executing state (which may be a parent) */
107 	const struct smf_state *executing;
108 #endif /* CONFIG_SMF_ANCESTOR_SUPPORT */
109 	/**
110 	 * This value is set by the set_terminate function and
111 	 * should terminate the state machine when its set to a
112 	 * value other than zero when it's returned by the
113 	 * run_state function.
114 	 */
115 	int32_t terminate_val;
116 	/**
117 	 * The state machine casts this to a "struct internal_ctx" and it's
118 	 * used to track state machine context
119 	 */
120 	uint32_t internal;
121 };
122 
123 /**
124  * @brief Initializes the state machine and sets its initial state.
125  *
126  * @param ctx        State machine context
127  * @param init_state Initial state the state machine starts in.
128  */
129 void smf_set_initial(struct smf_ctx *ctx, const struct smf_state *init_state);
130 
131 /**
132  * @brief Changes a state machines state. This handles exiting the previous
133  *        state and entering the target state. For HSMs the entry and exit
134  *        actions of the Least Common Ancestor will not be run.
135  *
136  * @param ctx       State machine context
137  * @param new_state State to transition to (NULL is valid and exits all states)
138  */
139 void smf_set_state(struct smf_ctx *ctx, const struct smf_state *new_state);
140 
141 /**
142  * @brief Terminate a state machine
143  *
144  * @param ctx  State machine context
145  * @param val  Non-Zero termination value that's returned by the smf_run_state
146  *             function.
147  */
148 void smf_set_terminate(struct smf_ctx *ctx, int32_t val);
149 
150 /**
151  * @brief Tell the SMF to stop propagating the event to ancestors. This allows
152  *        HSMs to implement 'programming by difference' where substates can
153  *        handle events on their own or propagate up to a common handler.
154  *
155  * @param ctx  State machine context
156  */
157 void smf_set_handled(struct smf_ctx *ctx);
158 
159 /**
160  * @brief Runs one iteration of a state machine (including any parent states)
161  *
162  * @param ctx  State machine context
163  * @return	   A non-zero value should terminate the state machine. This
164  *			   non-zero value could represent a terminal state being reached
165  *			   or the detection of an error that should result in the
166  *			   termination of the state machine.
167  */
168 int32_t smf_run_state(struct smf_ctx *ctx);
169 
170 #ifdef __cplusplus
171 }
172 #endif
173 
174 /**
175  * @}
176  */
177 
178 #endif /* ZEPHYR_INCLUDE_SMF_H_ */
179