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