Skip to content

Commit

Permalink
Lib: SMF: Add return code to signal event propagation
Browse files Browse the repository at this point in the history
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() wil not propagate
events regardless of the return value as the transition
is considered to have occurred.

Documentation, tests and samples have been updated,
as has the hawkBit and USB-C subsystems.

Signed-off-by: Glenn Andrews <glenn.andrews.42@gmail.com>
  • Loading branch information
glenn-andrews committed Jan 11, 2025
1 parent 99a63a7 commit 0a6b01b
Show file tree
Hide file tree
Showing 16 changed files with 479 additions and 453 deletions.
17 changes: 14 additions & 3 deletions doc/services/smf/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ By default, a state can have no ancestor states, resulting in a flat state
machine. But to enable the creation of a hierarchical state machine, the
:kconfig:option:`CONFIG_SMF_ANCESTOR_SUPPORT` option must be enabled.

In a hierarchical state machine, the run action returns
:c:enum:`smf_state_result` instead of void, with the return value determining
if a state handles the received event, or propagates it to the run action of
any parent state.

By default, the hierarchical state machines do not support initial transitions
to child states on entering a superstate. To enable them the
:kconfig:option:`CONFIG_SMF_INITIAL_TRANSITION` option must be enabled.
Expand Down Expand Up @@ -114,9 +119,15 @@ smf_run_state if it returns a non-zero value.
Preventing Parent Run Actions
=============================

Calling :c:func:`smf_set_handled` prevents calling the run action of parent
states. It is not required to call :c:func:`smf_set_handled` if the state
calls :c:func:`smf_set_state`.
Propagation of events to parent actions of an HSM is determined by the return
value from the state's run action. :c:enum:`SMF_EVENT_HANDLED` prevents
calling the run action of parent states while :c:enum:`SMF_EVENT_PROPAGATE`
will allow the run actions of the parent states to be called. Calling
:c:func:`smf_set_state` prevents calling parent run actions, even if
:c:enum:`SMF_EVENT_PROPAGATE` is returned.

If :kconfig:option:`CONFIG_SMF_ANCESTOR_SUPPORT` is not enabled, the return
value from run actions is void as there are no parents to call.

State Machine Termination
=========================
Expand Down
58 changes: 38 additions & 20 deletions include/zephyr/smf.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,12 @@
* @param _parent State parent object or NULL
* @param _initial State initial transition object or NULL
*/
#define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \
{ \
.entry = _entry, \
.run = _run, \
.exit = _exit, \
IF_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT, (.parent = _parent,)) \
IF_ENABLED(CONFIG_SMF_INITIAL_TRANSITION, (.initial = _initial,)) \
}
#define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \
{.entry = _entry, \
.run = _run, \
.exit = _exit, \
IF_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT, (.parent = _parent,)) \
IF_ENABLED(CONFIG_SMF_INITIAL_TRANSITION, (.initial = _initial,)) }

/**
* @brief Macro to cast user defined object to state machine
Expand All @@ -55,24 +53,53 @@ extern "C" {

#include <zephyr/kernel.h>

#ifdef CONFIG_SMF_ANCESTOR_SUPPORT
/**
* @brief enum for the return value of a state_execution function
*/
enum smf_state_result {
SMF_EVENT_HANDLED,
SMF_EVENT_PROPAGATE,
};
#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */

/**
* @brief Function pointer that implements a portion of a state
*
* @param obj pointer user defined object
*/
typedef void (*state_method)(void *obj);

#ifdef CONFIG_SMF_ANCESTOR_SUPPORT
/**
* @brief Function pointer that implements a portion of a state
*
* @param obj pointer user defined object
* @return If the event should be propagated to parent states or not
*/
typedef void (*state_execution)(void *obj);
typedef enum smf_state_result (*state_execution)(void *obj);
#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */

/** General state that can be used in multiple state machines. */
struct smf_state {
/** Optional method that will be run when this state is entered */
const state_execution entry;
const state_method entry;
#ifndef CONFIG_SMF_ANCESTOR_SUPPORT
/**
* Optional method that will be run repeatedly during state machine
* loop.
*/
const state_method run;
#else
/**
* Optional method that will be run repeatedly during state machine
* loop.
*/
const state_execution run;
#endif /* CONFIG_SMF_ANCESTOR_SUPPORT */

/** Optional method that will be run when this state exists */
const state_execution exit;
const state_method exit;
#ifdef CONFIG_SMF_ANCESTOR_SUPPORT
/**
* Optional parent state that contains common entry/run/exit
Expand Down Expand Up @@ -147,15 +174,6 @@ void smf_set_state(struct smf_ctx *ctx, const struct smf_state *new_state);
*/
void smf_set_terminate(struct smf_ctx *ctx, int32_t val);

/**
* @brief Tell the SMF to stop propagating the event to ancestors. This allows
* HSMs to implement 'programming by difference' where substates can
* handle events on their own or propagate up to a common handler.
*
* @param ctx State machine context
*/
void smf_set_handled(struct smf_ctx *ctx);

/**
* @brief Runs one iteration of a state machine (including any parent states)
*
Expand Down
31 changes: 16 additions & 15 deletions lib/smf/smf.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,11 @@ static bool smf_execute_ancestor_run_actions(struct smf_ctx *ctx)
ctx->executing = tmp_state;
/* Execute parent run action */
if (tmp_state->run) {
tmp_state->run(ctx);
enum smf_state_result rc = tmp_state->run(ctx);

if (rc == SMF_EVENT_HANDLED) {
internal->handled = true;
}
/* No need to continue if terminate was set */
if (internal->terminate) {
return true;
Expand Down Expand Up @@ -193,8 +197,7 @@ static bool smf_execute_all_exit_actions(struct smf_ctx *const ctx, const struct
struct internal_ctx *const internal = (void *)&ctx->internal;

for (const struct smf_state *to_execute = ctx->current;
to_execute != NULL && to_execute != topmost;
to_execute = to_execute->parent) {
to_execute != NULL && to_execute != topmost; to_execute = to_execute->parent) {
if (to_execute->exit) {
to_execute->exit(ctx);

Expand Down Expand Up @@ -373,13 +376,6 @@ void smf_set_terminate(struct smf_ctx *ctx, int32_t val)
ctx->terminate_val = val;
}

void smf_set_handled(struct smf_ctx *ctx)
{
struct internal_ctx *const internal = (void *)&ctx->internal;

internal->handled = true;
}

int32_t smf_run_state(struct smf_ctx *const ctx)
{
struct internal_ctx *const internal = (void *)&ctx->internal;
Expand All @@ -389,15 +385,20 @@ int32_t smf_run_state(struct smf_ctx *const ctx)
return ctx->terminate_val;
}

#ifdef CONFIG_SMF_ANCESTOR_SUPPORT
ctx->executing = ctx->current;
#endif

#ifndef CONFIG_SMF_ANCESTOR_SUPPORT
if (ctx->current->run) {
ctx->current->run(ctx);
}
#else
ctx->executing = ctx->current;
if (ctx->current->run) {
enum smf_state_result rc = ctx->current->run(ctx);

if (rc == SMF_EVENT_HANDLED) {
internal->handled = true;
}
}

#ifdef CONFIG_SMF_ANCESTOR_SUPPORT
if (smf_execute_ancestor_run_actions(ctx)) {
return ctx->terminate_val;
}
Expand Down
27 changes: 17 additions & 10 deletions samples/subsys/smf/hsm_psicc2/src/hsm_psicc2_thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@ static void initial_entry(void *o)
obj->foo = false;
}

static void initial_run(void *o)
static enum smf_state_result initial_run(void *o)
{
LOG_INF("%s", __func__);
return SMF_EVENT_PROPAGATE;
}

static void initial_exit(void *o)
Expand All @@ -67,7 +68,7 @@ static void s_entry(void *o)
LOG_INF("%s", __func__);
}

static void s_run(void *o)
static enum smf_state_result s_run(void *o)
{
LOG_INF("%s", __func__);
struct s_object *obj = (struct s_object *)o;
Expand All @@ -84,12 +85,13 @@ static void s_run(void *o)
} else {
LOG_INF("%s received EVENT_I and did nothing", __func__);
}
smf_set_handled(SMF_CTX(obj));
return SMF_EVENT_HANDLED;
break;
case EVENT_TERMINATE:
LOG_INF("%s received SMF_EVENT_TERMINATE. Terminating", __func__);
smf_set_terminate(SMF_CTX(obj), -1);
}
return SMF_EVENT_PROPAGATE;
}

static void s_exit(void *o)
Expand All @@ -103,7 +105,7 @@ static void s1_entry(void *o)
LOG_INF("%s", __func__);
}

static void s1_run(void *o)
static enum smf_state_result s1_run(void *o)
{
LOG_INF("%s", __func__);
struct s_object *obj = (struct s_object *)o;
Expand Down Expand Up @@ -136,9 +138,10 @@ static void s1_run(void *o)
break;
case EVENT_I:
LOG_INF("%s received EVENT_I", __func__);
smf_set_handled(SMF_CTX(obj));
return SMF_EVENT_HANDLED;
break;
}
return SMF_EVENT_PROPAGATE;
}

static void s1_exit(void *o)
Expand All @@ -152,7 +155,7 @@ static void s11_entry(void *o)
LOG_INF("%s", __func__);
}

static void s11_run(void *o)
static enum smf_state_result s11_run(void *o)
{
LOG_INF("%s", __func__);
struct s_object *obj = (struct s_object *)o;
Expand All @@ -176,6 +179,7 @@ static void s11_run(void *o)
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S]);
break;
}
return SMF_EVENT_PROPAGATE;
}

static void s11_exit(void *o)
Expand All @@ -189,7 +193,7 @@ static void s2_entry(void *o)
LOG_INF("%s", __func__);
}

static void s2_run(void *o)
static enum smf_state_result s2_run(void *o)
{
LOG_INF("%s", __func__);
struct s_object *obj = (struct s_object *)o;
Expand All @@ -207,12 +211,13 @@ static void s2_run(void *o)
if (!obj->foo) {
LOG_INF("%s received EVENT_I and set foo true", __func__);
obj->foo = true;
smf_set_handled(SMF_CTX(obj));
return SMF_EVENT_HANDLED;
} else {
LOG_INF("%s received EVENT_I and did nothing", __func__);
}
break;
}
return SMF_EVENT_PROPAGATE;
}

static void s2_exit(void *o)
Expand All @@ -226,7 +231,7 @@ static void s21_entry(void *o)
LOG_INF("%s", __func__);
}

static void s21_run(void *o)
static enum smf_state_result s21_run(void *o)
{
LOG_INF("%s", __func__);
struct s_object *obj = (struct s_object *)o;
Expand All @@ -245,6 +250,7 @@ static void s21_run(void *o)
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S1]);
break;
}
return SMF_EVENT_PROPAGATE;
}

static void s21_exit(void *o)
Expand All @@ -258,7 +264,7 @@ static void s211_entry(void *o)
LOG_INF("%s", __func__);
}

static void s211_run(void *o)
static enum smf_state_result s211_run(void *o)
{
LOG_INF("%s", __func__);
struct s_object *obj = (struct s_object *)o;
Expand All @@ -273,6 +279,7 @@ static void s211_run(void *o)
smf_set_state(SMF_CTX(obj), &demo_states[STATE_S]);
break;
}
return SMF_EVENT_PROPAGATE;
}

static void s211_exit(void *o)
Expand Down
Loading

0 comments on commit 0a6b01b

Please sign in to comment.