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_debug("%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_debug("%s: current state %s[%d] event %d", 62 sm->name, sm->state_info[state].name, state, 63 event); 64 } 65 66 if (state != WLAN_SM_ENGINE_STATE_NONE) { 67 event_handled = (*sm->state_info[state].wlan_sm_event) ( 68 sm->ctx, event, event_data_len, event_data); 69 if (!event_handled) { 70 sm_engine_nofl_err("%s: event %d not handled in state %s", 71 sm->name, event, 72 sm->state_info[sm->cur_state].name); 73 return QDF_STATUS_E_INVAL; 74 } 75 } 76 77 return QDF_STATUS_SUCCESS; 78 } 79 80 qdf_export_symbol(wlan_sm_dispatch); 81 82 void wlan_sm_transition_to(struct wlan_sm *sm, uint8_t state) 83 { 84 const struct wlan_sm_state_info *state_info = sm->state_info; 85 uint8_t new_state; 86 uint8_t old_state; 87 uint8_t new_sub_st; 88 uint8_t ol_sub_st; 89 uint8_t cur_state; 90 91 if (!sm) { 92 sm_engine_err("SM is NULL"); 93 return; 94 } 95 96 cur_state = sm->cur_state; 97 98 /* cannot change state from state entry/exit routines */ 99 if (qdf_atomic_read(&sm->in_state_transition)) { 100 sm_engine_alert( 101 "%s: can not call state transition from entry/exit routines", 102 sm->name); 103 QDF_BUG(0); 104 return; 105 } 106 107 qdf_atomic_set(&sm->in_state_transition, 1); 108 109 wlan_sm_save_history(sm, SM_EVENT_STATE_TRANSITION, sm->cur_state, 110 state, 0xFF); 111 112 if ((state == WLAN_SM_ENGINE_STATE_NONE) || 113 (state >= WLAN_SM_ENGINE_MAX_STATES) || 114 (state >= sm->num_states)) { 115 sm_engine_err( 116 "%s: to state %d needs to be a valid state current_state=%d", 117 sm->name, cur_state, state); 118 return; 119 } 120 121 /* 122 * Here state and sub state are derived for debug printing only 123 * as SME keeps state and sub state as flat, to differentiate between 124 * state and substate, checks current state if it has parent state, 125 * the parent state is printed along with the sub state 126 */ 127 if (state_info[cur_state].parent_state != WLAN_SM_ENGINE_STATE_NONE) 128 old_state = state_info[cur_state].parent_state; 129 else 130 old_state = cur_state; 131 132 if (state_info[state].parent_state != WLAN_SM_ENGINE_STATE_NONE) 133 new_state = state_info[state].parent_state; 134 else 135 new_state = state; 136 137 if (state_info[cur_state].parent_state != WLAN_SM_ENGINE_STATE_NONE) 138 ol_sub_st = cur_state; 139 else 140 ol_sub_st = 0; 141 142 if (state_info[state].parent_state != WLAN_SM_ENGINE_STATE_NONE) 143 new_sub_st = state; 144 else 145 new_sub_st = 0; 146 147 sm_engine_debug("%s: transition(state) %s[%d] => %s[%d]", 148 sm->name, state_info[old_state].name, old_state, 149 state_info[new_state].name, new_state); 150 sm_engine_debug("%s: transition(sub_state) %s[%d] => %s[%d]", 151 sm->name, 152 ol_sub_st ? state_info[ol_sub_st].name : "IDLE", 153 ol_sub_st, 154 new_sub_st ? state_info[new_sub_st].name : "IDLE", 155 new_sub_st); 156 157 /* 158 * call the exit function(s) of the current state hierarchy 159 * starting from substate. 160 */ 161 while (cur_state != WLAN_SM_ENGINE_STATE_NONE) { 162 if (state_info[cur_state].wlan_sm_exit) 163 state_info[cur_state].wlan_sm_exit(sm->ctx); 164 165 cur_state = state_info[cur_state].parent_state; 166 } 167 168 /* 169 * call the entry function(s) of the current state hierarchy 170 * starting from superstate. 171 */ 172 cur_state = state; 173 while (cur_state != WLAN_SM_ENGINE_STATE_NONE) { 174 if (state_info[cur_state].wlan_sm_entry) 175 state_info[cur_state].wlan_sm_entry(sm->ctx); 176 177 sm->cur_state = cur_state; 178 cur_state = state_info[cur_state].initial_substate; 179 180 if (cur_state != WLAN_SM_ENGINE_STATE_NONE) 181 sm_engine_debug("%s: Entering Initial sub state %s", 182 sm->name, state_info[cur_state].name); 183 } 184 qdf_atomic_set(&sm->in_state_transition, 0); 185 } 186 187 qdf_export_symbol(wlan_sm_transition_to); 188 189 void wlan_sm_reset(struct wlan_sm *sm, uint8_t init_state) 190 { 191 sm->cur_state = init_state; 192 } 193 194 static QDF_STATUS wlan_sm_validate_state_info(const char *name, 195 const struct wlan_sm_state_info *state_info, 196 uint8_t i) 197 { 198 bool state_visited[WLAN_SM_ENGINE_MAX_STATES] = {false}; 199 uint8_t state, next_state; 200 /* 201 * make sure that the state definitions are in order 202 */ 203 if ((state_info[i].state >= WLAN_SM_ENGINE_MAX_STATES) || 204 (state_info[i].state != i)) { 205 sm_engine_err("%s: entry %d has invalid state %d", 206 name, i, state_info[i].state); 207 208 return QDF_STATUS_E_FAILURE; 209 } 210 /* detect if there is any loop in the hierarichy */ 211 state = state_info[i].state; 212 while (state != WLAN_SM_ENGINE_STATE_NONE) { 213 if (state_visited[state]) { 214 sm_engine_err("%s: detected a loop with entry %d", 215 name, i); 216 return QDF_STATUS_E_FAILURE; 217 } 218 219 state_visited[state] = true; 220 next_state = state_info[state].parent_state; 221 if (next_state != WLAN_SM_ENGINE_STATE_NONE) { 222 if (!state_info[next_state].has_substates) { 223 sm_engine_err( 224 "%s: state %d is marked as parent of %d but is not a super state", 225 name, next_state, state); 226 return QDF_STATUS_E_FAILURE; 227 } 228 } 229 state = next_state; 230 } 231 232 return QDF_STATUS_SUCCESS; 233 } 234 235 struct wlan_sm *wlan_sm_create(const char *name, void *ctx, 236 uint8_t init_state, 237 struct wlan_sm_state_info *state_info, 238 uint8_t num_states, 239 const char **event_names, 240 uint32_t num_event_names) 241 { 242 struct wlan_sm *sm; 243 u_int32_t i; 244 245 if (num_states > WLAN_SM_ENGINE_MAX_STATES) { 246 sm_engine_err("%s: Num states exceeded", name); 247 return NULL; 248 } 249 250 /* 251 * validate the state_info table. 252 * the entries need to be valid and also 253 * need to be in order. 254 */ 255 for (i = 0; i < num_states; ++i) { 256 if (wlan_sm_validate_state_info(name, state_info, i) != 257 QDF_STATUS_SUCCESS) { 258 sm_engine_err("%s: states validation failed", name); 259 return NULL; 260 } 261 } 262 263 sm = qdf_mem_malloc(sizeof(*sm)); 264 if (!sm) { 265 sm_engine_alert("%s: sm allocation failed", name); 266 return NULL; 267 } 268 269 wlan_sm_history_init(sm); 270 271 sm->cur_state = init_state; 272 sm->num_states = num_states; 273 sm->state_info = state_info; 274 sm->ctx = ctx; 275 sm->last_event = WLAN_SM_ENGINE_EVENT_NONE; 276 qdf_atomic_set(&sm->in_state_transition, 0); 277 sm->event_names = event_names; 278 sm->num_event_names = num_event_names; 279 280 qdf_str_lcopy(sm->name, name, WLAN_SM_ENGINE_MAX_NAME); 281 282 sm_engine_debug("%s: sm creation successful", name); 283 284 return sm; 285 } 286 287 qdf_export_symbol(wlan_sm_create); 288 289 void wlan_sm_delete(struct wlan_sm *sm) 290 { 291 wlan_sm_history_delete(sm); 292 qdf_mem_free(sm); 293 } 294 295 qdf_export_symbol(wlan_sm_delete); 296 297 uint8_t wlan_sm_get_lastevent(struct wlan_sm *sm) 298 { 299 return sm->last_event; 300 } 301 302 uint8_t wlan_sm_get_current_state(struct wlan_sm *sm) 303 { 304 return sm->cur_state; 305 } 306 307 qdf_export_symbol(wlan_sm_get_current_state); 308 309 const char *wlan_sm_get_state_name(struct wlan_sm *sm, uint8_t state) 310 { 311 return sm->state_info[state].name; 312 } 313 314 const char *wlan_sm_get_current_state_name(struct wlan_sm *sm) 315 { 316 return sm->state_info[sm->cur_state].name; 317 } 318 319 qdf_export_symbol(wlan_sm_get_current_state_name); 320