summaryrefslogtreecommitdiff
path: root/common/usb_sm.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/usb_sm.c')
-rw-r--r--common/usb_sm.c166
1 files changed, 166 insertions, 0 deletions
diff --git a/common/usb_sm.c b/common/usb_sm.c
new file mode 100644
index 0000000000..6d3cacdb3b
--- /dev/null
+++ b/common/usb_sm.c
@@ -0,0 +1,166 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "common.h"
+#include "task.h"
+#include "usb_pd.h"
+#include "usb_sm.h"
+#include "util.h"
+#include "console.h"
+
+void init_state(int port, struct sm_obj *obj, sm_state target)
+{
+#if (CONFIG_SM_NESTING_NUM > 0)
+ int i;
+
+ sm_state tmp_super[CONFIG_SM_NESTING_NUM];
+#endif
+
+ obj->last_state = NULL;
+ obj->task_state = target;
+
+#if (CONFIG_SM_NESTING_NUM > 0)
+
+ /* Prepare to execute all entry actions of the target's super states */
+
+ /*
+ * Get targets super state. This will be NULL if the target
+ * has no super state
+ */
+ tmp_super[CONFIG_SM_NESTING_NUM - 1] =
+ (sm_state)(uintptr_t)target(port, SUPER_SIG);
+
+ /* Get all super states of the target */
+ for (i = CONFIG_SM_NESTING_NUM - 1; i > 0; i--) {
+ if (tmp_super[i] != NULL)
+ tmp_super[i - 1] =
+ (sm_state)(uintptr_t)tmp_super[i](port, SUPER_SIG);
+ else
+ tmp_super[i - 1] = NULL;
+ }
+
+ /* Execute all super state entry actions in forward order */
+ for (i = 0; i < CONFIG_SM_NESTING_NUM; i++)
+ if (tmp_super[i] != NULL)
+ tmp_super[i](port, ENTRY_SIG);
+#endif
+
+ /* Now execute the target entry action */
+ target(port, ENTRY_SIG);
+}
+
+int set_state(int port, struct sm_obj *obj, sm_state target)
+{
+#if (CONFIG_SM_NESTING_NUM > 0)
+ int i;
+ int no_execute;
+
+ sm_state tmp_super[CONFIG_SM_NESTING_NUM];
+ sm_state target_super;
+ sm_state last_super;
+ sm_state super;
+
+ /* Execute all exit actions is reverse order */
+
+ /* Get target's super state */
+ target_super = (sm_state)(uintptr_t)target(port, SUPER_SIG);
+ tmp_super[0] = obj->task_state;
+
+ do {
+ /* Execute exit action */
+ tmp_super[0](port, EXIT_SIG);
+
+ /* Get super state */
+ tmp_super[0] =
+ (sm_state)(uintptr_t)tmp_super[0](port, SUPER_SIG);
+ /*
+ * No need to execute a super state's exit action that has
+ * shared ancestry with the target.
+ */
+ super = target_super;
+ while (super != NULL) {
+ if (tmp_super[0] == super) {
+ tmp_super[0] = NULL;
+ break;
+ }
+
+ /* Get target state next super state if it exists */
+ super = (sm_state)(uintptr_t)super(port, SUPER_SIG);
+ }
+ } while (tmp_super[0] != NULL);
+
+ /* All done executing the exit actions */
+#else
+ obj->task_state(port, EXIT_SIG);
+#endif
+ /* update the state variables */
+ obj->last_state = obj->task_state;
+ obj->task_state = target;
+
+#if (CONFIG_SM_NESTING_NUM > 0)
+ /* Prepare to execute all entry actions of the target's super states */
+
+ tmp_super[CONFIG_SM_NESTING_NUM - 1] =
+ (sm_state)(uintptr_t)target(port, SUPER_SIG);
+
+ /* Get all super states of the target */
+ for (i = CONFIG_SM_NESTING_NUM - 1; i > 0; i--) {
+ if (tmp_super[i] != NULL)
+ tmp_super[i - 1] =
+ (sm_state)(uintptr_t)tmp_super[i](port, SUPER_SIG);
+ else
+ tmp_super[i - 1] = NULL;
+ }
+
+ /* Get super state of last state */
+ last_super = (sm_state)(uintptr_t)obj->last_state(port, SUPER_SIG);
+
+ /* Execute all super state entry actions in forward order */
+ for (i = 0; i < CONFIG_SM_NESTING_NUM; i++) {
+ /* No super state */
+ if (tmp_super[i] == NULL)
+ continue;
+
+ /*
+ * We only want to execute the target state's super state entry
+ * action if it doesn't share a super state with the previous
+ * state.
+ */
+ super = last_super;
+ no_execute = 0;
+ while (super != NULL) {
+ if (tmp_super[i] == super) {
+ no_execute = 1;
+ break;
+ }
+
+ /* Get last state's next super state if it exists */
+ super = (sm_state)(uintptr_t)super(port, SUPER_SIG);
+ }
+
+ /* Execute super state's entry */
+ if (!no_execute)
+ tmp_super[i](port, ENTRY_SIG);
+ }
+#endif
+
+ /* Now execute the target entry action */
+ target(port, ENTRY_SIG);
+
+ return 0;
+}
+
+void exe_state(int port, struct sm_obj *obj, enum signal sig)
+{
+#if (CONFIG_SM_NESTING_NUM > 0)
+ sm_state state = obj->task_state;
+
+ do {
+ state = (sm_state)(uintptr_t)state(port, sig);
+ } while (state != NULL);
+#else
+ obj->task_state(port, sig);
+#endif
+}