1 /*
2 * Copyright 2021 The Chromium OS Authors
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/smf.h>
8
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_REGISTER(smf);
11
12 /*
13 * Private structure (to this file) used to track state machine context.
14 * The structure is not used directly, but instead to cast the "internal"
15 * member of the smf_ctx structure.
16 */
17 struct internal_ctx {
18 bool new_state : 1;
19 bool terminate : 1;
20 bool exit : 1;
21 };
22
share_paren(const struct smf_state * test_state,const struct smf_state * target_state)23 static bool share_paren(const struct smf_state *test_state,
24 const struct smf_state *target_state)
25 {
26 for (const struct smf_state *state = test_state;
27 state != NULL;
28 state = state->parent) {
29 if (target_state == state) {
30 return true;
31 }
32 }
33
34 return false;
35 }
36
last_state_share_paren(struct smf_ctx * const ctx,const struct smf_state * state)37 static bool last_state_share_paren(struct smf_ctx *const ctx,
38 const struct smf_state *state)
39 {
40 /* Get parent state of previous state */
41 if (!ctx->previous) {
42 return false;
43 }
44
45 return share_paren(ctx->previous->parent, state);
46 }
47
get_child_of(const struct smf_state * states,const struct smf_state * parent)48 static const struct smf_state *get_child_of(const struct smf_state *states,
49 const struct smf_state *parent)
50 {
51 for (const struct smf_state *tmp = states; ; tmp = tmp->parent) {
52 if (tmp->parent == parent) {
53 return tmp;
54 }
55
56 if (tmp->parent == NULL) {
57 return NULL;
58 }
59 }
60
61 return NULL;
62 }
63
get_last_of(const struct smf_state * states)64 static const struct smf_state *get_last_of(const struct smf_state *states)
65 {
66 return get_child_of(states, NULL);
67 }
68
69 /**
70 * @brief Execute all ancestor entry actions
71 *
72 * @param ctx State machine context
73 * @param target The entry actions of this target's ancestors are executed
74 * @return true if the state machine should terminate, else false
75 */
smf_execute_ancestor_entry_actions(struct smf_ctx * const ctx,const struct smf_state * target)76 __unused static bool smf_execute_ancestor_entry_actions(
77 struct smf_ctx *const ctx, const struct smf_state *target)
78 {
79 struct internal_ctx * const internal = (void *) &ctx->internal;
80
81 for (const struct smf_state *to_execute = get_last_of(target);
82 to_execute != NULL && to_execute != target;
83 to_execute = get_child_of(target, to_execute)) {
84 /* Execute parent state's entry */
85 if (!last_state_share_paren(ctx, to_execute) && to_execute->entry) {
86 to_execute->entry(ctx);
87
88 /* No need to continue if terminate was set */
89 if (internal->terminate) {
90 return true;
91 }
92 }
93 }
94
95 return false;
96 }
97
98 /**
99 * @brief Execute all ancestor run actions
100 *
101 * @param ctx State machine context
102 * @param target The run actions of this target's ancestors are executed
103 * @return true if the state machine should terminate, else false
104 */
smf_execute_ancestor_run_actions(struct smf_ctx * ctx)105 __unused static bool smf_execute_ancestor_run_actions(struct smf_ctx *ctx)
106 {
107 struct internal_ctx * const internal = (void *) &ctx->internal;
108 /* Execute all run actions in reverse order */
109
110 /* Return if the current state switched states */
111 if (internal->new_state) {
112 internal->new_state = false;
113 return false;
114 }
115
116 /* Return if the current state terminated */
117 if (internal->terminate) {
118 return true;
119 }
120
121 /* Try to run parent run actions */
122 for (const struct smf_state *tmp_state = ctx->current->parent;
123 tmp_state != NULL;
124 tmp_state = tmp_state->parent) {
125 /* Execute parent run action */
126 if (tmp_state->run) {
127 tmp_state->run(ctx);
128 /* No need to continue if terminate was set */
129 if (internal->terminate) {
130 return true;
131 }
132
133 if (internal->new_state) {
134 break;
135 }
136 }
137 }
138
139 internal->new_state = false;
140 /* All done executing the run actions */
141
142 return false;
143 }
144
145 /**
146 * @brief Execute all ancestor exit actions
147 *
148 * @param ctx State machine context
149 * @param target The exit actions of this target's ancestors are executed
150 * @return true if the state machine should terminate, else false
151 */
smf_execute_ancestor_exit_actions(struct smf_ctx * const ctx,const struct smf_state * target)152 __unused static bool smf_execute_ancestor_exit_actions(
153 struct smf_ctx *const ctx, const struct smf_state *target)
154 {
155 struct internal_ctx * const internal = (void *) &ctx->internal;
156
157 /* Execute all parent exit actions in reverse order */
158
159 for (const struct smf_state *tmp_state = ctx->current->parent;
160 tmp_state != NULL;
161 tmp_state = tmp_state->parent) {
162 if (!share_paren(target->parent, tmp_state) && tmp_state->exit) {
163 tmp_state->exit(ctx);
164
165 /* No need to continue if terminate was set */
166 if (internal->terminate) {
167 return true;
168 }
169 }
170 }
171 return false;
172 }
173
smf_set_initial(struct smf_ctx * ctx,const struct smf_state * init_state)174 void smf_set_initial(struct smf_ctx *ctx, const struct smf_state *init_state)
175 {
176 struct internal_ctx * const internal = (void *) &ctx->internal;
177
178 internal->exit = false;
179 internal->terminate = false;
180 ctx->current = init_state;
181 ctx->previous = NULL;
182 ctx->terminate_val = 0;
183
184 if (IS_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT)) {
185 internal->new_state = false;
186
187 if (smf_execute_ancestor_entry_actions(ctx, init_state)) {
188 return;
189 }
190 }
191
192 /* Now execute the initial state's entry action */
193 if (init_state->entry) {
194 init_state->entry(ctx);
195 }
196 }
197
smf_set_state(struct smf_ctx * const ctx,const struct smf_state * target)198 void smf_set_state(struct smf_ctx *const ctx, const struct smf_state *target)
199 {
200 struct internal_ctx * const internal = (void *) &ctx->internal;
201
202 /*
203 * It does not make sense to call set_state in an exit phase of a state
204 * since we are already in a transition; we would always ignore the
205 * intended state to transition into.
206 */
207 if (internal->exit) {
208 LOG_WRN("Calling %s from exit action", __func__);
209 return;
210 }
211
212 internal->exit = true;
213
214 /* Execute the current states exit action */
215 if (ctx->current->exit) {
216 ctx->current->exit(ctx);
217
218 /*
219 * No need to continue if terminate was set in the
220 * exit action
221 */
222 if (internal->terminate) {
223 return;
224 }
225 }
226
227 if (IS_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT)) {
228 internal->new_state = true;
229
230 if (smf_execute_ancestor_exit_actions(ctx, target)) {
231 return;
232 }
233 }
234
235 internal->exit = false;
236
237 /* update the state variables */
238 ctx->previous = ctx->current;
239 ctx->current = target;
240
241 if (IS_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT)) {
242 if (smf_execute_ancestor_entry_actions(ctx, target)) {
243 return;
244 }
245 }
246
247 /* Now execute the target entry action */
248 if (ctx->current->entry) {
249 ctx->current->entry(ctx);
250 /*
251 * If terminate was set, it will be handled in the
252 * smf_run_state function
253 */
254 }
255 }
256
smf_set_terminate(struct smf_ctx * ctx,int32_t val)257 void smf_set_terminate(struct smf_ctx *ctx, int32_t val)
258 {
259 struct internal_ctx * const internal = (void *) &ctx->internal;
260
261 internal->terminate = true;
262 ctx->terminate_val = val;
263 }
264
smf_run_state(struct smf_ctx * const ctx)265 int32_t smf_run_state(struct smf_ctx *const ctx)
266 {
267 struct internal_ctx * const internal = (void *) &ctx->internal;
268
269 /* No need to continue if terminate was set */
270 if (internal->terminate) {
271 return ctx->terminate_val;
272 }
273
274 if (ctx->current->run) {
275 ctx->current->run(ctx);
276 }
277
278 if (IS_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT)) {
279 if (smf_execute_ancestor_run_actions(ctx)) {
280 return ctx->terminate_val;
281 }
282 }
283
284 return 0;
285 }
286