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