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