diff options
Diffstat (limited to 'common/usb_sm.c')
-rw-r--r-- | common/usb_sm.c | 166 |
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 +} |