Skip to content

Commit 8eb077e

Browse files
glenn-andrewskartben
authored andcommitted
Lib: SMF: Add return code to signal event propagation
See Discussion #83659 for information about the purpose of this change. Modifies run actions of hierarchical state machines to return a value indicating if the event was handled by the run action or should be propagated up to the parent run action. Flat state machines are not affected, and their run action returns void. smf_set_handled() has been removed and replaced by this return value. smf_set_state() will not propagate events regardless of the return value as the transition is considered to have occurred. Documentation, tests, samples, has been updated. USB-C and hawkBit use SMF and have been updated to use the new return codes. Signed-off-by: Glenn Andrews <[email protected]>
1 parent 2c4ec7d commit 8eb077e

22 files changed

+475
-379
lines changed

doc/releases/migration-guide-4.2.rst

+11
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,17 @@ ZBus
324324
which was previously allocated through :c:func:`k_malloc` internally. The structure must remain valid
325325
in memory until :c:func:`zbus_chan_rem_obs` is called.
326326

327+
State Machine Framework
328+
=======================
329+
330+
* :c:func:`smf_set_handled` has been removed.
331+
* State run actions now return an :c:enum:`smf_state_result` value instead of void. and the return
332+
code determines if the event is propagated to parent run actions or has been handled. A run action
333+
that handles the event completely should return :c:enum:`SMF_EVENT_HANDLED`, and run actions that
334+
propagate handling to parent states should return :c:enum:`SMF_EVENT_PROPAGATE`.
335+
* Flat state machines ignore the return value; returning :c:enum:`SMF_EVENT_HANDLED`
336+
would be the most technically accurate response.
337+
327338
Modules
328339
*******
329340

doc/services/smf/index.rst

+30-18
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ State Creation
1818

1919
A state is represented by three functions, where one function implements the
2020
Entry actions, another function implements the Run actions, and the last
21-
function implements the Exit actions. The prototype for these functions is as
22-
follows: ``void funct(void *obj)``, where the ``obj`` parameter is a user
23-
defined structure that has the state machine context, :c:struct:`smf_ctx`, as
24-
its first member. For example::
21+
function implements the Exit actions. The prototype for the entry and exit
22+
functions are as follows: ``void funct(void *obj)``, and the prototype for the
23+
run action is ``enum smf_state_result funct(void *obj)`` where the ``obj``
24+
parameter is a user defined structure that has the state machine context,
25+
:c:struct:`smf_ctx`, as its first member. For example::
2526

2627
struct user_object {
2728
struct smf_ctx ctx;
@@ -39,6 +40,16 @@ By default, a state can have no ancestor states, resulting in a flat state
3940
machine. But to enable the creation of a hierarchical state machine, the
4041
:kconfig:option:`CONFIG_SMF_ANCESTOR_SUPPORT` option must be enabled.
4142

43+
The return value of the run action, :c:enum:`smf_state_result` determines if the
44+
state machine propagates the event to parent run actions
45+
(:c:enum:`SMF_EVENT_PROPAGATE`) or if the event was handled by the run action
46+
(:c:enum:`SMF_EVENT_HANDLED`). Flat state machines do not have parent actions,
47+
so the return code is ignored; returning :c:enum:`SMF_EVENT_HANDLED` is
48+
recommended.
49+
50+
Calling :c:func:`smf_set_state` prevents calling parent run
51+
actions, even if :c:enum:`SMF_EVENT_PROPAGATE` is returned.
52+
4253
By default, the hierarchical state machines do not support initial transitions
4354
to child states on entering a superstate. To enable them the
4455
:kconfig:option:`CONFIG_SMF_INITIAL_TRANSITION` option must be enabled.
@@ -111,13 +122,6 @@ To run the state machine, the :c:func:`smf_run_state` function should be
111122
called in some application dependent way. An application should cease calling
112123
smf_run_state if it returns a non-zero value.
113124

114-
Preventing Parent Run Actions
115-
=============================
116-
117-
Calling :c:func:`smf_set_handled` prevents calling the run action of parent
118-
states. It is not required to call :c:func:`smf_set_handled` if the state
119-
calls :c:func:`smf_set_state`.
120-
121125
State Machine Termination
122126
=========================
123127

@@ -197,19 +201,21 @@ Code::
197201
{
198202
/* Do something */
199203
}
200-
static void s0_run(void *o)
204+
static enum smf_state_result s0_run(void *o)
201205
{
202206
smf_set_state(SMF_CTX(&s_obj), &demo_states[S1]);
207+
return SMF_EVENT_HANDLED;
203208
}
204209
static void s0_exit(void *o)
205210
{
206211
/* Do something */
207212
}
208213

209214
/* State S1 */
210-
static void s1_run(void *o)
215+
static enum smf_state_result s1_run(void *o)
211216
{
212217
smf_set_state(SMF_CTX(&s_obj), &demo_states[S2]);
218+
return SMF_EVENT_HANDLED;
213219
}
214220
static void s1_exit(void *o)
215221
{
@@ -221,9 +227,10 @@ Code::
221227
{
222228
/* Do something */
223229
}
224-
static void s2_run(void *o)
230+
static enum smf_state_result s2_run(void *o)
225231
{
226232
smf_set_state(SMF_CTX(&s_obj), &demo_states[S0]);
233+
return SMF_EVENT_HANDLED;
227234
}
228235

229236
/* Populate state table */
@@ -311,21 +318,24 @@ Code::
311318
}
312319

313320
/* State S0 */
314-
static void s0_run(void *o)
321+
static enum smf_state_result s0_run(void *o)
315322
{
316323
smf_set_state(SMF_CTX(&s_obj), &demo_states[S1]);
324+
return SMF_EVENT_HANDLED;
317325
}
318326

319327
/* State S1 */
320-
static void s1_run(void *o)
328+
static enum smf_state_result s1_run(void *o)
321329
{
322330
smf_set_state(SMF_CTX(&s_obj), &demo_states[S2]);
331+
return SMF_EVENT_HANDLED;
323332
}
324333

325334
/* State S2 */
326-
static void s2_run(void *o)
335+
static enum smf_state_result s2_run(void *o)
327336
{
328337
smf_set_state(SMF_CTX(&s_obj), &demo_states[S0]);
338+
return SMF_EVENT_HANDLED;
329339
}
330340

331341
/* Populate state table */
@@ -369,7 +379,7 @@ When designing hierarchical state machines, the following should be considered:
369379
state. For example, the s1_exit function is called before the parent_exit
370380
function is called.
371381
- The parent_run function only executes if the child_run function does not
372-
call either :c:func:`smf_set_state` or :c:func:`smf_set_handled`.
382+
call either :c:func:`smf_set_state` or return :c:enum:`SMF_EVENT_HANDLED`.
373383

374384
Event Driven State Machine Example
375385
**********************************
@@ -439,6 +449,7 @@ Code::
439449
if (s->events & EVENT_BTN_PRESS) {
440450
smf_set_state(SMF_CTX(&s_obj), &demo_states[S1]);
441451
}
452+
return SMF_EVENT_HANDLED;
442453
}
443454

444455
/* State S1 */
@@ -455,6 +466,7 @@ Code::
455466
if (s->events & EVENT_BTN_PRESS) {
456467
smf_set_state(SMF_CTX(&s_obj), &demo_states[S0]);
457468
}
469+
return SMF_EVENT_HANDLED;
458470
}
459471

460472
/* Populate state table */

include/zephyr/smf.h

+26-13
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
* @param _parent State parent object or NULL
3333
* @param _initial State initial transition object or NULL
3434
*/
35+
/* clang-format off */
3536
#define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \
3637
{ \
3738
.entry = _entry, \
@@ -40,6 +41,7 @@
4041
IF_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT, (.parent = _parent,)) \
4142
IF_ENABLED(CONFIG_SMF_INITIAL_TRANSITION, (.initial = _initial,)) \
4243
}
44+
/* clang-format on */
4345

4446
/**
4547
* @brief Macro to cast user defined object to state machine
@@ -56,23 +58,43 @@ extern "C" {
5658
#include <zephyr/kernel.h>
5759

5860
/**
59-
* @brief Function pointer that implements a portion of a state
61+
* @brief enum for the return value of a state_execution function
62+
*/
63+
enum smf_state_result {
64+
SMF_EVENT_HANDLED,
65+
SMF_EVENT_PROPAGATE,
66+
};
67+
68+
/**
69+
* @brief Function pointer that implements a entry and exit actions
70+
* of a state
71+
*
72+
* @param obj pointer user defined object
73+
*/
74+
typedef void (*state_method)(void *obj);
75+
76+
/**
77+
* @brief Function pointer that implements a the run action of a state
6078
*
6179
* @param obj pointer user defined object
80+
* @return If the event should be propagated to parent states or not
81+
* (Ignored when CONFIG_SMF_ANCESTOR_SUPPORT not defined)
6282
*/
63-
typedef void (*state_execution)(void *obj);
83+
typedef enum smf_state_result (*state_execution)(void *obj);
6484

6585
/** General state that can be used in multiple state machines. */
6686
struct smf_state {
6787
/** Optional method that will be run when this state is entered */
68-
const state_execution entry;
88+
const state_method entry;
89+
6990
/**
7091
* Optional method that will be run repeatedly during state machine
7192
* loop.
7293
*/
7394
const state_execution run;
95+
7496
/** Optional method that will be run when this state exists */
75-
const state_execution exit;
97+
const state_method exit;
7698
#ifdef CONFIG_SMF_ANCESTOR_SUPPORT
7799
/**
78100
* Optional parent state that contains common entry/run/exit
@@ -147,15 +169,6 @@ void smf_set_state(struct smf_ctx *ctx, const struct smf_state *new_state);
147169
*/
148170
void smf_set_terminate(struct smf_ctx *ctx, int32_t val);
149171

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-
159172
/**
160173
* @brief Runs one iteration of a state machine (including any parent states)
161174
*

lib/smf/smf.c

+16-11
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,11 @@ static bool smf_execute_ancestor_run_actions(struct smf_ctx *ctx)
158158
ctx->executing = tmp_state;
159159
/* Execute parent run action */
160160
if (tmp_state->run) {
161-
tmp_state->run(ctx);
161+
enum smf_state_result rc = tmp_state->run(ctx);
162+
163+
if (rc == SMF_EVENT_HANDLED) {
164+
internal->handled = true;
165+
}
162166
/* No need to continue if terminate was set */
163167
if (internal->terminate) {
164168
return true;
@@ -188,8 +192,7 @@ static bool smf_execute_all_exit_actions(struct smf_ctx *const ctx, const struct
188192
struct internal_ctx *const internal = (void *)&ctx->internal;
189193

190194
for (const struct smf_state *to_execute = ctx->current;
191-
to_execute != NULL && to_execute != topmost;
192-
to_execute = to_execute->parent) {
195+
to_execute != NULL && to_execute != topmost; to_execute = to_execute->parent) {
193196
if (to_execute->exit) {
194197
to_execute->exit(ctx);
195198

@@ -381,13 +384,6 @@ void smf_set_terminate(struct smf_ctx *ctx, int32_t val)
381384
ctx->terminate_val = val;
382385
}
383386

384-
void smf_set_handled(struct smf_ctx *ctx)
385-
{
386-
struct internal_ctx *const internal = (void *)&ctx->internal;
387-
388-
internal->handled = true;
389-
}
390-
391387
int32_t smf_run_state(struct smf_ctx *const ctx)
392388
{
393389
struct internal_ctx *const internal = (void *)&ctx->internal;
@@ -406,11 +402,20 @@ int32_t smf_run_state(struct smf_ctx *const ctx)
406402
ctx->executing = ctx->current;
407403
#endif
408404

405+
#ifndef CONFIG_SMF_ANCESTOR_SUPPORT
409406
if (ctx->current->run) {
410407
ctx->current->run(ctx);
411408
}
409+
#else
410+
ctx->executing = ctx->current;
411+
if (ctx->current->run) {
412+
enum smf_state_result rc = ctx->current->run(ctx);
413+
414+
if (rc == SMF_EVENT_HANDLED) {
415+
internal->handled = true;
416+
}
417+
}
412418

413-
#ifdef CONFIG_SMF_ANCESTOR_SUPPORT
414419
if (smf_execute_ancestor_run_actions(ctx)) {
415420
return ctx->terminate_val;
416421
}

0 commit comments

Comments
 (0)