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