From bc7748c8521be70522b7d8a34d455c10632143cd Mon Sep 17 00:00:00 2001 From: Srinivas Pitla Date: Thu, 7 Jun 2018 16:01:04 +0530 Subject: [PATCH] qcacmn: Implement State machine framework This is generic state machine framework, which provides APIs for state definition, state transition, event posting Change-Id: Ie7d5a064967098fcb77a663047c479d1d1576595 CRs-Fixed: 2307722 --- qdf/inc/qdf_types.h | 2 + qdf/linux/src/qdf_trace.c | 2 + .../sm_engine/inc/wlan_sm_engine.h | 268 +++++++++++++++ .../sm_engine/inc/wlan_sm_engine_dbg.h | 188 +++++++++++ .../sm_engine/src/wlan_sm_engine.c | 319 ++++++++++++++++++ .../sm_engine/src/wlan_sm_engine_dbg.c | 85 +++++ 6 files changed, 864 insertions(+) create mode 100644 umac/cmn_services/sm_engine/inc/wlan_sm_engine.h create mode 100644 umac/cmn_services/sm_engine/inc/wlan_sm_engine_dbg.h create mode 100644 umac/cmn_services/sm_engine/src/wlan_sm_engine.c create mode 100644 umac/cmn_services/sm_engine/src/wlan_sm_engine_dbg.c diff --git a/qdf/inc/qdf_types.h b/qdf/inc/qdf_types.h index d4c4d4e5a4c2..3fe538189b63 100644 --- a/qdf/inc/qdf_types.h +++ b/qdf/inc/qdf_types.h @@ -360,6 +360,7 @@ typedef void (*qdf_timer_func_t)(void *); * @QDF_MODULE_ID_TARGET: Target module ID * @QDF_MODULE_ID_MBSSIE: MBSS IE ID * @QDF_MODULE_ID_FWOL: FW Offload module ID + * @QDF_MODULE_ID_SM_ENGINE: SM engine module ID * @QDF_MODULE_ID_ANY: anything * @QDF_MODULE_ID_MAX: Max place holder module ID */ @@ -467,6 +468,7 @@ typedef enum { QDF_MODULE_ID_TARGET, QDF_MODULE_ID_MBSSIE, QDF_MODULE_ID_FWOL, + QDF_MODULE_ID_SM_ENGINE, QDF_MODULE_ID_ANY, QDF_MODULE_ID_MAX, } QDF_MODULE_ID; diff --git a/qdf/linux/src/qdf_trace.c b/qdf/linux/src/qdf_trace.c index b74e91fd1eba..fbd5c6a8fea3 100644 --- a/qdf/linux/src/qdf_trace.c +++ b/qdf/linux/src/qdf_trace.c @@ -2834,6 +2834,7 @@ struct category_name_info g_qdf_category_name[MAX_SUPPORTED_CATEGORY] = { [QDF_MODULE_ID_TARGET] = {"TARGET"}, [QDF_MODULE_ID_MBSSIE] = {"MBSSIE"}, [QDF_MODULE_ID_FWOL] = {"fwol"}, + [QDF_MODULE_ID_SM_ENGINE] = {"SM_ENGINE"}, [QDF_MODULE_ID_ANY] = {"ANY"}, }; qdf_export_symbol(g_qdf_category_name); @@ -3283,6 +3284,7 @@ static void set_default_trace_levels(struct category_info *cinfo) [QDF_MODULE_ID_CP_STATS] = QDF_TRACE_LEVEL_ERROR, [QDF_MODULE_ID_MBSSIE] = QDF_TRACE_LEVEL_INFO, [QDF_MODULE_ID_FWOL] = QDF_TRACE_LEVEL_NONE, + [QDF_MODULE_ID_SM_ENGINE] = QDF_TRACE_LEVEL_DEBUG, [QDF_MODULE_ID_ANY] = QDF_TRACE_LEVEL_INFO, }; diff --git a/umac/cmn_services/sm_engine/inc/wlan_sm_engine.h b/umac/cmn_services/sm_engine/inc/wlan_sm_engine.h new file mode 100644 index 000000000000..501a9a7ee00c --- /dev/null +++ b/umac/cmn_services/sm_engine/inc/wlan_sm_engine.h @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Define general SM framework, The modules can implement state machine + * using framework, it provides framework for state, event, state transition, + * event handling + * + * The module, whoever implement SM using this framework need to define an array + * of structures (of type struct wlan_sm_state_info) as below, + * for example, sample state array definition + * + * struct wlan_sm_state_info sm_info[] = { + * { + * (uint8_t) WLAN_VDEV_S_INIT, + * (uint8_t) WLAN_SM_ENGINE_STATE_NONE, + * (uint8_t) WLAN_SM_ENGINE_STATE_NONE, + * true, + * "INIT", + * mlme_vdev_state_init_entry, --> gets invoked on entering the state + * mlme_vdev_state_init_exit, --> gets invoked before exiting the state + * mlme_vdev_state_init_event --> gets invoked on event dispatch + * }, + * { + * (uint8_t) WLAN_VDEV_S_START, + * (uint8_t) WLAN_SM_ENGINE_STATE_NONE, + * (uint8_t) WLAN_SM_ENGINE_STATE_NONE, + * true, + * "START", + * mlme_vdev_state_start_entry, + * mlme_vdev_state_start_exit, + * mlme_vdev_state_start_event + * }, + * { + * (uint8_t) WLAN_VDEV_S_DFS_CAC_WAIT, + * (uint8_t) WLAN_SM_ENGINE_STATE_NONE, + * (uint8_t) WLAN_SM_ENGINE_STATE_NONE, + * true, + * "DFS_CAC_WAIT", + * mlme_vdev_state_dfs_cac_wait_entry, + * mlme_vdev_state_dfs_cac_wait_exit, + * mlme_vdev_state_dfs_cac_wait_event + * }, + * ... + * }; + * + * Invoke wlan_sm_create() with this state array as below + * + * sm = wlan_sm_create("VDEV_SM", vdev_obj, WLAN_VDEV_S_INIT, sm_info, 3, + * event_names[], num_events); + * + * on successful creation, invoke below functions to dispatch events and state + * transition + * + * Event dispatch: + * wlan_sm_dispatch(sm, start_event_id, 0, NULL); + * + * State transition: + * wlan_sm_transition_to(sm, WLAN_VDEV_S_INIT); + * + * + */ +#ifndef _WLAN_SM_ENGINE_H_ +#define _WLAN_SM_ENGINE_H_ + +#include +#include +#include + +/* invalid state */ +#define WLAN_SM_ENGINE_STATE_NONE 255 +/* invalid event */ +#define WLAN_SM_ENGINE_EVENT_NONE 255 + +#define WLAN_SM_ENGINE_MAX_STATE_NAME 128 +#define WLAN_SM_ENGINE_MAX_NAME 64 +#define WLAN_SM_ENGINE_MAX_STATES 200 +#define WLAN_SM_ENGINE_MAX_EVENTS 200 + +/** + * struct wlan_sm_state_info - state structure definition + * @state: State id + * @parent_state: Parent state id (optional) + * @initial_substate: Initial sub state of this state + * @has_substates: flag to specify, whether it has sub states + * @name: Name of the state + * @wlan_sm_entry: State entry callback poitner + * @wlan_sm_exit: State exit callback poitner + * @wlan_sm_event: State event callback poitner + */ +struct wlan_sm_state_info { + uint8_t state; + uint8_t parent_state; + uint8_t initial_substate; + uint8_t has_substates; + const char *name; + void (*wlan_sm_entry) (void *ctx); + void (*wlan_sm_exit) (void *ctx); + bool (*wlan_sm_event) (void *ctx, uint16_t event, + uint16_t event_data_len, void *event_data); +}; + +/** + * struct wlan_sm - state machine structure + * @name: Name of the statemachine + * @cur_state: Current state (state/sub-state) + * @num_states: Number of states + * @last_event: Holds the last handled event of SM + * @state_info: Initialized States' table + * @ctx: Holds the caller's context + * @in_state_transition: Flag to check whether state transition is in progress + * @event_names: Array of event names + * @num_event_names: Number of event names + * @history: Holds the SM history pointer + */ +struct wlan_sm { + uint8_t name[WLAN_SM_ENGINE_MAX_NAME]; + uint8_t cur_state; + uint8_t num_states; + uint8_t last_event; + struct wlan_sm_state_info *state_info; + void *ctx; + qdf_atomic_t in_state_transition; + const char **event_names; + uint32_t num_event_names; +#ifdef SM_ENG_HIST_ENABLE + struct wlan_sm_history history; +#endif +}; + +#define WLAN_SM_ENGINE_ENTRY(name, state, parent, initsubstate, has_substates) \ + { state, parent, initsubstate, has_substates, \ + "##name", wlan_sm_##name_entry, wlan_sm_##name_exit,\ + wlan_sm_##name_event } + +/* + * flag definitions + */ +#define WLAN_SM_ENGINE_ASYNCHRONOUS 0x0 /* run SM asynchronously */ +#define WLAN_SM_ENGINE_SYNCHRONOUS 0x1 /* run SM synchronously */ + +/** + * wlan_sm_create() - SM create + * @name: Name of SM owner module + * @ctx: caller pointer, used on invoking callbacks + * @init_state: Default state of the SM + * @state_info: States' definitions + * @num_state: Number of states + * @event_names: Event name table + * @num_event_names: Number of events + * + * Creates SM object, intializes with init_state, stores the name and owner + * module pointer, states definition table, and event name table + * + * Return: Handle to struct wlan_sm on successful creation, + * NULL on Failure + */ +struct wlan_sm *wlan_sm_create(const char *name, void *ctx, + uint8_t init_state, + struct wlan_sm_state_info *state_info, + uint8_t num_states, + const char **event_names, + uint32_t num_event_names); + +/** + * wlan_sm_delete() - SM delete + * @sm: state machine handle + * + * Delete SM object + * + * Return: void + */ +void wlan_sm_delete(struct wlan_sm *sm); + +/** + * wlan_sm_dispatch() - API to notify event to SM + * @sm: state machine handle + * @event: event id + * @event_data_len: Size of event data + * @event_data: Event data + * + * Notifies event to SM, it invokes event callback of the current state of SM + * + * Return: QDF_STATUS_SUCCESS for handling + * QDF_STATUS_E_INVAL for not handling + */ +QDF_STATUS wlan_sm_dispatch(struct wlan_sm *sm, uint16_t event, + uint16_t event_data_len, void *event_data); + +/** + * wlan_sm_transition_to() - API to move the state of SM + * @sm: state machine handle + * @state: State id + * + * Moves the SM's state + * + * Return: void + */ +void wlan_sm_transition_to(struct wlan_sm *sm, uint8_t state); + +/** + * wlan_sm_get_lastevent() - API to get last dispatched event + * @sm: state machine handle + * + * Gets the last dispatched event + * + * Return: event id + */ +uint8_t wlan_sm_get_lastevent(struct wlan_sm *sm); + +/** + * wlan_sm_get_current_state() - API to get current state of SM + * @sm: state machine handle + * + * Gets the current state of SM + * + * Return: state id + */ +uint8_t wlan_sm_get_current_state(struct wlan_sm *sm); + +/** + * wlan_sm_get_current_state_name() - API to get current state's name of SM + * @sm: state machine handle + * + * Gets the current state name of SM + * + * Return: name of the state + */ +const char *wlan_sm_get_current_state_name(struct wlan_sm *sm); + +/** + * wlan_sm_get_state_name() - API to get state's name + * @sm: state machine handle + * @state: state id + * + * Gets the given state name of SM + * + * Return: name of the state + */ +const char *wlan_sm_get_state_name(struct wlan_sm *sm, uint8_t state); + +/** + * wlan_sm_reset() - API to reset SM state + * @sm: state machine handle + * @init_state: state to reset SM + * + * Resets the SM to given state + * + * Return: void + */ +void wlan_sm_reset(struct wlan_sm *sm, uint8_t init_state); + +#endif diff --git a/umac/cmn_services/sm_engine/inc/wlan_sm_engine_dbg.h b/umac/cmn_services/sm_engine/inc/wlan_sm_engine_dbg.h new file mode 100644 index 000000000000..5ecef9252fc9 --- /dev/null +++ b/umac/cmn_services/sm_engine/inc/wlan_sm_engine_dbg.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + /** + * DOC: Define the debug data structure of UMAC SM + */ +#ifndef _WLAN_SM_ENGINE_DBG_H_ +#define _WLAN_SM_ENGINE_DBG_H_ + +#include +#include + +#define sm_engine_alert(params...) \ + QDF_TRACE_FATAL(QDF_MODULE_ID_SM_ENGINE, params) + +#define sm_engine_err(params...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_SM_ENGINE, params) + +#define sm_engine_warn(params...) \ + QDF_TRACE_WARN(QDF_MODULE_ID_SM_ENGINE, params) + +#define sm_engine_info(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_SM_ENGINE, params) + +#define sm_engine_debug(params...) \ + QDF_TRACE_DEBUG(QDF_MODULE_ID_SM_ENGINE, params) + +#define WLAN_SM_ENGINE_HISTORY_SIZE 50 +struct wlan_sm; +/** + * enum wlan_sm_trace_type - history element type + * @SM_EVENT_STATE_TRANSITION - Represents state transition + * @SM_EVENT_MSG_PROCESSING - Represents event processing + */ +enum wlan_sm_trace_type { + SM_EVENT_STATE_TRANSITION = 1, + SM_EVENT_MSG_PROCESSING, +}; + +#ifdef SM_ENG_HIST_ENABLE + +/** + * struct wlan_sm_history_info - history element structure + * @trace_type: history element type + * @event_type: Type of the event + * @initial_state: Current state (state/sub-state) + * @final_state: New state + */ +struct wlan_sm_history_info { + enum wlan_sm_trace_type trace_type; + uint16_t event_type; + uint8_t initial_state; + uint8_t final_state; +}; + +/** + * struct wlan_sm_history - history structure + * @sm_history_lock: SM history lock + * @index: Last updated entry index + * @data: Histoy elements array + */ +struct wlan_sm_history { + qdf_spinlock_t sm_history_lock; + int index; + struct wlan_sm_history_info data[WLAN_SM_ENGINE_HISTORY_SIZE]; +}; + +/** + * wlan_sm_save_history() - API to save SM history + * @sm: state machine handle + * @trace_type: type of operation + * @initial_state: current state + * @final_state: Resultant state + * @event_type: Event id + * + * Stores the SM state transition and event processing + * + * Return: void + */ +void wlan_sm_save_history(struct wlan_sm *sm, + enum wlan_sm_trace_type trace_type, + uint8_t initial_state, uint8_t final_state, + uint16_t event_type); + +/** + * wlan_sm_history_init() - API to initialize SM history module + * @sm: state machine handle + * + * Initializes SM history module + * + * Return: void + */ +void wlan_sm_history_init(struct wlan_sm *sm); + +/** + * wlan_sm_history_delete() - API to delete SM history module + * @sm: state machine handle + * + * Deletes SM history module + * + * Return: void + */ +void wlan_sm_history_delete(struct wlan_sm *sm); + +/** + * wlan_sm_print_history() - API to print SM history + * @sm: state machine handle + * + * Prints SM history + * + * Return: void + */ +void wlan_sm_print_history(struct wlan_sm *sm); + +#else /* SM_ENG_HIST_ENABLE */ + +/** + * wlan_sm_save_history() - API to save SM history + * @sm: state machine handle + * @trace_type: type of operation + * @initial_state: current state + * @final_state: Resultant state + * @event_type: Event id + * + * Stores the SM state transition and event processing + * + * Return: void + */ +static inline void wlan_sm_save_history(struct wlan_sm *sm, + enum wlan_sm_trace_type trace_type, + uint8_t initial_state, + uint8_t final_state, + uint16_t event_type) +{ +} + +/** + * wlan_sm_history_init() - API to initialize SM history module + * @sm: state machine handle + * + * Initializes SM history module + * + * Return: void + */ +static inline void wlan_sm_history_init(struct wlan_sm *sm) +{ +} + +/** + * wlan_sm_history_delete() - API to delete SM history module + * @sm: state machine handle + * + * Deletes SM history module + * + * Return: void + */ +static inline void wlan_sm_history_delete(struct wlan_sm *sm) +{ +} + +/** + * wlan_sm_print_history() - API to print SM history + * @sm: state machine handle + * + * Prints SM history + * + * Return: void + */ +static inline void wlan_sm_print_history(struct wlan_sm *sm) +{ +} + +#endif /* SM_ENG_HIST_ENABLE */ +#endif /* _WLAN_SM_ENGINE_DBG_H_ */ diff --git a/umac/cmn_services/sm_engine/src/wlan_sm_engine.c b/umac/cmn_services/sm_engine/src/wlan_sm_engine.c new file mode 100644 index 000000000000..e2987138921e --- /dev/null +++ b/umac/cmn_services/sm_engine/src/wlan_sm_engine.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements general SM framework + */ + +#include "wlan_sm_engine.h" +#include "wlan_sm_engine_dbg.h" +#include +#include +#include + +QDF_STATUS wlan_sm_dispatch(struct wlan_sm *sm, uint16_t event, + uint16_t event_data_len, void *event_data) +{ + bool event_handled = false; + uint8_t state; + const char *event_name = NULL; + + if (!sm) { + sm_engine_err("SM is NULL"); + return QDF_STATUS_E_FAILURE; + } + + state = sm->cur_state; + + if (event == WLAN_SM_ENGINE_EVENT_NONE) { + sm_engine_err("%s: invalid event %d", sm->name, event); + return QDF_STATUS_E_FAILURE; + } + sm->last_event = event; + + wlan_sm_save_history(sm, SM_EVENT_MSG_PROCESSING, sm->cur_state, + sm->cur_state, event); + + if (sm->event_names) { + if (event < sm->num_event_names) + event_name = sm->event_names[event]; + + sm_engine_debug("%s: current state %s event %s[%d]", + sm->name, + sm->state_info[sm->cur_state].name, + event_name ? event_name : "UNKNOWN_EVENT", + event); + } else { + sm_engine_debug("%s: current state %s event %d", + sm->name, + sm->state_info[sm->cur_state].name, + event); + } + + if (state != WLAN_SM_ENGINE_STATE_NONE) { + event_handled = (*sm->state_info[state].wlan_sm_event) ( + sm->ctx, event, event_data_len, event_data); + if (!event_handled) { + sm_engine_err("%s: event %d not handled in state %s", + sm->name, event, + sm->state_info[sm->cur_state].name); + return QDF_STATUS_E_INVAL; + } + } + + return QDF_STATUS_SUCCESS; +} + +qdf_export_symbol(wlan_sm_dispatch); + +void wlan_sm_transition_to(struct wlan_sm *sm, uint8_t state) +{ + const struct wlan_sm_state_info *state_info = sm->state_info; + uint8_t new_state; + uint8_t old_state; + uint8_t new_sub_st; + uint8_t ol_sub_st; + uint8_t cur_state; + + if (!sm) { + sm_engine_err("SM is NULL"); + return; + } + + cur_state = sm->cur_state; + + /* cannot change state from state entry/exit routines */ + if (qdf_atomic_read(&sm->in_state_transition)) { + sm_engine_alert( + "%s: can not call state transition from entry/exit routines", + sm->name); + QDF_BUG(0); + return; + } + + qdf_atomic_set(&sm->in_state_transition, 1); + + wlan_sm_save_history(sm, SM_EVENT_STATE_TRANSITION, sm->cur_state, + state, 0xFF); + + if ((state == WLAN_SM_ENGINE_STATE_NONE) || + (state >= WLAN_SM_ENGINE_MAX_STATES) || + (state >= sm->num_states)) { + sm_engine_err( + "%s: to state %d needs to be a valid state current_state=%d", + sm->name, cur_state, state); + return; + } + + /* + * Here state and sub state are derived for debug printing only + * as SME keeps state and sub state as flat, to differentiate between + * state and substate, checks current state if it has parent state, + * the parent state is printed along with the sub state + */ + if (state_info[cur_state].parent_state != WLAN_SM_ENGINE_STATE_NONE) + old_state = state_info[cur_state].parent_state; + else + old_state = cur_state; + + if (state_info[state].parent_state != WLAN_SM_ENGINE_STATE_NONE) + new_state = state_info[state].parent_state; + else + new_state = state; + + if (state_info[cur_state].parent_state != WLAN_SM_ENGINE_STATE_NONE) + ol_sub_st = cur_state; + else + ol_sub_st = 0; + + if (state_info[state].parent_state != WLAN_SM_ENGINE_STATE_NONE) + new_sub_st = state; + else + new_sub_st = 0; + + sm_engine_debug("%s: transition(state) %s => %s", + sm->name, state_info[old_state].name, + state_info[new_state].name); + sm_engine_debug("%s: transition(sub_state) %s => %s", + sm->name, + ol_sub_st ? state_info[ol_sub_st].name : "IDLE", + new_sub_st ? state_info[new_sub_st].name : "IDLE"); + + /* + * call the exit function(s) of the current state hierarchy + * starting from substate. + */ + while (cur_state != WLAN_SM_ENGINE_STATE_NONE) { + if (state_info[cur_state].wlan_sm_exit) + state_info[cur_state].wlan_sm_exit(sm->ctx); + + cur_state = state_info[cur_state].parent_state; + } + + /* + * call the entry function(s) of the current state hierarchy + * starting from superstate. + */ + cur_state = state; + while (cur_state != WLAN_SM_ENGINE_STATE_NONE) { + if (state_info[cur_state].wlan_sm_entry) + state_info[cur_state].wlan_sm_entry(sm->ctx); + + sm->cur_state = cur_state; + cur_state = state_info[cur_state].initial_substate; + + if (cur_state != WLAN_SM_ENGINE_STATE_NONE) + sm_engine_debug("%s: Entering Initial sub state %s", + sm->name, state_info[cur_state].name); + } + qdf_atomic_set(&sm->in_state_transition, 0); +} + +qdf_export_symbol(wlan_sm_transition_to); + +void wlan_sm_reset(struct wlan_sm *sm, uint8_t init_state) +{ + sm->cur_state = init_state; +} + +static QDF_STATUS wlan_sm_validate_state_info(const char *name, + const struct wlan_sm_state_info *state_info, + uint8_t i) +{ + bool state_visited[WLAN_SM_ENGINE_MAX_STATES] = {false}; + uint8_t state, next_state; + /* + * make sure that the state definitions are in order + */ + if ((state_info[i].state >= WLAN_SM_ENGINE_MAX_STATES) || + (state_info[i].state != i)) { + sm_engine_err("%s: entry %d has invalid state %d", + name, i, state_info[i].state); + + return QDF_STATUS_E_FAILURE; + } + /* detect if there is any loop in the hierarichy */ + state = state_info[i].state; + while (state != WLAN_SM_ENGINE_STATE_NONE) { + if (state_visited[state]) { + sm_engine_err("%s: detected a loop with entry %d", + name, i); + return QDF_STATUS_E_FAILURE; + } + + state_visited[state] = true; + next_state = state_info[state].parent_state; + if (next_state != WLAN_SM_ENGINE_STATE_NONE) { + if (!state_info[next_state].has_substates) { + sm_engine_err( + "%s: state %d is marked as parent of %d but is not a super state", + name, next_state, state); + return QDF_STATUS_E_FAILURE; + } + } + state = next_state; + } + + return QDF_STATUS_SUCCESS; +} + +struct wlan_sm *wlan_sm_create(const char *name, void *ctx, + uint8_t init_state, + struct wlan_sm_state_info *state_info, + uint8_t num_states, + const char **event_names, + uint32_t num_event_names) +{ + struct wlan_sm *sm; + u_int32_t i; + + if (num_states > WLAN_SM_ENGINE_MAX_STATES) { + sm_engine_err("%s: Num states exceeded", name); + return NULL; + } + + /* + * validate the state_info table. + * the entries need to be valid and also + * need to be in order. + */ + for (i = 0; i < num_states; ++i) { + if (wlan_sm_validate_state_info(name, state_info, i) != + QDF_STATUS_SUCCESS) { + sm_engine_err("%s: states validation failed", name); + return NULL; + } + } + + sm = qdf_mem_malloc(sizeof(*sm)); + if (!sm) { + sm_engine_alert("%s: sm allocation failed", name); + return NULL; + } + + wlan_sm_history_init(sm); + + sm->cur_state = init_state; + sm->num_states = num_states; + sm->state_info = state_info; + sm->ctx = ctx; + sm->last_event = WLAN_SM_ENGINE_EVENT_NONE; + qdf_atomic_set(&sm->in_state_transition, 0); + sm->event_names = event_names; + sm->num_event_names = num_event_names; + + qdf_str_lcopy(sm->name, name, WLAN_SM_ENGINE_MAX_NAME); + + sm_engine_debug("%s: sm creation successful", name); + + return sm; +} + +qdf_export_symbol(wlan_sm_create); + +void wlan_sm_delete(struct wlan_sm *sm) +{ + wlan_sm_history_delete(sm); + qdf_mem_free(sm); +} + +qdf_export_symbol(wlan_sm_delete); + +uint8_t wlan_sm_get_lastevent(struct wlan_sm *sm) +{ + return sm->last_event; +} + +uint8_t wlan_sm_get_current_state(struct wlan_sm *sm) +{ + return sm->cur_state; +} + +qdf_export_symbol(wlan_sm_get_current_state); + +const char *wlan_sm_get_state_name(struct wlan_sm *sm, uint8_t state) +{ + return sm->state_info[state].name; +} + +const char *wlan_sm_get_current_state_name(struct wlan_sm *sm) +{ + return sm->state_info[sm->cur_state].name; +} + +qdf_export_symbol(wlan_sm_get_current_state_name); diff --git a/umac/cmn_services/sm_engine/src/wlan_sm_engine_dbg.c b/umac/cmn_services/sm_engine/src/wlan_sm_engine_dbg.c new file mode 100644 index 000000000000..cc4267e5a355 --- /dev/null +++ b/umac/cmn_services/sm_engine/src/wlan_sm_engine_dbg.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Implements general SM debug framework + */ +#include +#include + +void wlan_sm_save_history(struct wlan_sm *sm, + enum wlan_sm_trace_type trace_type, + uint8_t initial_state, uint8_t final_state, + uint16_t event_type) +{ + struct wlan_sm_history *p_sm_history = &sm->history; + struct wlan_sm_history_info *p_memento; + + /* + * History saved in circular buffer. + * Save a pointer to next write location and increment pointer. + */ + qdf_spin_lock_bh(&p_sm_history->sm_history_lock); + p_memento = &p_sm_history->data[p_sm_history->index]; + p_sm_history->index++; + + if (p_sm_history->index >= WLAN_SM_ENGINE_HISTORY_SIZE) + p_sm_history->index = 0; + + qdf_spin_unlock_bh(&p_sm_history->sm_history_lock); + + qdf_mem_zero(p_memento, sizeof(*p_memento)); + p_memento->trace_type = trace_type; + p_memento->initial_state = initial_state; + p_memento->final_state = final_state; + p_memento->event_type = event_type; +} + +void wlan_sm_history_init(struct wlan_sm *sm) +{ + qdf_spinlock_create(&sm->history.sm_history_lock); +} + +void wlan_sm_history_delete(struct wlan_sm *sm) +{ + qdf_spinlock_destroy(&sm->history.sm_history_lock); +} + +void wlan_sm_print_history(struct wlan_sm *sm) +{ + struct wlan_sm_history *p_sm_history = &sm->history; + struct wlan_sm_history_info *p_memento; + uint16_t i; + + /* + * History saved in circular buffer. + * Save a pointer to next write location and increment pointer. + */ + qdf_spin_lock_bh(&p_sm_history->sm_history_lock); + sm_engine_info(" SM History index is %d", p_sm_history->index); + for (i = 0; i < WLAN_SM_ENGINE_HISTORY_SIZE; i++) { + p_memento = &p_sm_history->data[i]; + sm_engine_info(" (%d) trace_type: %d, event: %d", i, + p_memento->trace_type, p_memento->event_type); + sm_engine_info(" (%d) initial state : %d, final state: %d", i, + p_memento->initial_state, + p_memento->final_state); + } + + qdf_spin_unlock_bh(&p_sm_history->sm_history_lock); +}