1 /* 2 * Copyright (c) 2018 The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /** 20 * DOC: Implements general SM framework 21 */ 22 23 #include "wlan_sm_engine.h" 24 #include "wlan_sm_engine_dbg.h" 25 #include <qdf_module.h> 26 #include <qdf_mem.h> 27 #include <qdf_str.h> 28 29 QDF_STATUS wlan_sm_dispatch(struct wlan_sm *sm, uint16_t event, 30 uint16_t event_data_len, void *event_data) 31 { 32 bool event_handled = false; 33 uint8_t state; 34 const char *event_name = NULL; 35 36 if (!sm) { 37 sm_engine_err("SM is NULL"); 38 return QDF_STATUS_E_FAILURE; 39 } 40 41 state = sm->cur_state; 42 43 if (event == WLAN_SM_ENGINE_EVENT_NONE) { 44 sm_engine_err("%s: invalid event %d", sm->name, event); 45 return QDF_STATUS_E_FAILURE; 46 } 47 sm->last_event = event; 48 49 wlan_sm_save_history(sm, SM_EVENT_MSG_PROCESSING, sm->cur_state, 50 sm->cur_state, event); 51 52 if (sm->event_names) { 53 if (event < sm->num_event_names) 54 event_name = sm->event_names[event]; 55 56 sm_engine_nofl_info("%s: current state %s[%d] event %s[%d]", 57 sm->name, sm->state_info[state].name, state, 58 event_name ? event_name : "UNKNOWN_EVENT", 59 event); 60 } else { 61 sm_engine_nofl_info("%s: current state %s[%d] event %d", 62 sm->name, 63 sm->state_info[state].name, state, 64 event); 65 } 66 67 if (state != WLAN_SM_ENGINE_STATE_NONE) { 68 event_handled = (*sm->state_info[state].wlan_sm_event) ( 69 sm->ctx, event, event_data_len, event_data); 70 if (!event_handled) { 71 sm_engine_nofl_err("%s: event %d not handled in state %s", 72 sm->name, event, 73 sm->state_info[sm->cur_state].name); 74 return QDF_STATUS_E_INVAL; 75 } 76 } 77 78 return QDF_STATUS_SUCCESS; 79 } 80 81 qdf_export_symbol(wlan_sm_dispatch); 82 83 void wlan_sm_transition_to(struct wlan_sm *sm, uint8_t state) 84 { 85 const struct wlan_sm_state_info *state_info = sm->state_info; 86 uint8_t new_state; 87 uint8_t old_state; 88 uint8_t new_sub_st; 89 uint8_t ol_sub_st; 90 uint8_t cur_state; 91 92 if (!sm) { 93 sm_engine_err("SM is NULL"); 94 return; 95 } 96 97 cur_state = sm->cur_state; 98 99 /* cannot change state from state entry/exit routines */ 100 if (qdf_atomic_read(&sm->in_state_transition)) { 101 sm_engine_alert( 102 "%s: can not call state transition from entry/exit routines", 103 sm->name); 104 QDF_BUG(0); 105 return; 106 } 107 108 qdf_atomic_set(&sm->in_state_transition, 1); 109 110 wlan_sm_save_history(sm, SM_EVENT_STATE_TRANSITION, sm->cur_state, 111 state, 0xFF); 112 113 if ((state == WLAN_SM_ENGINE_STATE_NONE) || 114 (state >= WLAN_SM_ENGINE_MAX_STATES) || 115 (state >= sm->num_states)) { 116 sm_engine_err( 117 "%s: to state %d needs to be a valid state current_state=%d", 118 sm->name, cur_state, state); 119 return; 120 } 121 122 /* 123 * Here state and sub state are derived for debug printing only 124 * as SME keeps state and sub state as flat, to differentiate between 125 * state and substate, checks current state if it has parent state, 126 * the parent state is printed along with the sub state 127 */ 128 if (state_info[cur_state].parent_state != WLAN_SM_ENGINE_STATE_NONE) 129 old_state = state_info[cur_state].parent_state; 130 else 131 old_state = cur_state; 132 133 if (state_info[state].parent_state != WLAN_SM_ENGINE_STATE_NONE) 134 new_state = state_info[state].parent_state; 135 else 136 new_state = state; 137 138 if (state_info[cur_state].parent_state != WLAN_SM_ENGINE_STATE_NONE) 139 ol_sub_st = cur_state; 140 else 141 ol_sub_st = 0; 142 143 if (state_info[state].parent_state != WLAN_SM_ENGINE_STATE_NONE) 144 new_sub_st = state; 145 else 146 new_sub_st = 0; 147 148 sm_engine_nofl_info("%s: transition(state) %s[%d] => %s[%d]", 149 sm->name, state_info[old_state].name, old_state, 150 state_info[new_state].name, new_state); 151 sm_engine_nofl_info("%s: transition(sub_state) %s[%d] => %s[%d]", 152 sm->name, 153 ol_sub_st ? state_info[ol_sub_st].name : "IDLE", 154 ol_sub_st, 155 new_sub_st ? state_info[new_sub_st].name : "IDLE", 156 new_sub_st); 157 158 /* 159 * call the exit function(s) of the current state hierarchy 160 * starting from substate. 161 */ 162 while (cur_state != WLAN_SM_ENGINE_STATE_NONE) { 163 if (state_info[cur_state].wlan_sm_exit) 164 state_info[cur_state].wlan_sm_exit(sm->ctx); 165 166 cur_state = state_info[cur_state].parent_state; 167 } 168 169 /* 170 * call the entry function(s) of the current state hierarchy 171 * starting from superstate. 172 */ 173 cur_state = state; 174 while (cur_state != WLAN_SM_ENGINE_STATE_NONE) { 175 if (state_info[cur_state].wlan_sm_entry) 176 state_info[cur_state].wlan_sm_entry(sm->ctx); 177 178 sm->cur_state = cur_state; 179 cur_state = state_info[cur_state].initial_substate; 180 181 if (cur_state != WLAN_SM_ENGINE_STATE_NONE) 182 sm_engine_debug("%s: Entering Initial sub state %s", 183 sm->name, state_info[cur_state].name); 184 } 185 qdf_atomic_set(&sm->in_state_transition, 0); 186 } 187 188 qdf_export_symbol(wlan_sm_transition_to); 189 190 void wlan_sm_reset(struct wlan_sm *sm, uint8_t init_state) 191 { 192 sm->cur_state = init_state; 193 } 194 195 static QDF_STATUS wlan_sm_validate_state_info(const char *name, 196 const struct wlan_sm_state_info *state_info, 197 uint8_t i) 198 { 199 bool state_visited[WLAN_SM_ENGINE_MAX_STATES] = {false}; 200 uint8_t state, next_state; 201 /* 202 * make sure that the state definitions are in order 203 */ 204 if ((state_info[i].state >= WLAN_SM_ENGINE_MAX_STATES) || 205 (state_info[i].state != i)) { 206 sm_engine_err("%s: entry %d has invalid state %d", 207 name, i, state_info[i].state); 208 209 return QDF_STATUS_E_FAILURE; 210 } 211 /* detect if there is any loop in the hierarichy */ 212 state = state_info[i].state; 213 while (state != WLAN_SM_ENGINE_STATE_NONE) { 214 if (state_visited[state]) { 215 sm_engine_err("%s: detected a loop with entry %d", 216 name, i); 217 return QDF_STATUS_E_FAILURE; 218 } 219 220 state_visited[state] = true; 221 next_state = state_info[state].parent_state; 222 if (next_state != WLAN_SM_ENGINE_STATE_NONE) { 223 if (!state_info[next_state].has_substates) { 224 sm_engine_err( 225 "%s: state %d is marked as parent of %d but is not a super state", 226 name, next_state, state); 227 return QDF_STATUS_E_FAILURE; 228 } 229 } 230 state = next_state; 231 } 232 233 return QDF_STATUS_SUCCESS; 234 } 235 236 struct wlan_sm *wlan_sm_create(const char *name, void *ctx, 237 uint8_t init_state, 238 struct wlan_sm_state_info *state_info, 239 uint8_t num_states, 240 const char **event_names, 241 uint32_t num_event_names) 242 { 243 struct wlan_sm *sm; 244 u_int32_t i; 245 246 if (num_states > WLAN_SM_ENGINE_MAX_STATES) { 247 sm_engine_err("%s: Num states exceeded", name); 248 return NULL; 249 } 250 251 /* 252 * validate the state_info table. 253 * the entries need to be valid and also 254 * need to be in order. 255 */ 256 for (i = 0; i < num_states; ++i) { 257 if (wlan_sm_validate_state_info(name, state_info, i) != 258 QDF_STATUS_SUCCESS) { 259 sm_engine_err("%s: states validation failed", name); 260 return NULL; 261 } 262 } 263 264 sm = qdf_mem_malloc(sizeof(*sm)); 265 if (!sm) { 266 sm_engine_alert("%s: sm allocation failed", name); 267 return NULL; 268 } 269 270 wlan_sm_history_init(sm); 271 272 sm->cur_state = init_state; 273 sm->num_states = num_states; 274 sm->state_info = state_info; 275 sm->ctx = ctx; 276 sm->last_event = WLAN_SM_ENGINE_EVENT_NONE; 277 qdf_atomic_set(&sm->in_state_transition, 0); 278 sm->event_names = event_names; 279 sm->num_event_names = num_event_names; 280 281 qdf_str_lcopy(sm->name, name, WLAN_SM_ENGINE_MAX_NAME); 282 283 sm_engine_debug("%s: sm creation successful", name); 284 285 return sm; 286 } 287 288 qdf_export_symbol(wlan_sm_create); 289 290 void wlan_sm_delete(struct wlan_sm *sm) 291 { 292 wlan_sm_history_delete(sm); 293 qdf_mem_free(sm); 294 } 295 296 qdf_export_symbol(wlan_sm_delete); 297 298 uint8_t wlan_sm_get_lastevent(struct wlan_sm *sm) 299 { 300 return sm->last_event; 301 } 302 303 uint8_t wlan_sm_get_current_state(struct wlan_sm *sm) 304 { 305 return sm->cur_state; 306 } 307 308 qdf_export_symbol(wlan_sm_get_current_state); 309 310 const char *wlan_sm_get_state_name(struct wlan_sm *sm, uint8_t state) 311 { 312 return sm->state_info[state].name; 313 } 314 315 const char *wlan_sm_get_current_state_name(struct wlan_sm *sm) 316 { 317 return sm->state_info[sm->cur_state].name; 318 } 319 320 qdf_export_symbol(wlan_sm_get_current_state_name); 321