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;
+}

Reply via email to