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
This commit is contained in:
Srinivas Pitla 2018-06-07 16:01:04 +05:30 • committed by nshrivas
commit bc7748c852
6 changed files with 864 additions and 0 deletions

View file

@ -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;

View file

@ -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,
};

View file

@ -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 <qdf_types.h>
#include <qdf_atomic.h>
#include <wlan_sm_engine_dbg.h>
/* 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

View file

@ -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 <qdf_types.h>
#include <qdf_trace.h>
#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_ */

View file

@ -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 <qdf_module.h>
#include <qdf_mem.h>
#include <qdf_str.h>
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);

View file

@ -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 <wlan_sm_engine.h>
#include <wlan_sm_engine_dbg.h>
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);
}