xref: /wlan-dirver/qca-wifi-host-cmn/umac/cmn_services/sm_engine/src/wlan_sm_engine.c (revision dae10a5fbc53d54c53c4ba24fa018ad8b1e7c008)
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