This is an automated email from the ASF dual-hosted git repository.
linguini pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
The following commit(s) were added to refs/heads/master by this push:
new ca11a7e09 system/smf: Port SMF .c/.h files to NuttX
ca11a7e09 is described below
commit ca11a7e0930a0170d2c17e7913cd3fc221e2f6a5
Author: Felipe MdeO <[email protected]>
AuthorDate: Thu Jan 1 16:46:27 2026 -0300
system/smf: Port SMF .c/.h files to NuttX
This commit add state machine framework lib to the NuttX project. Also an
example is added to help users understand and use this feature.
Changes: Added some files. No impact in other features are expected.
Adjust SMF macro names
Fix issues requested during MR review
update kconfig
fix ci-cd issue
Signed-off-by: Felipe Moura <[email protected]>
---
examples/smf/CMakeLists.txt | 18 ++
examples/smf/Kconfig | 37 +++
examples/smf/Make.defs | 25 ++
examples/smf/Makefile | 37 +++
examples/smf/hsm_psicc2_thread.c | 626 +++++++++++++++++++++++++++++++++++++++
examples/smf/hsm_psicc2_thread.h | 89 ++++++
examples/smf/smf_main.c | 127 ++++++++
include/system/smf.h | 267 +++++++++++++++++
system/smf/Kconfig | 28 ++
system/smf/Make.defs | 25 ++
system/smf/Makefile | 29 ++
system/smf/smf.c | 605 +++++++++++++++++++++++++++++++++++++
12 files changed, 1913 insertions(+)
diff --git a/examples/smf/CMakeLists.txt b/examples/smf/CMakeLists.txt
new file mode 100644
index 000000000..bba48ca02
--- /dev/null
+++ b/examples/smf/CMakeLists.txt
@@ -0,0 +1,18 @@
+#
##############################################################################
+# apps/examples/hsm_psicc2/CMakeLists.txt
+#
+# SPDX-License-Identifier: Apache-2.0
+#
##############################################################################
+
+if(CONFIG_EXAMPLES_SMF)
+ nuttx_add_application(
+ NAME
+ ${CONFIG_EXAMPLES_SMF_PROGNAME}
+ SRCS
+ smf_main.c
+ hsm_psicc2_thread.c
+ STACKSIZE
+ ${CONFIG_EXAMPLES_SMF_STACKSIZE}
+ PRIORITY
+ ${CONFIG_EXAMPLES_SMF_PRIORITY})
+endif()
diff --git a/examples/smf/Kconfig b/examples/smf/Kconfig
new file mode 100644
index 000000000..8d8efc1f7
--- /dev/null
+++ b/examples/smf/Kconfig
@@ -0,0 +1,37 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config EXAMPLES_SMF
+ tristate "State Machine Framework PSICC2 demo (HSM)"
+ default n
+ depends on NSH_BUILTIN_APPS
+ depends on SYSTEM_SMF
+ depends on SYSTEM_SMF_ANCESTOR_SUPPORT
+ depends on SYSTEM_SMF_INITIAL_TRANSITION
+
+if EXAMPLES_SMF
+
+config EXAMPLES_SMF_PROGNAME
+ string "Program name"
+ default "hsm_psicc2"
+
+config EXAMPLES_SMF_PRIORITY
+ int "Priority"
+ default 100
+
+config EXAMPLES_SMF_STACKSIZE
+ int "Stack size"
+ default 2048
+
+config EXAMPLES_SMF_EVENT_QUEUE_SIZE
+ int "Event queue size"
+ default 10
+
+config EXAMPLES_SMF_MQ_NAME
+ string "Message queue name"
+ default "/hsm_psicc2_mq"
+
+endif
+
diff --git a/examples/smf/Make.defs b/examples/smf/Make.defs
new file mode 100644
index 000000000..00be28079
--- /dev/null
+++ b/examples/smf/Make.defs
@@ -0,0 +1,25 @@
+############################################################################
+# apps/examples/hello/Make.defs
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+ifneq ($(CONFIG_EXAMPLES_SMF),)
+CONFIGURED_APPS += $(APPDIR)/examples/smf
+endif
diff --git a/examples/smf/Makefile b/examples/smf/Makefile
new file mode 100644
index 000000000..2fd9309c1
--- /dev/null
+++ b/examples/smf/Makefile
@@ -0,0 +1,37 @@
+############################################################################
+# apps/examples/hsm_psicc2/Makefile
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+include $(APPDIR)/Make.defs
+
+# HSM PSICC2 (SMF demo) built-in application info
+
+PROGNAME = $(CONFIG_EXAMPLES_SMF_PROGNAME)
+PRIORITY = $(CONFIG_EXAMPLES_SMF_PRIORITY)
+STACKSIZE = $(CONFIG_EXAMPLES_SMF_STACKSIZE)
+MODULE = $(CONFIG_EXAMPLES_SMF)
+
+# HSM PSICC2 Example
+
+MAINSRC = smf_main.c
+CSRCS += hsm_psicc2_thread.c
+
+include $(APPDIR)/Application.mk
diff --git a/examples/smf/hsm_psicc2_thread.c b/examples/smf/hsm_psicc2_thread.c
new file mode 100644
index 000000000..e457969e1
--- /dev/null
+++ b/examples/smf/hsm_psicc2_thread.c
@@ -0,0 +1,626 @@
+/****************************************************************************
+ * apps/examples/smf/hsm_psicc2_thread.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright (c) 2024 Glenn Andrews
+ * State Machine example copyright (c) Miro Samek
+ *
+ * Implementation of the statechart in Figure 2.11 of
+ * Practical UML Statecharts in C/C++, 2nd Edition by Miro Samek
+ * https://www.state-machine.com/psicc2
+ * Used with permission of the author.
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <mqueue.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <system/smf.h>
+
+#include "hsm_psicc2_thread.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct s_object
+{
+ struct smf_ctx ctx; /* MUST be first (matches SMF_CTX(obj)) */
+ struct hsm_psicc2_event event;
+ int foo;
+};
+
+enum demo_states
+{
+ STATE_INITIAL,
+ STATE_S,
+ STATE_S1,
+ STATE_S2,
+ STATE_S11,
+ STATE_S21,
+ STATE_S211
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct s_object s_obj;
+static bool g_started;
+static const struct smf_state demo_states[];
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * STATE_INITIAL
+ ****************************************************************************/
+
+static void initial_entry(void *o)
+{
+ struct s_object *obj = (struct s_object *)o;
+
+ printf("[psicc2] %s\n", __func__);
+ obj->foo = false;
+}
+
+static enum smf_state_result initial_run(void *o)
+{
+ (void)o;
+
+ printf("[psicc2] %s\n", __func__);
+ return SMF_EVENT_PROPAGATE;
+}
+
+static void initial_exit(void *o)
+{
+ (void)o;
+
+ printf("[psicc2] %s\n", __func__);
+}
+
+/****************************************************************************
+ * STATE_S
+ ****************************************************************************/
+
+static void s_entry(void *o)
+{
+ (void)o;
+
+ printf("[psicc2] %s", __func__);
+}
+
+static enum smf_state_result s_run(void *o)
+{
+ struct s_object *obj = (struct s_object *)o;
+
+ printf("[psicc2] %s\n", __func__);
+
+ switch (obj->event.event_id)
+ {
+ case EVENT_E:
+ printf("[psicc2] %s received EVENT_E", __func__);
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S11]);
+ break;
+
+ case EVENT_I:
+ if (obj->foo)
+ {
+ printf("[psicc2] %s received EVENT_I and set foo false\n",
+ __func__);
+ obj->foo = false;
+ }
+ else
+ {
+ printf("[psicc2] %s received EVENT_I and did nothing\n",
+ __func__);
+ }
+
+ return SMF_EVENT_HANDLED;
+
+ case EVENT_TERMINATE:
+ printf("[psicc2] %s received EVENT_TERMINATE. Terminating\n",
+ __func__);
+ smf_set_terminate(SMF_CTX(obj), -1);
+ break;
+
+ default:
+ break;
+ }
+
+ return SMF_EVENT_PROPAGATE;
+}
+
+static void s_exit(void *o)
+{
+ (void)o;
+
+ printf("%s", __func__);
+}
+
+/****************************************************************************
+ * STATE_S1
+ ****************************************************************************/
+
+static void s1_entry(void *o)
+{
+ (void)o;
+
+ printf("%s", __func__);
+}
+
+static enum smf_state_result s1_run(void *o)
+{
+ struct s_object *obj = (struct s_object *)o;
+
+ printf("[psicc2] %s\n", __func__);
+
+ switch (obj->event.event_id)
+ {
+ case EVENT_A:
+ printf("[psicc2] %s received EVENT_A\n", __func__);
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S1]);
+ break;
+
+ case EVENT_B:
+ printf("[psicc2] %s received EVENT_B\n", __func__);
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S11]);
+ break;
+
+ case EVENT_C:
+ printf("[psicc2] %s received EVENT_C\n", __func__);
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S2]);
+ break;
+
+ case EVENT_D:
+ if (!obj->foo)
+ {
+ printf("[psicc2] %s received EVENT_D and acted on it\n",
+ __func__);
+ obj->foo = true;
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S]);
+ }
+ else
+ {
+ printf("[psicc2] %s received EVENT_D and ignored it\n",
+ __func__);
+ }
+ break;
+
+ case EVENT_F:
+ printf("[psicc2] %s received EVENT_F\n", __func__);
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S211]);
+ break;
+
+ case EVENT_I:
+ printf("[psicc2] %s received EVENT_I\n", __func__);
+ return SMF_EVENT_HANDLED;
+
+ default:
+ break;
+ }
+
+ return SMF_EVENT_PROPAGATE;
+}
+
+static void s1_exit(void *o)
+{
+ (void)o;
+
+ printf("[psicc2] %s\n", __func__);
+}
+
+/****************************************************************************
+ * STATE_S11
+ ****************************************************************************/
+
+static void s11_entry(void *o)
+{
+ (void)o;
+
+ printf("[psicc2] %s\n", __func__);
+}
+
+static enum smf_state_result s11_run(void *o)
+{
+ struct s_object *obj = (struct s_object *)o;
+
+ printf("[psicc2] %s\n", __func__);
+
+ switch (obj->event.event_id)
+ {
+ case EVENT_D:
+ if (obj->foo)
+ {
+ printf("[psicc2] %s received EVENT_D and acted upon it\n",
+ __func__);
+ obj->foo = false;
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S1]);
+ }
+ else
+ {
+ printf("[psicc2] %s received EVENT_D and ignored it\n",
+ __func__);
+ }
+ break;
+
+ case EVENT_G:
+ printf("[psicc2] %s received EVENT_G\n", __func__);
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S21]);
+ break;
+
+ case EVENT_H:
+ printf("[psicc2] %s received EVENT_H\n", __func__);
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S]);
+ break;
+
+ default:
+ break;
+ }
+
+ return SMF_EVENT_PROPAGATE;
+}
+
+static void s11_exit(void *o)
+{
+ (void)o;
+
+ printf("[psicc2] %s\n", __func__);
+}
+
+/****************************************************************************
+ * STATE_S2
+ ****************************************************************************/
+
+static void s2_entry(void *o)
+{
+ (void)o;
+
+ printf("[psicc2] %s\n", __func__);
+}
+
+static enum smf_state_result s2_run(void *o)
+{
+ struct s_object *obj = (struct s_object *)o;
+
+ printf("[psicc2] %s\n", __func__);
+
+ switch (obj->event.event_id)
+ {
+ case EVENT_C:
+ printf("[psicc2] %s received EVENT_C\n", __func__);
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S1]);
+ break;
+
+ case EVENT_F:
+ printf("[psicc2] %s received EVENT_F\n", __func__);
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S11]);
+ break;
+
+ case EVENT_I:
+ if (!obj->foo)
+ {
+ printf("[psicc2] %s received EVENT_I and set foo true\n",
+ __func__);
+ obj->foo = true;
+ return SMF_EVENT_HANDLED;
+ }
+ else
+ {
+ printf("[psicc2] %s received EVENT_I and did nothing\n",
+ __func__);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return SMF_EVENT_PROPAGATE;
+}
+
+static void s2_exit(void *o)
+{
+ (void)o;
+
+ printf("[psicc2] %s\n", __func__);
+}
+
+/****************************************************************************
+ * STATE_S21
+ ****************************************************************************/
+
+static void s21_entry(void *o)
+{
+ (void)o;
+
+ printf("[psicc2] %s\n", __func__);
+}
+
+static enum smf_state_result s21_run(void *o)
+{
+ struct s_object *obj = (struct s_object *)o;
+
+ printf("[psicc2] %s\n", __func__);
+
+ switch (obj->event.event_id)
+ {
+ case EVENT_A:
+ printf("[psicc2] %s received EVENT_A\n", __func__);
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S21]);
+ break;
+
+ case EVENT_B:
+ printf("[psicc2] %s received EVENT_B\n", __func__);
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S211]);
+ break;
+
+ case EVENT_G:
+ printf("[psicc2] %s received EVENT_G\n", __func__);
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S1]);
+ break;
+
+ default:
+ break;
+ }
+
+ return SMF_EVENT_PROPAGATE;
+}
+
+static void s21_exit(void *o)
+{
+ (void)o;
+
+ printf("[psicc2] %s\n", __func__);
+}
+
+/****************************************************************************
+ * STATE_S211
+ ****************************************************************************/
+
+static void s211_entry(void *o)
+{
+ (void)o;
+
+ printf("[psicc2] %s\n", __func__);
+}
+
+static enum smf_state_result s211_run(void *o)
+{
+ struct s_object *obj = (struct s_object *)o;
+
+ printf("[psicc2] %s\n", __func__);
+
+ switch (obj->event.event_id)
+ {
+ case EVENT_D:
+ printf("[psicc2] %s received EVENT_D\n", __func__);
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S21]);
+ break;
+
+ case EVENT_H:
+ printf("[psicc2] %s received EVENT_H\n", __func__);
+ smf_set_state(SMF_CTX(obj), &demo_states[STATE_S]);
+ break;
+
+ default:
+ break;
+ }
+
+ return SMF_EVENT_PROPAGATE;
+}
+
+static void s211_exit(void *o)
+{
+ (void)o;
+
+ printf("[psicc2] %s\n", __func__);
+}
+
+/****************************************************************************
+ * State Table
+ ****************************************************************************/
+
+static const struct smf_state demo_states[] =
+{
+ [STATE_INITIAL] = SMF_CREATE_STATE(
+ initial_entry,
+ initial_run,
+ initial_exit,
+ NULL,
+ &demo_states[STATE_S2]
+ ),
+ [STATE_S] = SMF_CREATE_STATE(
+ s_entry,
+ s_run,
+ s_exit,
+ &demo_states[STATE_INITIAL],
+ &demo_states[STATE_S11]
+ ),
+ [STATE_S1] = SMF_CREATE_STATE(
+ s1_entry,
+ s1_run,
+ s1_exit,
+ &demo_states[STATE_S],
+ &demo_states[STATE_S11]
+ ),
+ [STATE_S2] = SMF_CREATE_STATE(
+ s2_entry,
+ s2_run,
+ s2_exit,
+ &demo_states[STATE_S],
+ &demo_states[STATE_S211]
+ ),
+ [STATE_S11] = SMF_CREATE_STATE(
+ s11_entry,
+ s11_run,
+ s11_exit,
+ &demo_states[STATE_S1],
+ NULL
+ ),
+ [STATE_S21] = SMF_CREATE_STATE(
+ s21_entry,
+ s21_run,
+ s21_exit,
+ &demo_states[STATE_S2],
+ &demo_states[STATE_S211]
+ ),
+ [STATE_S211] = SMF_CREATE_STATE(
+ s211_entry,
+ s211_run,
+ s211_exit,
+ &demo_states[STATE_S21],
+ NULL
+ )
+};
+
+/****************************************************************************
+ * Name: hsm_psicc2_thread_main
+ ****************************************************************************/
+
+static int hsm_psicc2_thread_main(int argc, char **argv)
+{
+ struct mq_attr attr;
+ mqd_t mq;
+ ssize_t n;
+ int rc;
+
+ (void)argc;
+ (void)argv;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.mq_maxmsg = CONFIG_EXAMPLES_SMF_EVENT_QUEUE_SIZE;
+ attr.mq_msgsize = sizeof(struct hsm_psicc2_event);
+
+ mq = mq_open(CONFIG_EXAMPLES_SMF_MQ_NAME, O_CREAT | O_RDONLY, 0666, &attr);
+ if (mq == (mqd_t)-1)
+ {
+ printf("psicc2][ERR] mq_open(%s) failed: %d\n",
+ CONFIG_EXAMPLES_SMF_MQ_NAME, errno);
+ return -1;
+ }
+
+ printf("[psicc2] State Machine thread started\n");
+
+ memset(&s_obj, 0, sizeof(s_obj));
+ smf_set_initial(SMF_CTX(&s_obj), &demo_states[STATE_INITIAL]);
+
+ while (true)
+ {
+ n = mq_receive(mq, (char *)&s_obj.event, sizeof(s_obj.event), NULL);
+ if (n < 0)
+ {
+ printf("psicc2][ERR] mq_receive failed: %d\n", errno);
+ continue;
+ }
+
+ rc = smf_run_state(SMF_CTX(&s_obj));
+ if (rc != 0)
+ {
+ printf("[psicc2] SMF terminated (rc=%d). Exiting thread\n", rc);
+ break;
+ }
+ }
+
+ mq_close(mq);
+ mq_unlink(CONFIG_EXAMPLES_SMF_MQ_NAME);
+
+ g_started = false;
+ memset(&s_obj, 0, sizeof(s_obj));
+
+ return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: hsm_psicc2_thread_start
+ ****************************************************************************/
+
+int hsm_psicc2_thread_start(void)
+{
+ int pid;
+
+ if (g_started)
+ {
+ return 0;
+ }
+
+ pid = task_create("psicc2_thread",
+ CONFIG_EXAMPLES_SMF_PRIORITY,
+ CONFIG_EXAMPLES_SMF_STACKSIZE,
+ hsm_psicc2_thread_main,
+ NULL);
+ if (pid < 0)
+ {
+ printf("psicc2][ERR] task_create failed: %d", errno);
+ return -1;
+ }
+
+ g_started = true;
+ return 0;
+}
+
+/****************************************************************************
+ * Name: hsm_psicc2_post_event
+ ****************************************************************************/
+
+int hsm_psicc2_post_event(uint32_t event_id)
+{
+ struct hsm_psicc2_event ev;
+ mqd_t mq;
+ int rc;
+
+ ev.event_id = event_id;
+
+ mq = mq_open(CONFIG_EXAMPLES_SMF_MQ_NAME, O_WRONLY | O_NONBLOCK);
+ if (mq == (mqd_t)-1)
+ {
+ printf("psicc2][ERR] mq_open(O_WRONLY) failed: %d "
+ "(did you run 'hsm_psicc2 start'?)\n",
+ errno);
+ return -1;
+ }
+
+ rc = mq_send(mq, (const char *)&ev, sizeof(ev), 0);
+ if (rc < 0)
+ {
+ printf("psicc2][ERR] mq_send failed: %d\n", errno);
+ mq_close(mq);
+ return -1;
+ }
+
+ mq_close(mq);
+ return 0;
+}
diff --git a/examples/smf/hsm_psicc2_thread.h b/examples/smf/hsm_psicc2_thread.h
new file mode 100644
index 000000000..385a29703
--- /dev/null
+++ b/examples/smf/hsm_psicc2_thread.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+ * apps/examples/smf/hsm_psicc2_thread.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright (c) 2024 Glenn Andrews
+ * State Machine example copyright (c) Miro Samek
+ *
+ * Implementation of the statechart in Figure 2.11 of
+ * Practical UML Statecharts in C/C++, 2nd Edition by Miro Samek
+ * https://www.state-machine.com/psicc2
+ * Used with permission of the author.
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __APPS_EXAMPLES_SMF_HSM_PSICC2_THREAD_H
+#define __APPS_EXAMPLES_SMF_HSM_PSICC2_THREAD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct hsm_psicc2_event
+{
+ uint32_t event_id;
+};
+
+enum demo_events
+{
+ EVENT_A,
+ EVENT_B,
+ EVENT_C,
+ EVENT_D,
+ EVENT_E,
+ EVENT_F,
+ EVENT_G,
+ EVENT_H,
+ EVENT_I,
+ EVENT_TERMINATE
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+int hsm_psicc2_thread_start(void);
+int hsm_psicc2_post_event(uint32_t event_id);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __APPS_EXAMPLES_SMF_HSM_PSICC2_THREAD_H */
diff --git a/examples/smf/smf_main.c b/examples/smf/smf_main.c
new file mode 100644
index 000000000..b6a9677a5
--- /dev/null
+++ b/examples/smf/smf_main.c
@@ -0,0 +1,127 @@
+/****************************************************************************
+ * apps/examples/smf/smf_main.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright (c) 2024 Glenn Andrews
+ * State Machine example copyright (c) Miro Samek
+ *
+ * Implementation of the statechart in Figure 2.11 of
+ * Practical UML Statecharts in C/C++, 2nd Edition by Miro Samek
+ * https://www.state-machine.com/psicc2
+ * Used with permission of the author.
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "hsm_psicc2_thread.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usage
+ ****************************************************************************/
+
+static void usage(void)
+{
+ printf("Usage:\n");
+ printf(" hsm_psicc2 start\n");
+ printf(" hsm_psicc2 event <A..I>\n");
+ printf(" hsm_psicc2 terminate\n");
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: main
+ ****************************************************************************/
+
+int main(int argc, char *argv[])
+{
+ if (argc < 2)
+ {
+ usage();
+ return 1;
+ }
+
+ if (strcmp(argv[1], "start") == 0)
+ {
+ printf("State Machine Framework Demo\n");
+ printf("See PSiCC2 Fig 2.11 for the statechart\n");
+ printf("https://www.state-machine.com/psicc2\n\n");
+ return hsm_psicc2_thread_start();
+ }
+
+ if (strcmp(argv[1], "terminate") == 0)
+ {
+ return hsm_psicc2_post_event(EVENT_TERMINATE);
+ }
+
+ if (strcmp(argv[1], "event") == 0)
+ {
+ int c;
+
+ if (argc < 3 || argv[2] == NULL || argv[2][0] == '\0')
+ {
+ usage();
+ return 1;
+ }
+
+ c = toupper((unsigned char)argv[2][0]);
+ switch (c)
+ {
+ case 'A':
+ return hsm_psicc2_post_event(EVENT_A);
+ case 'B':
+ return hsm_psicc2_post_event(EVENT_B);
+ case 'C':
+ return hsm_psicc2_post_event(EVENT_C);
+ case 'D':
+ return hsm_psicc2_post_event(EVENT_D);
+ case 'E':
+ return hsm_psicc2_post_event(EVENT_E);
+ case 'F':
+ return hsm_psicc2_post_event(EVENT_F);
+ case 'G':
+ return hsm_psicc2_post_event(EVENT_G);
+ case 'H':
+ return hsm_psicc2_post_event(EVENT_H);
+ case 'I':
+ return hsm_psicc2_post_event(EVENT_I);
+ default:
+ printf("Invalid event '%c'\n", c);
+ return 1;
+ }
+ }
+
+ usage();
+ return 1;
+}
diff --git a/include/system/smf.h b/include/system/smf.h
new file mode 100644
index 000000000..edb1bf81b
--- /dev/null
+++ b/include/system/smf.h
@@ -0,0 +1,267 @@
+/****************************************************************************
+ * apps/include/system/smf.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __APPS_INCLUDE_SYSTEM_SMF_H
+#define __APPS_INCLUDE_SYSTEM_SMF_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
+# ifdef CONFIG_SYSTEM_SMF_INITIAL_TRANSITION
+# define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \
+ { \
+ .entry = (_entry), \
+ .run = (_run), \
+ .exit = (_exit), \
+ .parent = (_parent), \
+ .initial = (_initial), \
+ }
+# else
+# define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \
+ { \
+ .entry = (_entry), \
+ .run = (_run), \
+ .exit = (_exit), \
+ .parent = (_parent), \
+ }
+# endif
+#else
+# define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \
+ { \
+ .entry = (_entry), \
+ .run = (_run), \
+ .exit = (_exit), \
+ }
+#endif
+
+#define SMF_CTX(o) ((struct smf_ctx *)(o))
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+enum smf_state_result
+{
+ SMF_EVENT_HANDLED,
+ SMF_EVENT_PROPAGATE
+};
+
+typedef void (*state_method)(void *obj);
+
+typedef enum smf_state_result (*state_execution)(void *obj);
+
+struct smf_state
+{
+ /* Optional method that will be run when this state is entered. */
+
+ state_method entry;
+
+ /* Optional method that will be run repeatedly during the state machine
+ * loop.
+ */
+
+ state_execution run;
+
+ /* Optional method that will be run when this state exits. */
+
+ state_method exit;
+
+#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
+ /* Optional parent state that contains common entry/run/exit
+ * implementation among various child states.
+ * entry: Parent function executes BEFORE child function.
+ * run: Parent function executes AFTER child function.
+ * exit: Parent function executes AFTER child function.
+ * Note: When transitioning between two child states with a shared
+ * parent, that parent's exit and entry functions do not execute.
+ */
+
+ const struct smf_state *parent;
+
+# ifdef CONFIG_SYSTEM_SMF_INITIAL_TRANSITION
+ /* Optional initial transition state. NULL for leaf states. */
+
+ const struct smf_state *initial;
+# endif /* CONFIG_SYSTEM_SMF_INITIAL_TRANSITION */
+#endif /* CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT */
+};
+
+struct smf_ctx
+{
+ /* Current state the state machine is executing. */
+
+ const struct smf_state *current;
+
+ /* Previous state the state machine executed. */
+
+ const struct smf_state *previous;
+
+#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
+ /* Currently executing state (which may be a parent). */
+
+ const struct smf_state *executing;
+#endif /* CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT */
+
+ /* This value is set by the set_terminate function and should
+ * terminate the state machine when its set to a value other than
+ * zero when it's returned by the run_state function.
+ */
+
+ int32_t terminate_val;
+
+ /* The state machine casts this to a "struct internal_ctx" and it's
+ * used to track state machine context.
+ */
+
+ uint32_t internal;
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: smf_set_initial
+ *
+ * Description:
+ * Initialize the state machine and set its initial state.
+ *
+ * Input Parameters:
+ * ctx - State machine context
+ * init_state - Initial state the state machine starts in
+ *
+ ****************************************************************************/
+
+void smf_set_initial(struct smf_ctx *ctx,
+ const struct smf_state *init_state);
+
+/****************************************************************************
+ * Name: smf_set_state
+ *
+ * Description:
+ * Change the state machine state. This handles exiting the previous state
+ * and entering the target state. For HSMs the entry and exit actions of
+ * the Least Common Ancestor are not run.
+ *
+ * Input Parameters:
+ * ctx - State machine context
+ * new_state - State to transition to
+ *
+ ****************************************************************************/
+
+void smf_set_state(struct smf_ctx *ctx, const struct smf_state *new_state);
+
+/****************************************************************************
+ * Name: smf_set_terminate
+ *
+ * Description:
+ * Terminate a state machine.
+ *
+ * Input Parameters:
+ * ctx - State machine context
+ * val - Non-zero termination value returned by smf_run_state
+ *
+ ****************************************************************************/
+
+void smf_set_terminate(struct smf_ctx *ctx, int32_t val);
+
+/****************************************************************************
+ * Name: smf_run_state
+ *
+ * Description:
+ * Run one iteration of a state machine (including any parent states).
+ *
+ * Input Parameters:
+ * ctx - State machine context
+ *
+ * Returned Value:
+ * A non-zero value terminates the state machine. This non-zero value may
+ * represent a terminal state being reached or detection of an error.
+ *
+ ****************************************************************************/
+
+int32_t smf_run_state(struct smf_ctx *ctx);
+
+/****************************************************************************
+ * Name: smf_get_current_leaf_state
+ *
+ * Description:
+ * Get the current leaf state. This may be a parent state if the HSM is
+ * malformed (initial transitions are not set up correctly).
+ *
+ ****************************************************************************/
+
+static inline const struct smf_state *
+smf_get_current_leaf_state(const struct smf_ctx *ctx)
+{
+ return ctx->current;
+}
+
+/****************************************************************************
+ * Name: smf_get_current_executing_state
+ *
+ * Description:
+ * Get the state that is currently executing. This may be a parent state.
+ *
+ ****************************************************************************/
+
+static inline const struct smf_state *
+smf_get_current_executing_state(const struct smf_ctx *ctx)
+{
+#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
+ return ctx->executing;
+#else
+ return ctx->current;
+#endif /* CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT */
+}
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __APPS_INCLUDE_SYSTEM_SMF_H */
diff --git a/system/smf/Kconfig b/system/smf/Kconfig
new file mode 100644
index 000000000..62e52cf56
--- /dev/null
+++ b/system/smf/Kconfig
@@ -0,0 +1,28 @@
+# SMF (State Machine Framework) configuration options
+#
+# For syntax reference, see kconfig-language.txt in the NuttX tools repository.
+
+config SYSTEM_SMF
+ bool "(SMF) support"
+ default n
+ ---help---
+ Enables the State Machine Framework (SMF) for implementing state machines
+
+if SYSTEM_SMF
+
+config SYSTEM_SMF_ANCESTOR_SUPPORT
+ bool "Enable ancestor/parent state support"
+ default n
+ ---help---
+ Enables support for parent/ancestor relationships between SMF states.
+ Required for hierarchical state machines.
+
+config SYSTEM_SMF_INITIAL_TRANSITION
+ bool "Enable initial transition support"
+ default n
+ depends on SYSTEM_SMF_ANCESTOR_SUPPORT
+ ---help---
+ Enables support for initial transitions in hierarchical states.
+ Depends on ancestor/parent state support.
+
+endif # SYSTEM_SMF
diff --git a/system/smf/Make.defs b/system/smf/Make.defs
new file mode 100644
index 000000000..d628714f2
--- /dev/null
+++ b/system/smf/Make.defs
@@ -0,0 +1,25 @@
+############################################################################
+# apps/smf/Make.defs
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+ifeq ($(CONFIG_SYSTEM_SMF),y)
+CONFIGURED_APPS += $(APPDIR)/system/smf
+endif
diff --git a/system/smf/Makefile b/system/smf/Makefile
new file mode 100644
index 000000000..d5e1b549c
--- /dev/null
+++ b/system/smf/Makefile
@@ -0,0 +1,29 @@
+############################################################################
+# apps/smf/Makefile
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+include $(APPDIR)/Make.defs
+
+# State Machine Framework library
+
+CSRCS = smf.c
+
+include $(APPDIR)/Application.mk
diff --git a/system/smf/smf.c b/system/smf/smf.c
new file mode 100644
index 000000000..473a64e23
--- /dev/null
+++ b/system/smf/smf.c
@@ -0,0 +1,605 @@
+/****************************************************************************
+ * apps/system/smf/smf.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright 2021 The Chromium OS Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <system/smf.h>
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct internal_ctx
+{
+ bool new_state : 1;
+ bool terminate : 1;
+ bool is_exit : 1;
+ bool handled : 1;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
+
+/****************************************************************************
+ * Name: is_descendant_of
+ ****************************************************************************/
+
+static bool is_descendant_of(const struct smf_state *test_state,
+ const struct smf_state *target_state)
+{
+ const struct smf_state *state;
+
+ for (state = test_state; state != NULL; state = state->parent)
+ {
+ if (target_state == state)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/****************************************************************************
+ * Name: get_child_of
+ ****************************************************************************/
+
+static const struct smf_state *
+get_child_of(const struct smf_state *states, const struct smf_state *parent)
+{
+ const struct smf_state *state = states;
+
+ while (state != NULL)
+ {
+ if (state->parent == parent)
+ {
+ return state;
+ }
+
+ state = state->parent;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ * Name: get_lca_of
+ *
+ * Description:
+ * Find the Least Common Ancestor (LCA) of two states that are not
+ * ancestors of one another.
+ *
+ ****************************************************************************/
+
+static const struct smf_state *
+get_lca_of(const struct smf_state *source, const struct smf_state *dest)
+{
+ const struct smf_state *ancestor;
+
+ for (ancestor = source->parent; ancestor != NULL;
+ ancestor = ancestor->parent)
+ {
+ /* First common ancestor. */
+
+ if (is_descendant_of(dest, ancestor))
+ {
+ return ancestor;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ * Name: smf_execute_all_entry_actions
+ ****************************************************************************/
+
+static bool smf_execute_all_entry_actions(struct smf_ctx *const ctx,
+ const struct smf_state *new_state,
+ const struct smf_state *topmost)
+{
+ struct internal_ctx *const internal = (void *)&ctx->internal;
+ const struct smf_state *to_execute;
+
+ if (new_state == topmost)
+ {
+ /* There are no child states, so do nothing. */
+
+ return false;
+ }
+
+ for (to_execute = get_child_of(new_state, topmost);
+ to_execute != NULL && to_execute != new_state;
+ to_execute = get_child_of(new_state, to_execute))
+ {
+ /* Keep track of the executing entry action in case it calls
+ * smf_set_state().
+ */
+
+ ctx->executing = to_execute;
+
+ /* Execute every entry action EXCEPT that of the topmost state. */
+
+ if (to_execute->entry != NULL)
+ {
+ to_execute->entry(ctx);
+
+ /* No need to continue if terminate was set. */
+
+ if (internal->terminate)
+ {
+ ctx->executing = ctx->current;
+ return true;
+ }
+ }
+ }
+
+ /* Execute the new state entry action. */
+
+ ctx->executing = new_state;
+ if (new_state->entry != NULL)
+ {
+ new_state->entry(ctx);
+
+ /* No need to continue if terminate was set. */
+
+ if (internal->terminate)
+ {
+ ctx->executing = ctx->current;
+ return true;
+ }
+ }
+
+ ctx->executing = ctx->current;
+
+ return false;
+}
+
+/****************************************************************************
+ * Name: smf_execute_ancestor_run_actions
+ ****************************************************************************/
+
+static bool smf_execute_ancestor_run_actions(struct smf_ctx *const ctx)
+{
+ struct internal_ctx *const internal = (void *)&ctx->internal;
+ const struct smf_state *tmp_state;
+
+ /* Execute all run actions in reverse order. */
+
+ if (internal->terminate)
+ {
+ /* Return if the current state terminated. */
+
+ return true;
+ }
+
+ if (internal->new_state || internal->handled)
+ {
+ /* The child state transitioned or handled it. Stop propagating. */
+
+ return false;
+ }
+
+ /* Try to run parent run actions. */
+
+ for (tmp_state = ctx->current->parent; tmp_state != NULL;
+ tmp_state = tmp_state->parent)
+ {
+ enum smf_state_result rc;
+
+ /* Keep track of where we are in case an ancestor calls
+ * smf_set_state().
+ */
+
+ ctx->executing = tmp_state;
+
+ /* Execute parent run action. */
+
+ if (tmp_state->run != NULL)
+ {
+ rc = tmp_state->run(ctx);
+
+ if (rc == SMF_EVENT_HANDLED)
+ {
+ internal->handled = true;
+ }
+
+ /* No need to continue if terminate was set. */
+
+ if (internal->terminate)
+ {
+ ctx->executing = ctx->current;
+ return true;
+ }
+
+ /* This state dealt with it. Stop propagating. */
+
+ if (internal->new_state || internal->handled)
+ {
+ break;
+ }
+ }
+ }
+
+ /* All done executing the run actions. */
+
+ ctx->executing = ctx->current;
+
+ return false;
+}
+
+/****************************************************************************
+ * Name: smf_execute_all_exit_actions
+ ****************************************************************************/
+
+static bool smf_execute_all_exit_actions(struct smf_ctx *const ctx,
+ const struct smf_state *topmost)
+{
+ struct internal_ctx *const internal = (void *)&ctx->internal;
+ const struct smf_state *tmp_state;
+ const struct smf_state *to_execute;
+
+ tmp_state = ctx->executing;
+
+ for (to_execute = ctx->current;
+ to_execute != NULL && to_execute != topmost;
+ to_execute = to_execute->parent)
+ {
+ if (to_execute->exit != NULL)
+ {
+ ctx->executing = to_execute;
+ to_execute->exit(ctx);
+
+ /* No need to continue if terminate was set in the exit action. */
+
+ if (internal->terminate)
+ {
+ ctx->executing = tmp_state;
+ return true;
+ }
+ }
+ }
+
+ ctx->executing = tmp_state;
+
+ return false;
+}
+
+#endif /* CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT */
+
+/****************************************************************************
+ * Name: smf_clear_internal_state
+ ****************************************************************************/
+
+static void smf_clear_internal_state(struct smf_ctx *const ctx)
+{
+ struct internal_ctx *const internal = (void *)&ctx->internal;
+
+ internal->is_exit = false;
+ internal->terminate = false;
+ internal->handled = false;
+ internal->new_state = false;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: smf_set_initial
+ ****************************************************************************/
+
+void smf_set_initial(struct smf_ctx *const ctx,
+ const struct smf_state *init_state)
+{
+#ifdef CONFIG_SYSTEM_SMF_INITIAL_TRANSITION
+ /* The final target will be the deepest leaf state that the target
+ * contains. Set that as the real target.
+ */
+
+ while (init_state->initial != NULL)
+ {
+ init_state = init_state->initial;
+ }
+#endif
+
+ smf_clear_internal_state(ctx);
+ ctx->current = init_state;
+ ctx->previous = NULL;
+ ctx->terminate_val = 0;
+
+#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
+ struct internal_ctx *const internal = (void *)&ctx->internal;
+ const struct smf_state *topmost;
+
+ ctx->executing = init_state;
+
+ /* topmost is the root ancestor of init_state, its parent == NULL. */
+
+ topmost = get_child_of(init_state, NULL);
+
+ /* Execute topmost state entry action,
+ * since smf_execute_all_entry_actions doesn't.
+ */
+
+ if (topmost->entry != NULL)
+ {
+ ctx->executing = topmost;
+ topmost->entry(ctx);
+ ctx->executing = init_state;
+
+ if (internal->terminate)
+ {
+ /* No need to continue if terminate was set. */
+
+ return;
+ }
+ }
+
+ if (smf_execute_all_entry_actions(ctx, init_state, topmost))
+ {
+ /* No need to continue if terminate was set. */
+
+ return;
+ }
+#else
+ /* Execute entry action if it exists. */
+
+ if (init_state->entry != NULL)
+ {
+ init_state->entry(ctx);
+ }
+#endif
+}
+
+/****************************************************************************
+ * Name: smf_set_state
+ ****************************************************************************/
+
+void smf_set_state(struct smf_ctx *const ctx,
+ const struct smf_state *new_state)
+{
+ struct internal_ctx *const internal = (void *)&ctx->internal;
+
+ if (new_state == NULL)
+ {
+ printf("SMF ERR: new_state cannot be NULL\n");
+ return;
+ }
+
+ /* It does not make sense to call smf_set_state in an exit phase of a
+ * state since we are already in a transition; we would always ignore
+ * the intended state to transition into.
+ */
+
+ if (internal->is_exit)
+ {
+ printf("SMF ERR: Calling %s from exit action\n", __func__);
+ return;
+ }
+
+#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
+ const struct smf_state *topmost;
+
+ if (ctx->executing != new_state &&
+ ctx->executing->parent == new_state->parent)
+ {
+ /* Optimize sibling transitions (different states under same parent). */
+
+ topmost = ctx->executing->parent;
+ }
+ else if (is_descendant_of(ctx->executing, new_state))
+ {
+ /* New state is a parent of where we are now. */
+
+ topmost = new_state;
+ }
+ else if (is_descendant_of(new_state, ctx->executing))
+ {
+ /* We are a parent of the new state. */
+
+ topmost = ctx->executing;
+ }
+ else
+ {
+ /* Not directly related, find LCA. */
+
+ topmost = get_lca_of(ctx->executing, new_state);
+ }
+
+ internal->is_exit = true;
+ internal->new_state = true;
+
+ /* Call all exit actions up to (but not including) the topmost. */
+
+ if (smf_execute_all_exit_actions(ctx, topmost))
+ {
+ /* No need to continue if terminate was set in the exit action. */
+
+ return;
+ }
+
+ /* If self-transition, call the exit action. */
+
+ if (ctx->executing == new_state && new_state->exit != NULL)
+ {
+ new_state->exit(ctx);
+
+ /* No need to continue if terminate was set in the exit action. */
+
+ if (internal->terminate)
+ {
+ return;
+ }
+ }
+
+ internal->is_exit = false;
+
+ /* If self transition, call the entry action. */
+
+ if (ctx->executing == new_state && new_state->entry != NULL)
+ {
+ new_state->entry(ctx);
+
+ /* No need to continue if terminate was set in the entry action. */
+
+ if (internal->terminate)
+ {
+ return;
+ }
+ }
+
+#ifdef CONFIG_SYSTEM_SMF_INITIAL_TRANSITION
+ /* The final target will be the deepest leaf state that the target
+ * contains. Set that as the real target.
+ */
+
+ while (new_state->initial != NULL)
+ {
+ new_state = new_state->initial;
+ }
+#endif
+
+ /* Update the state variables. */
+
+ ctx->previous = ctx->current;
+ ctx->current = new_state;
+ ctx->executing = new_state;
+
+ /* Call all entry actions (except those of topmost). */
+
+ if (smf_execute_all_entry_actions(ctx, new_state, topmost))
+ {
+ /* No need to continue if terminate was set in the entry action. */
+
+ return;
+ }
+#else
+ /* Flat state machines have a very simple transition. */
+
+ if (ctx->current->exit != NULL)
+ {
+ internal->is_exit = true;
+ ctx->current->exit(ctx);
+
+ /* No need to continue if terminate was set in the exit action. */
+
+ if (internal->terminate)
+ {
+ return;
+ }
+
+ internal->is_exit = false;
+ }
+
+ /* Update the state variables. */
+
+ ctx->previous = ctx->current;
+ ctx->current = new_state;
+
+ if (ctx->current->entry != NULL)
+ {
+ ctx->current->entry(ctx);
+
+ /* No need to continue if terminate was set in the entry action. */
+
+ if (internal->terminate)
+ {
+ return;
+ }
+ }
+#endif
+}
+
+/****************************************************************************
+ * Name: smf_set_terminate
+ ****************************************************************************/
+
+void smf_set_terminate(struct smf_ctx *const ctx, int32_t val)
+{
+ struct internal_ctx *const internal = (void *)&ctx->internal;
+
+ internal->terminate = true;
+ ctx->terminate_val = val;
+}
+
+/****************************************************************************
+ * Name: smf_run_state
+ ****************************************************************************/
+
+int32_t smf_run_state(struct smf_ctx *const ctx)
+{
+ struct internal_ctx *const internal = (void *)&ctx->internal;
+
+ /* No need to continue if terminate was set. */
+
+ if (internal->terminate)
+ {
+ return ctx->terminate_val;
+ }
+
+ /* Executing a state's run function could cause a transition, so clear
+ * the internal state to ensure that the transition is handled correctly.
+ */
+
+ smf_clear_internal_state(ctx);
+
+#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT
+ ctx->executing = ctx->current;
+
+ if (ctx->current->run != NULL)
+ {
+ enum smf_state_result rc;
+
+ rc = ctx->current->run(ctx);
+ if (rc == SMF_EVENT_HANDLED)
+ {
+ internal->handled = true;
+ }
+ }
+
+ if (smf_execute_ancestor_run_actions(ctx))
+ {
+ return ctx->terminate_val;
+ }
+#else
+ if (ctx->current->run != NULL)
+ {
+ ctx->current->run(ctx);
+ }
+#endif
+
+ return 0;
+}