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
wlan_sm_dispatch(struct wlan_sm * sm,uint16_t event,uint16_t event_data_len,void * event_data)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
wlan_sm_transition_to(struct wlan_sm * sm,uint8_t state)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
wlan_sm_reset(struct wlan_sm * sm,uint8_t init_state)185 void wlan_sm_reset(struct wlan_sm *sm, uint8_t init_state)
186 {
187 sm->cur_state = init_state;
188 }
189
wlan_sm_validate_state_info(const char * name,const struct wlan_sm_state_info * state_info,uint8_t i)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
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)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
wlan_sm_delete(struct wlan_sm * sm)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
wlan_sm_get_lastevent(struct wlan_sm * sm)291 uint8_t wlan_sm_get_lastevent(struct wlan_sm *sm)
292 {
293 return sm->last_event;
294 }
295
wlan_sm_get_current_state(struct wlan_sm * sm)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
wlan_sm_get_state_name(struct wlan_sm * sm,uint8_t state)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
wlan_sm_get_current_state_name(struct wlan_sm * sm)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