xref: /wlan-driver/qca-wifi-host-cmn/umac/cmn_services/sm_engine/src/wlan_sm_engine.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1*5113495bSYour Name /*
2*5113495bSYour Name  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
3*5113495bSYour Name  *
4*5113495bSYour Name  * Permission to use, copy, modify, and/or distribute this software for
5*5113495bSYour Name  * any purpose with or without fee is hereby granted, provided that the
6*5113495bSYour Name  * above copyright notice and this permission notice appear in all
7*5113495bSYour Name  * copies.
8*5113495bSYour Name  *
9*5113495bSYour Name  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10*5113495bSYour Name  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11*5113495bSYour Name  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12*5113495bSYour Name  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13*5113495bSYour Name  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14*5113495bSYour Name  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15*5113495bSYour Name  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16*5113495bSYour Name  * PERFORMANCE OF THIS SOFTWARE.
17*5113495bSYour Name  */
18*5113495bSYour Name 
19*5113495bSYour Name /**
20*5113495bSYour Name  * DOC: Implements general SM framework
21*5113495bSYour Name  */
22*5113495bSYour Name 
23*5113495bSYour Name #include "wlan_sm_engine.h"
24*5113495bSYour Name #include "wlan_sm_engine_dbg.h"
25*5113495bSYour Name #include <qdf_module.h>
26*5113495bSYour Name #include <qdf_mem.h>
27*5113495bSYour Name #include <qdf_str.h>
28*5113495bSYour Name 
wlan_sm_dispatch(struct wlan_sm * sm,uint16_t event,uint16_t event_data_len,void * event_data)29*5113495bSYour Name QDF_STATUS wlan_sm_dispatch(struct wlan_sm *sm, uint16_t event,
30*5113495bSYour Name 			    uint16_t event_data_len, void *event_data)
31*5113495bSYour Name {
32*5113495bSYour Name 	bool event_handled = false;
33*5113495bSYour Name 	uint8_t state;
34*5113495bSYour Name 	const char *event_name = NULL;
35*5113495bSYour Name 
36*5113495bSYour Name 	if (!sm) {
37*5113495bSYour Name 		sm_engine_err("SM is NULL");
38*5113495bSYour Name 		return QDF_STATUS_E_FAILURE;
39*5113495bSYour Name 	}
40*5113495bSYour Name 
41*5113495bSYour Name 	state = sm->cur_state;
42*5113495bSYour Name 
43*5113495bSYour Name 	if (event == WLAN_SM_ENGINE_EVENT_NONE) {
44*5113495bSYour Name 		sm_engine_err("%s: invalid event %d", sm->name, event);
45*5113495bSYour Name 		return QDF_STATUS_E_FAILURE;
46*5113495bSYour Name 	}
47*5113495bSYour Name 	sm->last_event = event;
48*5113495bSYour Name 
49*5113495bSYour Name 	wlan_sm_save_history(sm, SM_EVENT_MSG_PROCESSING, sm->cur_state,
50*5113495bSYour Name 			     sm->cur_state, event);
51*5113495bSYour Name 
52*5113495bSYour Name 	if (sm->event_names) {
53*5113495bSYour Name 		if (event < sm->num_event_names)
54*5113495bSYour Name 			event_name = sm->event_names[event];
55*5113495bSYour Name 
56*5113495bSYour Name 		sm_engine_nofl_debug("%s: %s, %s", sm->name,
57*5113495bSYour Name 				     sm->state_info[state].name,
58*5113495bSYour Name 				     event_name ? event_name : "UNKNOWN_EVENT");
59*5113495bSYour Name 	} else {
60*5113495bSYour Name 		sm_engine_nofl_debug("%s: %s ev [%d]", sm->name,
61*5113495bSYour Name 				     sm->state_info[state].name, event);
62*5113495bSYour Name 	}
63*5113495bSYour Name 
64*5113495bSYour Name 	if (state != WLAN_SM_ENGINE_STATE_NONE) {
65*5113495bSYour Name 		event_handled = (*sm->state_info[state].wlan_sm_event) (
66*5113495bSYour Name 				 sm->ctx, event, event_data_len, event_data);
67*5113495bSYour Name 		if (!event_handled) {
68*5113495bSYour Name 			sm_engine_nofl_info("%s: event %d not handled in state %s",
69*5113495bSYour Name 					    sm->name, event,
70*5113495bSYour Name 					    sm->state_info[sm->cur_state].name);
71*5113495bSYour Name 			return QDF_STATUS_E_INVAL;
72*5113495bSYour Name 		}
73*5113495bSYour Name 	}
74*5113495bSYour Name 
75*5113495bSYour Name 	return QDF_STATUS_SUCCESS;
76*5113495bSYour Name }
77*5113495bSYour Name 
78*5113495bSYour Name qdf_export_symbol(wlan_sm_dispatch);
79*5113495bSYour Name 
wlan_sm_transition_to(struct wlan_sm * sm,uint8_t state)80*5113495bSYour Name void wlan_sm_transition_to(struct wlan_sm *sm, uint8_t state)
81*5113495bSYour Name {
82*5113495bSYour Name 	struct wlan_sm_state_info *state_info;
83*5113495bSYour Name 	uint8_t new_state;
84*5113495bSYour Name 	uint8_t old_state;
85*5113495bSYour Name 	uint8_t new_sub_st;
86*5113495bSYour Name 	uint8_t ol_sub_st;
87*5113495bSYour Name 	uint8_t cur_state;
88*5113495bSYour Name 
89*5113495bSYour Name 	if (!sm) {
90*5113495bSYour Name 		sm_engine_err("SM is NULL");
91*5113495bSYour Name 		return;
92*5113495bSYour Name 	}
93*5113495bSYour Name 
94*5113495bSYour Name 	state_info = sm->state_info;
95*5113495bSYour Name 	cur_state = sm->cur_state;
96*5113495bSYour Name 
97*5113495bSYour Name 	/* cannot change state from state entry/exit routines */
98*5113495bSYour Name 	if (qdf_atomic_read(&sm->in_state_transition)) {
99*5113495bSYour Name 		sm_engine_alert(
100*5113495bSYour Name 			"%s: can not call state transition from entry/exit routines",
101*5113495bSYour Name 					sm->name);
102*5113495bSYour Name 		QDF_BUG(0);
103*5113495bSYour Name 		return;
104*5113495bSYour Name 	}
105*5113495bSYour Name 
106*5113495bSYour Name 	qdf_atomic_set(&sm->in_state_transition, 1);
107*5113495bSYour Name 
108*5113495bSYour Name 	wlan_sm_save_history(sm, SM_EVENT_STATE_TRANSITION, sm->cur_state,
109*5113495bSYour Name 			     state, 0xFF);
110*5113495bSYour Name 
111*5113495bSYour Name 	if ((state == WLAN_SM_ENGINE_STATE_NONE) ||
112*5113495bSYour Name 	    (state >= WLAN_SM_ENGINE_MAX_STATES) ||
113*5113495bSYour Name 	    (state >= sm->num_states)) {
114*5113495bSYour Name 		sm_engine_err(
115*5113495bSYour Name 			"%s: to state %d needs to be a valid state current_state=%d",
116*5113495bSYour Name 					sm->name, cur_state, state);
117*5113495bSYour Name 		return;
118*5113495bSYour Name 	}
119*5113495bSYour Name 
120*5113495bSYour Name 	/*
121*5113495bSYour Name 	 * Here state and sub state are derived for debug printing only
122*5113495bSYour Name 	 * as SME keeps state and sub state as flat, to differentiate between
123*5113495bSYour Name 	 * state and substate, checks current state if it has parent state,
124*5113495bSYour Name 	 * the parent state is printed along with the sub state
125*5113495bSYour Name 	 */
126*5113495bSYour Name 	if (state_info[cur_state].parent_state != WLAN_SM_ENGINE_STATE_NONE)
127*5113495bSYour Name 		old_state = state_info[cur_state].parent_state;
128*5113495bSYour Name 	else
129*5113495bSYour Name 		old_state = cur_state;
130*5113495bSYour Name 
131*5113495bSYour Name 	if (state_info[state].parent_state != WLAN_SM_ENGINE_STATE_NONE)
132*5113495bSYour Name 		new_state = state_info[state].parent_state;
133*5113495bSYour Name 	else
134*5113495bSYour Name 		new_state = state;
135*5113495bSYour Name 
136*5113495bSYour Name 	if (state_info[cur_state].parent_state != WLAN_SM_ENGINE_STATE_NONE)
137*5113495bSYour Name 		ol_sub_st = cur_state;
138*5113495bSYour Name 	else
139*5113495bSYour Name 		ol_sub_st = 0;
140*5113495bSYour Name 
141*5113495bSYour Name 	if (state_info[state].parent_state != WLAN_SM_ENGINE_STATE_NONE)
142*5113495bSYour Name 		new_sub_st = state;
143*5113495bSYour Name 	else
144*5113495bSYour Name 		new_sub_st = 0;
145*5113495bSYour Name 
146*5113495bSYour Name 	sm_engine_nofl_debug("%s: %s > %s, %s > %s", sm->name,
147*5113495bSYour Name 			     state_info[old_state].name,
148*5113495bSYour Name 			     state_info[new_state].name,
149*5113495bSYour Name 			     ol_sub_st ? state_info[ol_sub_st].name : "IDLE",
150*5113495bSYour Name 			     new_sub_st ? state_info[new_sub_st].name : "IDLE");
151*5113495bSYour Name 
152*5113495bSYour Name 	/*
153*5113495bSYour Name 	 * call the exit function(s) of the current state hierarchy
154*5113495bSYour Name 	 * starting from substate.
155*5113495bSYour Name 	 */
156*5113495bSYour Name 	while (cur_state != WLAN_SM_ENGINE_STATE_NONE) {
157*5113495bSYour Name 		if (state_info[cur_state].wlan_sm_exit)
158*5113495bSYour Name 			state_info[cur_state].wlan_sm_exit(sm->ctx);
159*5113495bSYour Name 
160*5113495bSYour Name 		cur_state = state_info[cur_state].parent_state;
161*5113495bSYour Name 	}
162*5113495bSYour Name 
163*5113495bSYour Name 	/*
164*5113495bSYour Name 	 * call the entry function(s) of the current state hierarchy
165*5113495bSYour Name 	 * starting from superstate.
166*5113495bSYour Name 	 */
167*5113495bSYour Name 	cur_state = state;
168*5113495bSYour Name 	while (cur_state != WLAN_SM_ENGINE_STATE_NONE) {
169*5113495bSYour Name 		if (state_info[cur_state].wlan_sm_entry)
170*5113495bSYour Name 			state_info[cur_state].wlan_sm_entry(sm->ctx);
171*5113495bSYour Name 
172*5113495bSYour Name 		sm->cur_state = cur_state;
173*5113495bSYour Name 		cur_state = state_info[cur_state].initial_substate;
174*5113495bSYour Name 
175*5113495bSYour Name 		if (cur_state != WLAN_SM_ENGINE_STATE_NONE)
176*5113495bSYour Name 			sm_engine_nofl_debug("%s: Initial sub state %s",
177*5113495bSYour Name 					     sm->name,
178*5113495bSYour Name 					     state_info[cur_state].name);
179*5113495bSYour Name 	}
180*5113495bSYour Name 	qdf_atomic_set(&sm->in_state_transition, 0);
181*5113495bSYour Name }
182*5113495bSYour Name 
183*5113495bSYour Name qdf_export_symbol(wlan_sm_transition_to);
184*5113495bSYour Name 
wlan_sm_reset(struct wlan_sm * sm,uint8_t init_state)185*5113495bSYour Name void wlan_sm_reset(struct wlan_sm *sm, uint8_t init_state)
186*5113495bSYour Name {
187*5113495bSYour Name 	sm->cur_state = init_state;
188*5113495bSYour Name }
189*5113495bSYour Name 
wlan_sm_validate_state_info(const char * name,const struct wlan_sm_state_info * state_info,uint8_t i)190*5113495bSYour Name static QDF_STATUS wlan_sm_validate_state_info(const char *name,
191*5113495bSYour Name 				const struct wlan_sm_state_info *state_info,
192*5113495bSYour Name 				uint8_t i)
193*5113495bSYour Name {
194*5113495bSYour Name 	bool state_visited[WLAN_SM_ENGINE_MAX_STATES] = {false};
195*5113495bSYour Name 	uint8_t state, next_state;
196*5113495bSYour Name 	/*
197*5113495bSYour Name 	 * make sure that the state definitions are in order
198*5113495bSYour Name 	 */
199*5113495bSYour Name 	if ((state_info[i].state >= WLAN_SM_ENGINE_MAX_STATES) ||
200*5113495bSYour Name 	    (state_info[i].state != i)) {
201*5113495bSYour Name 		sm_engine_err("%s: entry %d has invalid state %d",
202*5113495bSYour Name 			      name, i, state_info[i].state);
203*5113495bSYour Name 
204*5113495bSYour Name 		return QDF_STATUS_E_FAILURE;
205*5113495bSYour Name 	}
206*5113495bSYour Name 	/* detect if there is any loop in the hierarichy */
207*5113495bSYour Name 	state = state_info[i].state;
208*5113495bSYour Name 	while (state != WLAN_SM_ENGINE_STATE_NONE) {
209*5113495bSYour Name 		if (state_visited[state]) {
210*5113495bSYour Name 			sm_engine_err("%s: detected a loop with entry %d",
211*5113495bSYour Name 				      name, i);
212*5113495bSYour Name 			return QDF_STATUS_E_FAILURE;
213*5113495bSYour Name 		}
214*5113495bSYour Name 
215*5113495bSYour Name 		state_visited[state] = true;
216*5113495bSYour Name 		next_state = state_info[state].parent_state;
217*5113495bSYour Name 		if (next_state != WLAN_SM_ENGINE_STATE_NONE) {
218*5113495bSYour Name 			if (!state_info[next_state].has_substates) {
219*5113495bSYour Name 				sm_engine_err(
220*5113495bSYour Name 					"%s: state %d is marked as parent of %d but is not a super state",
221*5113495bSYour Name 						name, next_state, state);
222*5113495bSYour Name 				return QDF_STATUS_E_FAILURE;
223*5113495bSYour Name 			}
224*5113495bSYour Name 		}
225*5113495bSYour Name 		state = next_state;
226*5113495bSYour Name 	}
227*5113495bSYour Name 
228*5113495bSYour Name 	return QDF_STATUS_SUCCESS;
229*5113495bSYour Name }
230*5113495bSYour Name 
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*5113495bSYour Name struct wlan_sm *wlan_sm_create(const char *name, void *ctx,
232*5113495bSYour Name 			       uint8_t init_state,
233*5113495bSYour Name 			       struct wlan_sm_state_info *state_info,
234*5113495bSYour Name 			       uint8_t num_states,
235*5113495bSYour Name 			       const char **event_names,
236*5113495bSYour Name 			       uint32_t num_event_names)
237*5113495bSYour Name {
238*5113495bSYour Name 	struct wlan_sm *sm;
239*5113495bSYour Name 	u_int32_t i;
240*5113495bSYour Name 
241*5113495bSYour Name 	if (num_states > WLAN_SM_ENGINE_MAX_STATES) {
242*5113495bSYour Name 		sm_engine_err("%s: Num states exceeded", name);
243*5113495bSYour Name 		return NULL;
244*5113495bSYour Name 	}
245*5113495bSYour Name 
246*5113495bSYour Name 	/*
247*5113495bSYour Name 	 * validate the state_info table.
248*5113495bSYour Name 	 * the entries need to be valid and also
249*5113495bSYour Name 	 * need to be in order.
250*5113495bSYour Name 	 */
251*5113495bSYour Name 	for (i = 0; i < num_states; ++i) {
252*5113495bSYour Name 		if (wlan_sm_validate_state_info(name, state_info, i) !=
253*5113495bSYour Name 				QDF_STATUS_SUCCESS) {
254*5113495bSYour Name 			sm_engine_err("%s: states validation failed", name);
255*5113495bSYour Name 			return NULL;
256*5113495bSYour Name 		}
257*5113495bSYour Name 	}
258*5113495bSYour Name 
259*5113495bSYour Name 	sm = qdf_mem_malloc(sizeof(*sm));
260*5113495bSYour Name 	if (!sm)
261*5113495bSYour Name 		return NULL;
262*5113495bSYour Name 
263*5113495bSYour Name 	wlan_sm_history_init(sm);
264*5113495bSYour Name 
265*5113495bSYour Name 	sm->cur_state = init_state;
266*5113495bSYour Name 	sm->num_states = num_states;
267*5113495bSYour Name 	sm->state_info = state_info;
268*5113495bSYour Name 	sm->ctx = ctx;
269*5113495bSYour Name 	sm->last_event = WLAN_SM_ENGINE_EVENT_NONE;
270*5113495bSYour Name 	qdf_atomic_set(&sm->in_state_transition, 0);
271*5113495bSYour Name 	sm->event_names = event_names;
272*5113495bSYour Name 	sm->num_event_names = num_event_names;
273*5113495bSYour Name 
274*5113495bSYour Name 	qdf_str_lcopy(sm->name, name, WLAN_SM_ENGINE_MAX_NAME);
275*5113495bSYour Name 
276*5113495bSYour Name 	sm_engine_debug("%s: sm creation successful", name);
277*5113495bSYour Name 
278*5113495bSYour Name 	return sm;
279*5113495bSYour Name }
280*5113495bSYour Name 
281*5113495bSYour Name qdf_export_symbol(wlan_sm_create);
282*5113495bSYour Name 
wlan_sm_delete(struct wlan_sm * sm)283*5113495bSYour Name void wlan_sm_delete(struct wlan_sm *sm)
284*5113495bSYour Name {
285*5113495bSYour Name 	wlan_sm_history_delete(sm);
286*5113495bSYour Name 	qdf_mem_free(sm);
287*5113495bSYour Name }
288*5113495bSYour Name 
289*5113495bSYour Name qdf_export_symbol(wlan_sm_delete);
290*5113495bSYour Name 
wlan_sm_get_lastevent(struct wlan_sm * sm)291*5113495bSYour Name uint8_t wlan_sm_get_lastevent(struct wlan_sm *sm)
292*5113495bSYour Name {
293*5113495bSYour Name 	return sm->last_event;
294*5113495bSYour Name }
295*5113495bSYour Name 
wlan_sm_get_current_state(struct wlan_sm * sm)296*5113495bSYour Name uint8_t wlan_sm_get_current_state(struct wlan_sm *sm)
297*5113495bSYour Name {
298*5113495bSYour Name 	return sm->cur_state;
299*5113495bSYour Name }
300*5113495bSYour Name 
301*5113495bSYour Name qdf_export_symbol(wlan_sm_get_current_state);
302*5113495bSYour Name 
wlan_sm_get_state_name(struct wlan_sm * sm,uint8_t state)303*5113495bSYour Name const char *wlan_sm_get_state_name(struct wlan_sm *sm, uint8_t state)
304*5113495bSYour Name {
305*5113495bSYour Name 	return sm->state_info[state].name;
306*5113495bSYour Name }
307*5113495bSYour Name 
wlan_sm_get_current_state_name(struct wlan_sm * sm)308*5113495bSYour Name const char *wlan_sm_get_current_state_name(struct wlan_sm *sm)
309*5113495bSYour Name {
310*5113495bSYour Name 	return sm->state_info[sm->cur_state].name;
311*5113495bSYour Name }
312*5113495bSYour Name 
313*5113495bSYour Name qdf_export_symbol(wlan_sm_get_current_state_name);
314