1 /*
2  * Copyright (c) 2012-2015,2020-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /**
19  * DOC: Implements general SM framework for connection manager
20  */
21 
22 #include "wlan_cm_main_api.h"
23 #include "wlan_cm_sm.h"
24 #include "wlan_cm_roam_sm.h"
25 
cm_set_state(struct cnx_mgr * cm_ctx,enum wlan_cm_sm_state state)26 void cm_set_state(struct cnx_mgr *cm_ctx, enum wlan_cm_sm_state state)
27 {
28 	if (state < WLAN_CM_S_MAX)
29 		cm_ctx->sm.cm_state = state;
30 	else
31 		mlme_err("vdev %d mlme state (%d) is invalid",
32 			 wlan_vdev_get_id(cm_ctx->vdev), state);
33 }
34 
cm_set_substate(struct cnx_mgr * cm_ctx,enum wlan_cm_sm_state substate)35 void cm_set_substate(struct cnx_mgr *cm_ctx, enum wlan_cm_sm_state substate)
36 {
37 	if ((substate > WLAN_CM_S_MAX) && (substate < WLAN_CM_SS_MAX))
38 		cm_ctx->sm.cm_substate = substate;
39 	else
40 		mlme_err("vdev %d mlme sub state (%d) is invalid",
41 			 wlan_vdev_get_id(cm_ctx->vdev), substate);
42 }
43 
cm_sm_state_update(struct cnx_mgr * cm_ctx,enum wlan_cm_sm_state state,enum wlan_cm_sm_state substate)44 void cm_sm_state_update(struct cnx_mgr *cm_ctx,
45 			enum wlan_cm_sm_state state,
46 			enum wlan_cm_sm_state substate)
47 {
48 	if (!cm_ctx)
49 		return;
50 
51 	cm_set_state(cm_ctx, state);
52 	cm_set_substate(cm_ctx, substate);
53 }
54 
55 /**
56  * cm_state_init_entry() - Entry API for init state for connection mgr
57  * @ctx: connection manager ctx
58  *
59  * API to perform operations on moving to init state
60  *
61  * Return: void
62  */
cm_state_init_entry(void * ctx)63 static void cm_state_init_entry(void *ctx)
64 {
65 	struct cnx_mgr *cm_ctx = ctx;
66 
67 	cm_sm_state_update(cm_ctx, WLAN_CM_S_INIT, WLAN_CM_SS_IDLE);
68 }
69 
70 /**
71  * cm_state_init_exit() - Exit API for init state for connection mgr
72  * @ctx: connection manager ctx
73  *
74  * API to perform operations on exiting from init state
75  *
76  * Return: void
77  */
cm_state_init_exit(void * ctx)78 static void cm_state_init_exit(void *ctx)
79 {
80 }
81 
82 /**
83  * cm_state_init_event() - Init State event handler for connection mgr
84  * @ctx: connection manager ctx
85  * @event: event
86  * @data_len: length of @data
87  * @data: event data
88  *
89  * API to handle events in INIT state
90  *
91  * Return: bool
92  */
cm_state_init_event(void * ctx,uint16_t event,uint16_t data_len,void * data)93 static bool cm_state_init_event(void *ctx, uint16_t event,
94 				uint16_t data_len, void *data)
95 {
96 	struct cnx_mgr *cm_ctx = ctx;
97 	bool event_handled = true;
98 	QDF_STATUS status;
99 
100 	switch (event) {
101 	case WLAN_CM_SM_EV_CONNECT_REQ:
102 		status = cm_add_connect_req_to_list(cm_ctx, data);
103 		if (QDF_IS_STATUS_ERROR(status)) {
104 			/* if fail to add req return failure */
105 			event_handled = false;
106 			break;
107 		}
108 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTING);
109 		cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_CONNECT_START,
110 					 data_len, data);
111 		break;
112 	case WLAN_CM_SM_EV_CONNECT_FAILURE:
113 		if (cm_is_link_switch_connect_resp(data)) {
114 			/*
115 			 * If non-link switch connect fails, kernel will be
116 			 * notified so the driver and kernel are in sync,
117 			 * but link switch is internal to driver and any failure
118 			 * is not notified to kernel.
119 			 * This can lead to kernel and driver going out of sync
120 			 * and any new disconnect requests might get dropped as
121 			 * CM is in INIT state and kernel will assume that
122 			 * interface is still in connected state.
123 			 * To handle this situation, change the substate of CM
124 			 * to signify VDEV is in INIT state due to link switch,
125 			 * so that any later disconnect requests will not be
126 			 * dropped.
127 			 */
128 			cm_sm_transition_to(cm_ctx,
129 					    WLAN_CM_SS_IDLE_DUE_TO_LINK_SWITCH);
130 		}
131 		cm_connect_complete(cm_ctx, data);
132 		break;
133 	case WLAN_CM_SM_EV_DISCONNECT_DONE:
134 		if (cm_is_link_switch_disconnect_resp(data)) {
135 			/*
136 			 * Change the substate of CM incase the disconnect
137 			 * is due to link switch so that any disconnect requests
138 			 * from NB/SB will not get dropped when handling those
139 			 * in INIT state.
140 			 */
141 			cm_sm_transition_to(cm_ctx,
142 					    WLAN_CM_SS_IDLE_DUE_TO_LINK_SWITCH);
143 		}
144 		cm_disconnect_complete(cm_ctx, data);
145 		break;
146 	case WLAN_CM_SM_EV_DISCONNECT_REQ:
147 		status = cm_handle_discon_req_in_non_connected_state(cm_ctx, data,
148 								     WLAN_CM_S_INIT);
149 		if (QDF_IS_STATUS_ERROR(status)) {
150 			/*
151 			 * Return not handled as this req need to be
152 			 * dropped and return failure to the requester
153 			 */
154 			event_handled = false;
155 		}
156 		break;
157 	case WLAN_CM_SM_EV_ROAM_SYNC:
158 		/**
159 		 * If it's a legacy to MLO roaming, bringup the link vdev to
160 		 * process ROAM_SYNC indication on the link vdev.
161 		 */
162 		if (wlan_vdev_mlme_is_mlo_link_vdev(cm_ctx->vdev)) {
163 			cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTED);
164 			status = cm_sm_deliver_event_sync(cm_ctx,
165 							  WLAN_CM_SM_EV_ROAM_SYNC,
166 							  data_len, data);
167 			if (QDF_IS_STATUS_ERROR(status)) {
168 				cm_sm_transition_to(cm_ctx, WLAN_CM_S_INIT);
169 				event_handled = false;
170 			}
171 		} else {
172 			event_handled = false;
173 		}
174 		break;
175 	default:
176 		event_handled = false;
177 		break;
178 	}
179 
180 	return event_handled;
181 }
182 
183 /**
184  * cm_state_connecting_entry() - Entry API for connecting state for
185  * connection mgr
186  * @ctx: connection manager ctx
187  *
188  * API to perform operations on moving to connecting state
189  *
190  * Return: void
191  */
cm_state_connecting_entry(void * ctx)192 static void cm_state_connecting_entry(void *ctx)
193 {
194 	struct cnx_mgr *cm_ctx = ctx;
195 
196 	cm_sm_state_update(cm_ctx, WLAN_CM_S_CONNECTING, WLAN_CM_SS_IDLE);
197 }
198 
199 /**
200  * cm_state_connecting_exit() - Exit API for connecting state for
201  * connection mgr
202  * @ctx: connection manager ctx
203  *
204  * API to perform operations on exiting from connecting state
205  *
206  * Return: void
207  */
cm_state_connecting_exit(void * ctx)208 static void cm_state_connecting_exit(void *ctx)
209 {
210 }
211 
212 /**
213  * cm_state_connecting_event() - Connecting State event handler for
214  * connection mgr
215  * @ctx: connection manager ctx
216  * @event: event
217  * @data_len: length of @data
218  * @data: event data
219  *
220  * API to handle events in CONNECTING state
221  *
222  * Return: bool
223  */
cm_state_connecting_event(void * ctx,uint16_t event,uint16_t data_len,void * data)224 static bool cm_state_connecting_event(void *ctx, uint16_t event,
225 				      uint16_t data_len, void *data)
226 {
227 	struct cnx_mgr *cm_ctx = ctx;
228 	bool event_handled = true;
229 
230 	switch (event) {
231 	case WLAN_CM_SM_EV_CONNECT_START:
232 		cm_sm_transition_to(cm_ctx, WLAN_CM_SS_JOIN_PENDING);
233 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
234 		break;
235 	default:
236 		event_handled = false;
237 		break;
238 	}
239 
240 	return event_handled;
241 }
242 
243 /**
244  * cm_state_connected_entry() - Entry API for connected state for
245  * connection mgr
246  * @ctx: connection manager ctx
247  *
248  * API to perform operations on moving to connected state
249  *
250  * Return: void
251  */
cm_state_connected_entry(void * ctx)252 static void cm_state_connected_entry(void *ctx)
253 {
254 	struct cnx_mgr *cm_ctx = ctx;
255 
256 	cm_sm_state_update(cm_ctx, WLAN_CM_S_CONNECTED, WLAN_CM_SS_IDLE);
257 }
258 
259 /**
260  * cm_state_connected_exit() - Exit API for connected state for
261  * connection mgr
262  * @ctx: connection manager ctx
263  *
264  * API to perform operations on exiting from connected state
265  *
266  * Return: void
267  */
cm_state_connected_exit(void * ctx)268 static void cm_state_connected_exit(void *ctx)
269 {
270 }
271 
272 #if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD)
273 #ifdef WLAN_FEATURE_ROAM_OFFLOAD
274 static
cm_handle_fw_roam_connected_event(struct cnx_mgr * cm_ctx,uint16_t event,uint16_t data_len,void * data)275 bool cm_handle_fw_roam_connected_event(struct cnx_mgr *cm_ctx, uint16_t event,
276 				       uint16_t data_len, void *data)
277 {
278 	bool event_handled = true;
279 	QDF_STATUS status;
280 	struct cm_req *roam_cm_req;
281 
282 	switch (event) {
283 	case WLAN_CM_SM_EV_ROAM_INVOKE:
284 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_ROAMING);
285 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
286 		break;
287 	case WLAN_CM_SM_EV_ROAM_ABORT:
288 	case WLAN_CM_SM_EV_ROAM_INVOKE_FAIL:
289 	case WLAN_CM_SM_EV_ROAM_HO_FAIL:
290 		cm_remove_cmd(cm_ctx, data);
291 		break;
292 	case WLAN_CM_SM_EV_ROAM_START:
293 		status = cm_prepare_roam_cmd(cm_ctx, &roam_cm_req,
294 					     CM_ROAMING_FW);
295 		if (QDF_IS_STATUS_ERROR(status)) {
296 			event_handled = false;
297 			break;
298 		}
299 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_ROAMING);
300 		cm_sm_deliver_event_sync(cm_ctx, event,
301 					 sizeof(*roam_cm_req), roam_cm_req);
302 		break;
303 	case WLAN_CM_SM_EV_ROAM_SYNC:
304 		status = cm_prepare_roam_cmd(cm_ctx, &roam_cm_req,
305 					     CM_ROAMING_FW);
306 		if (QDF_IS_STATUS_ERROR(status)) {
307 			event_handled = false;
308 			break;
309 		}
310 		status = cm_add_fw_roam_cmd_to_list_n_ser(cm_ctx, roam_cm_req);
311 		if (QDF_IS_STATUS_ERROR(status)) {
312 			event_handled = false;
313 			break;
314 		}
315 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_ROAMING);
316 		status = cm_sm_deliver_event_sync(cm_ctx, event, data_len,
317 						  data);
318 		if (QDF_IS_STATUS_ERROR(status))
319 			event_handled = false;
320 		break;
321 	case WLAN_CM_SM_EV_ROAM_DONE:
322 		cm_fw_roam_complete(cm_ctx, data);
323 		break;
324 	default:
325 		event_handled = false;
326 		break;
327 	}
328 
329 	return event_handled;
330 }
331 #else /* WLAN_FEATURE_ROAM_OFFLOAD */
332 static inline
cm_handle_fw_roam_connected_event(struct cnx_mgr * cm_ctx,uint16_t event,uint16_t data_len,void * data)333 bool cm_handle_fw_roam_connected_event(struct cnx_mgr *cm_ctx, uint16_t event,
334 				       uint16_t data_len, void *data)
335 {
336 	return false;
337 }
338 #endif /* WLAN_FEATURE_ROAM_OFFLOAD */
339 
340 static bool
cm_handle_roam_connected_event(struct cnx_mgr * cm_ctx,uint16_t event,uint16_t data_len,void * data)341 cm_handle_roam_connected_event(struct cnx_mgr *cm_ctx, uint16_t event,
342 			       uint16_t data_len, void *data)
343 {
344 	bool event_handled = true;
345 
346 	/* Handle roam event only if roam is enabled */
347 	if (!cm_is_roam_enabled(wlan_vdev_get_psoc(cm_ctx->vdev)))
348 		return false;
349 
350 	switch (event) {
351 	case WLAN_CM_SM_EV_ROAM_REQ:
352 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_ROAMING);
353 		cm_sm_deliver_event_sync(cm_ctx,
354 					 WLAN_CM_SM_EV_ROAM_REQ,
355 					 data_len, data);
356 		break;
357 	default:
358 		event_handled =
359 			cm_handle_fw_roam_connected_event(cm_ctx, event,
360 							  data_len, data);
361 		break;
362 	}
363 
364 	return event_handled;
365 }
366 #else /* WLAN_FEATURE_HOST_ROAM || WLAN_FEATURE_ROAM_OFFLOAD */
367 static inline
cm_handle_roam_connected_event(struct cnx_mgr * cm_ctx,uint16_t event,uint16_t data_len,void * data)368 bool cm_handle_roam_connected_event(struct cnx_mgr *cm_ctx, uint16_t event,
369 				    uint16_t data_len, void *data)
370 {
371 	return false;
372 }
373 #endif /* WLAN_FEATURE_HOST_ROAM || WLAN_FEATURE_ROAM_OFFLOAD */
374 
375 /**
376  * cm_state_connected_event() - Connected State event handler for
377  * connection mgr
378  * @ctx: connection manager ctx
379  * @event: event
380  * @data_len: length of @data
381  * @data: event data
382  *
383  * API to handle events in CONNECTED state
384  *
385  * Return: bool
386  */
cm_state_connected_event(void * ctx,uint16_t event,uint16_t data_len,void * data)387 static bool cm_state_connected_event(void *ctx, uint16_t event,
388 				     uint16_t data_len, void *data)
389 {
390 	struct cnx_mgr *cm_ctx = ctx;
391 	bool event_handled = true;
392 	QDF_STATUS status;
393 	struct cm_req *roam_cm_req;
394 
395 	switch (event) {
396 	case WLAN_CM_SM_EV_CONNECT_REQ:
397 		status = cm_check_and_prepare_roam_req(cm_ctx, data,
398 						       &roam_cm_req);
399 		if (QDF_IS_STATUS_SUCCESS(status)) {
400 			cm_sm_deliver_event_sync(cm_ctx,
401 						 WLAN_CM_SM_EV_ROAM_REQ,
402 						 sizeof(*roam_cm_req),
403 						 roam_cm_req);
404 			break;
405 		}
406 		status = cm_handle_connect_req_in_non_init_state(cm_ctx, data,
407 							WLAN_CM_S_CONNECTED);
408 		if (QDF_IS_STATUS_ERROR(status)) {
409 			event_handled = false;
410 			break;
411 		}
412 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTING);
413 		cm_sm_deliver_event_sync(cm_ctx,
414 					 WLAN_CM_SM_EV_CONNECT_START,
415 					 data_len, data);
416 		break;
417 	case WLAN_CM_SM_EV_DISCONNECT_ACTIVE:
418 		cm_disconnect_active(cm_ctx, data);
419 		break;
420 	case WLAN_CM_SM_EV_CONNECT_SUCCESS:
421 		cm_connect_complete(cm_ctx, data);
422 		break;
423 	case WLAN_CM_SM_EV_DISCONNECT_REQ:
424 		status = cm_add_disconnect_req_to_list(cm_ctx, data);
425 		if (QDF_IS_STATUS_ERROR(status)) {
426 			/* if fail to add req return failure */
427 			event_handled = false;
428 			break;
429 		}
430 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
431 		cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_DISCONNECT_START,
432 					 data_len, data);
433 		break;
434 	case WLAN_CM_SM_EV_REASSOC_DONE:
435 		cm_reassoc_complete(cm_ctx, data);
436 		break;
437 	default:
438 		event_handled =
439 			cm_handle_roam_connected_event(cm_ctx, event,
440 						       data_len, data);
441 		break;
442 	}
443 	return event_handled;
444 }
445 
446 /**
447  * cm_state_disconnecting_entry() - Entry API for disconnecting state for
448  * connection mgr
449  * @ctx: connection manager ctx
450  *
451  * API to perform operations on moving to disconnecting state
452  *
453  * Return: void
454  */
cm_state_disconnecting_entry(void * ctx)455 static void cm_state_disconnecting_entry(void *ctx)
456 {
457 	struct cnx_mgr *cm_ctx = ctx;
458 
459 	cm_sm_state_update(cm_ctx, WLAN_CM_S_DISCONNECTING, WLAN_CM_SS_IDLE);
460 }
461 
462 /**
463  * cm_state_disconnecting_exit() - Exit API for disconnecting state for
464  * connection mgr
465  * @ctx: connection manager ctx
466  *
467  * API to perform operations on exiting from disconnecting state
468  *
469  * Return: void
470  */
cm_state_disconnecting_exit(void * ctx)471 static void cm_state_disconnecting_exit(void *ctx)
472 {
473 }
474 
475 /**
476  * cm_state_disconnecting_event() - Disconnecting State event handler for
477  * connection mgr
478  * @ctx: connection manager ctx
479  * @event: event
480  * @data_len: length of @data
481  * @data: event data
482  *
483  * API to handle events in Disconnecting state
484  *
485  * Return: bool
486  */
cm_state_disconnecting_event(void * ctx,uint16_t event,uint16_t data_len,void * data)487 static bool cm_state_disconnecting_event(void *ctx, uint16_t event,
488 					 uint16_t data_len, void *data)
489 {
490 	struct cnx_mgr *cm_ctx = ctx;
491 	bool event_handled = true;
492 	QDF_STATUS status;
493 
494 	switch (event) {
495 	case WLAN_CM_SM_EV_CONNECT_REQ:
496 		status = cm_handle_connect_req_in_non_init_state(cm_ctx, data,
497 						WLAN_CM_S_DISCONNECTING);
498 		if (QDF_IS_STATUS_ERROR(status)) {
499 			event_handled = false;
500 			break;
501 		}
502 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTING);
503 		cm_sm_deliver_event_sync(cm_ctx,
504 					 WLAN_CM_SM_EV_CONNECT_START,
505 					 data_len, data);
506 		break;
507 	case WLAN_CM_SM_EV_DISCONNECT_START:
508 		cm_disconnect_start(cm_ctx, data);
509 		break;
510 	case WLAN_CM_SM_EV_DISCONNECT_ACTIVE:
511 		cm_disconnect_active(cm_ctx, data);
512 		break;
513 	case WLAN_CM_SM_EV_DISCONNECT_DONE:
514 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_INIT);
515 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
516 		break;
517 	case WLAN_CM_SM_EV_DISCONNECT_REQ:
518 		status = cm_handle_discon_req_in_non_connected_state(cm_ctx,
519 						data, WLAN_CM_S_DISCONNECTING);
520 		if (QDF_IS_STATUS_ERROR(status)) {
521 			event_handled = false;
522 			break;
523 		}
524 		cm_sm_deliver_event_sync(cm_ctx,
525 					 WLAN_CM_SM_EV_DISCONNECT_START,
526 					 data_len, data);
527 		break;
528 	case WLAN_CM_SM_EV_RSO_STOP_RSP:
529 		cm_disconnect_continue_after_rso_stop(cm_ctx->vdev, data);
530 		break;
531 	default:
532 		event_handled = false;
533 		break;
534 	}
535 
536 	return event_handled;
537 }
538 
539 /**
540  * cm_subst_join_pending_entry() - Entry API for join pending sub-state for
541  * connection mgr
542  * @ctx: connection manager ctx
543  *
544  * API to perform operations on moving to join pending sub-state
545  *
546  * Return: void
547  */
cm_subst_join_pending_entry(void * ctx)548 static void cm_subst_join_pending_entry(void *ctx)
549 {
550 	struct cnx_mgr *cm_ctx = ctx;
551 
552 	if (cm_get_state(cm_ctx) != WLAN_CM_S_CONNECTING)
553 		QDF_BUG(0);
554 
555 	cm_set_substate(cm_ctx, WLAN_CM_SS_JOIN_PENDING);
556 }
557 
558 /**
559  * cm_subst_join_pending_exit() - Exit API for join pending sub-state for
560  * connection mgr
561  * @ctx: connection manager ctx
562  *
563  * API to perform operations on exiting from join pending sub-state
564  *
565  * Return: void
566  */
cm_subst_join_pending_exit(void * ctx)567 static void cm_subst_join_pending_exit(void *ctx)
568 {
569 }
570 
571 /**
572  * cm_subst_join_pending_event() - Join pending sub-state event handler for
573  * connection mgr
574  * @ctx: connection manager ctx
575  * @event: event
576  * @data_len: length of @data
577  * @data: event data
578  *
579  * API to handle events in Join pending sub-state
580  *
581  * Return: bool
582  */
cm_subst_join_pending_event(void * ctx,uint16_t event,uint16_t data_len,void * data)583 static bool cm_subst_join_pending_event(void *ctx, uint16_t event,
584 					uint16_t data_len, void *data)
585 {
586 	struct cnx_mgr *cm_ctx = ctx;
587 	bool event_handled = true;
588 	QDF_STATUS status = QDF_STATUS_SUCCESS;
589 	struct wlan_cm_connect_resp *resp;
590 	struct cm_req *cm_req;
591 
592 	switch (event) {
593 	case WLAN_CM_SM_EV_CONNECT_REQ:
594 		status =
595 			cm_handle_connect_req_in_non_init_state(cm_ctx, data,
596 						WLAN_CM_SS_JOIN_PENDING);
597 		if (QDF_IS_STATUS_ERROR(status)) {
598 			event_handled = false;
599 			break;
600 		}
601 		cm_sm_deliver_event_sync(cm_ctx,
602 					 WLAN_CM_SM_EV_CONNECT_START,
603 					 data_len, data);
604 		break;
605 	case WLAN_CM_SM_EV_CONNECT_START:
606 		cm_connect_start(cm_ctx, data);
607 		break;
608 	case WLAN_CM_SM_EV_CONNECT_ACTIVE:
609 		/* check if cm id is valid for the current req */
610 		if (!cm_check_cmid_match_list_head(cm_ctx, data)) {
611 			event_handled = false;
612 			break;
613 		}
614 		cm_sm_transition_to(cm_ctx, WLAN_CM_SS_JOIN_ACTIVE);
615 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
616 		break;
617 	case WLAN_CM_SM_EV_HW_MODE_SUCCESS:
618 	case WLAN_CM_SM_EV_HW_MODE_FAILURE:
619 	case WLAN_CM_SM_EV_BEARER_SWITCH_COMPLETE:
620 		/* check if cm id is valid for the current req */
621 		if (!cm_check_cmid_match_list_head(cm_ctx, data)) {
622 			event_handled = false;
623 			break;
624 		}
625 		cm_ser_connect_after_mode_change_resp(cm_ctx, data, event);
626 		break;
627 	case WLAN_CM_SM_EV_SCAN:
628 		cm_sm_transition_to(cm_ctx, WLAN_CM_SS_SCAN);
629 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
630 		break;
631 	case WLAN_CM_SM_EV_SCAN_FAILURE:
632 		status = QDF_STATUS_E_FAILURE;
633 		/* Fall through after setting status failure */
634 		fallthrough;
635 	case WLAN_CM_SM_EV_SCAN_SUCCESS:
636 		cm_connect_scan_resp(cm_ctx, data, status);
637 		break;
638 	case WLAN_CM_SM_EV_CONNECT_FAILURE:
639 		/* check if connect resp cm id is valid for the current req */
640 		if (!cm_connect_resp_cmid_match_list_head(cm_ctx, data)) {
641 			event_handled = false;
642 			break;
643 		}
644 		/*
645 		 * On connect req failure (before serialization), if there is a
646 		 * pending disconnect req then move to disconnecting state and
647 		 * wait for disconnect to complete before moving to INIT state.
648 		 * Else directly transition to INIT state.
649 		 *
650 		 * On disconnect completion or a new connect/disconnect req in
651 		 * disconnnecting state, the failed connect req will be flushed.
652 		 * This will ensure SM moves to INIT state after completion of
653 		 * all operation.
654 		 */
655 		if (cm_ctx->disconnect_count) {
656 			resp = data;
657 
658 			mlme_debug(CM_PREFIX_FMT "disconnect_count %d",
659 				   CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
660 						 resp->cm_id),
661 				   cm_ctx->disconnect_count);
662 			cm_req = cm_get_req_by_cm_id(cm_ctx, resp->cm_id);
663 			if (cm_req)
664 				cm_req->failed_req = true;
665 			cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
666 			break;
667 		}
668 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_INIT);
669 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
670 		break;
671 	case WLAN_CM_SM_EV_DISCONNECT_ACTIVE:
672 		cm_disconnect_active(cm_ctx, data);
673 		break;
674 	case WLAN_CM_SM_EV_DISCONNECT_DONE:
675 		cm_disconnect_complete(cm_ctx, data);
676 		break;
677 	case WLAN_CM_SM_EV_DISCONNECT_REQ:
678 		status = cm_handle_discon_req_in_non_connected_state(cm_ctx,
679 						data, WLAN_CM_SS_JOIN_PENDING);
680 		if (QDF_IS_STATUS_ERROR(status)) {
681 			event_handled = false;
682 			break;
683 		}
684 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
685 		cm_sm_deliver_event_sync(cm_ctx,
686 					 WLAN_CM_SM_EV_DISCONNECT_START,
687 					 data_len, data);
688 		break;
689 	case WLAN_CM_SM_EV_RSO_STOP_RSP:
690 		cm_disconnect_continue_after_rso_stop(cm_ctx->vdev, data);
691 		break;
692 	default:
693 		event_handled = false;
694 		break;
695 	}
696 
697 	return event_handled;
698 }
699 
700 /**
701  * cm_subst_scan_entry() - Entry API for scan sub-state for
702  * connection mgr
703  * @ctx: connection manager ctx
704  *
705  * API to perform operations on moving to scan sub-state
706  *
707  * Return: void
708  */
cm_subst_scan_entry(void * ctx)709 static void cm_subst_scan_entry(void *ctx)
710 {
711 	struct cnx_mgr *cm_ctx = ctx;
712 
713 	if (cm_get_state(cm_ctx) != WLAN_CM_S_CONNECTING)
714 		QDF_BUG(0);
715 
716 	cm_set_substate(cm_ctx, WLAN_CM_SS_SCAN);
717 }
718 
719 /**
720  * cm_subst_scan_exit() - Exit API for scan sub-state for
721  * connection mgr
722  * @ctx: connection manager ctx
723  *
724  * API to perform operations on exiting from scan sub-state
725  *
726  * Return: void
727  */
cm_subst_scan_exit(void * ctx)728 static void cm_subst_scan_exit(void *ctx)
729 {
730 }
731 
732 /**
733  * cm_subst_scan_event() - Scan sub-state event handler for
734  * connection mgr
735  * @ctx: connection manager ctx
736  * @event: event
737  * @data_len: length of @data
738  * @data: event data
739  *
740  * API to handle events in scan sub-state
741  *
742  * Return: bool
743  */
cm_subst_scan_event(void * ctx,uint16_t event,uint16_t data_len,void * data)744 static bool cm_subst_scan_event(void *ctx, uint16_t event,
745 				uint16_t data_len, void *data)
746 {
747 	struct cnx_mgr *cm_ctx = ctx;
748 	bool event_handled = true;
749 	QDF_STATUS status;
750 
751 	switch (event) {
752 	case WLAN_CM_SM_EV_CONNECT_REQ:
753 		status = cm_handle_connect_req_in_non_init_state(cm_ctx, data,
754 							WLAN_CM_SS_SCAN);
755 		if (QDF_IS_STATUS_ERROR(status)) {
756 			event_handled = false;
757 			break;
758 		}
759 		cm_sm_transition_to(cm_ctx, WLAN_CM_SS_JOIN_PENDING);
760 		cm_sm_deliver_event_sync(cm_ctx,
761 					 WLAN_CM_SM_EV_CONNECT_START,
762 					 data_len, data);
763 		break;
764 	case WLAN_CM_SM_EV_SCAN:
765 		cm_connect_scan_start(cm_ctx, data);
766 		break;
767 	case WLAN_CM_SM_EV_SCAN_SUCCESS:
768 	case WLAN_CM_SM_EV_SCAN_FAILURE:
769 		/* check if scan id is valid for the current req */
770 		if (!cm_check_scanid_match_list_head(cm_ctx, data)) {
771 			event_handled = false;
772 			break;
773 		}
774 		cm_sm_transition_to(cm_ctx, WLAN_CM_SS_JOIN_PENDING);
775 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
776 		break;
777 	case WLAN_CM_SM_EV_DISCONNECT_ACTIVE:
778 		cm_disconnect_active(cm_ctx, data);
779 		break;
780 	case WLAN_CM_SM_EV_DISCONNECT_DONE:
781 		cm_disconnect_complete(cm_ctx, data);
782 		break;
783 	case WLAN_CM_SM_EV_DISCONNECT_REQ:
784 		status = cm_handle_discon_req_in_non_connected_state(cm_ctx,
785 						data, WLAN_CM_SS_SCAN);
786 		if (QDF_IS_STATUS_ERROR(status)) {
787 			event_handled = false;
788 			break;
789 		}
790 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
791 		cm_sm_deliver_event_sync(cm_ctx,
792 					 WLAN_CM_SM_EV_DISCONNECT_START,
793 					 data_len, data);
794 		break;
795 	case WLAN_CM_SM_EV_RSO_STOP_RSP:
796 		cm_disconnect_continue_after_rso_stop(cm_ctx->vdev, data);
797 		break;
798 	default:
799 		event_handled = false;
800 		break;
801 	}
802 
803 	return event_handled;
804 }
805 
806 /**
807  * cm_subst_join_active_entry() - Entry API for join active sub-state for
808  * connection mgr
809  * @ctx: connection manager ctx
810  *
811  * API to perform operations on moving to join active sub-state
812  *
813  * Return: void
814  */
cm_subst_join_active_entry(void * ctx)815 static void cm_subst_join_active_entry(void *ctx)
816 {
817 	struct cnx_mgr *cm_ctx = ctx;
818 
819 	if (cm_get_state(cm_ctx) != WLAN_CM_S_CONNECTING)
820 		QDF_BUG(0);
821 
822 	cm_set_substate(cm_ctx, WLAN_CM_SS_JOIN_ACTIVE);
823 }
824 
825 /**
826  * cm_subst_join_active_exit() - Exit API for join active sub-state for
827  * connection mgr
828  * @ctx: connection manager ctx
829  *
830  * API to perform operations on exiting from join active sub-state
831  *
832  * Return: void
833  */
cm_subst_join_active_exit(void * ctx)834 static void cm_subst_join_active_exit(void *ctx)
835 {
836 }
837 
838 /**
839  * cm_subst_join_active_event() - Join active sub-state event handler for
840  * connection mgr
841  * @ctx: connection manager ctx
842  * @event: event
843  * @data_len: length of @data
844  * @data: event data
845  *
846  * API to handle events in join active sub-state
847  *
848  * Return: bool
849  */
cm_subst_join_active_event(void * ctx,uint16_t event,uint16_t data_len,void * data)850 static bool cm_subst_join_active_event(void *ctx, uint16_t event,
851 				       uint16_t data_len, void *data)
852 {
853 	struct cnx_mgr *cm_ctx = ctx;
854 	bool event_handled = true;
855 	QDF_STATUS status;
856 
857 	switch (event) {
858 	case WLAN_CM_SM_EV_CONNECT_REQ:
859 		status = cm_handle_connect_req_in_non_init_state(cm_ctx, data,
860 							WLAN_CM_SS_JOIN_ACTIVE);
861 		if (QDF_IS_STATUS_ERROR(status)) {
862 			event_handled = false;
863 			break;
864 		}
865 		cm_sm_transition_to(cm_ctx, WLAN_CM_SS_JOIN_PENDING);
866 		cm_sm_deliver_event_sync(cm_ctx,
867 					 WLAN_CM_SM_EV_CONNECT_START,
868 					 data_len, data);
869 		break;
870 	case WLAN_CM_SM_EV_CONNECT_ACTIVE:
871 		cm_connect_active(cm_ctx, data);
872 		break;
873 	case WLAN_CM_SM_EV_CONNECT_SUCCESS:
874 		/* check if connect resp cm id is valid for the current req */
875 		if (!cm_connect_resp_cmid_match_list_head(cm_ctx, data)) {
876 			event_handled = false;
877 			break;
878 		}
879 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTED);
880 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
881 		break;
882 	case WLAN_CM_SM_EV_CONNECT_GET_NEXT_CANDIDATE:
883 		/* check if connect resp cm id is valid for the current req */
884 		if (!cm_connect_resp_cmid_match_list_head(cm_ctx, data)) {
885 			event_handled = false;
886 			break;
887 		}
888 		cm_try_next_candidate(cm_ctx, data);
889 		break;
890 	case WLAN_CM_SM_EV_CONNECT_FAILURE:
891 		/* check if connect resp cm id is valid for the current req */
892 		if (!cm_connect_resp_cmid_match_list_head(cm_ctx, data)) {
893 			event_handled = false;
894 			break;
895 		}
896 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_INIT);
897 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
898 		break;
899 	case WLAN_CM_SM_EV_BSS_SELECT_IND_SUCCESS:
900 		/* check if cm id is valid for the current req */
901 		if (!cm_check_cmid_match_list_head(cm_ctx, data)) {
902 			event_handled = false;
903 			break;
904 		}
905 		cm_peer_create_on_bss_select_ind_resp(cm_ctx, data);
906 		break;
907 	case WLAN_CM_SM_EV_BSS_CREATE_PEER_SUCCESS:
908 		/* check if cm id is valid for the current req */
909 		if (!cm_check_cmid_match_list_head(cm_ctx, data)) {
910 			event_handled = false;
911 			break;
912 		}
913 		cm_resume_connect_after_peer_create(cm_ctx, data);
914 		break;
915 	case WLAN_CM_SM_EV_DISCONNECT_REQ:
916 		status = cm_handle_discon_req_in_non_connected_state(cm_ctx,
917 						data, WLAN_CM_SS_JOIN_ACTIVE);
918 		if (QDF_IS_STATUS_ERROR(status)) {
919 			event_handled = false;
920 			break;
921 		}
922 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
923 		cm_sm_deliver_event_sync(cm_ctx,
924 					 WLAN_CM_SM_EV_DISCONNECT_START,
925 					 data_len, data);
926 		break;
927 	default:
928 		event_handled = false;
929 		break;
930 	}
931 
932 	return event_handled;
933 }
934 
935 #ifdef CONN_MGR_ADV_FEATURE
936 /**
937  * cm_subst_idle_due_to_link_switch_entry() - Entry API for idle due to
938  * link switch substate for connection manager.
939  * @ctx: Connection manager context
940  *
941  * API to perform entry operations to this substate.
942  */
cm_subst_idle_due_to_link_switch_entry(void * ctx)943 static void cm_subst_idle_due_to_link_switch_entry(void *ctx)
944 {
945 	struct cnx_mgr *cm_ctx = ctx;
946 
947 	if (cm_get_state(cm_ctx) != WLAN_CM_S_INIT)
948 		QDF_BUG(0);
949 
950 	cm_set_substate(cm_ctx, WLAN_CM_SS_IDLE_DUE_TO_LINK_SWITCH);
951 }
952 
953 /**
954  * cm_subst_idle_due_to_link_switch_exit() - Exit API from idle due to
955  * link switch substate for connection manager.
956  * @ctx: Connection manager context
957  *
958  * API to perform exit operations before leaving from this substate.
959  */
cm_subst_idle_due_to_link_switch_exit(void * ctx)960 static inline void cm_subst_idle_due_to_link_switch_exit(void *ctx)
961 {
962 }
963 
964 /**
965  * cm_subst_idle_due_to_link_switch_event() - Event handler API for idle
966  * due to link switch substate for connection manager.
967  * @ctx: connection manager ctx
968  * @event: event
969  * @data_len: length of @data
970  * @data: event data
971  *
972  * API to handle events in IDLE_DUE_TO_LINK_SWITCH substate.
973  * Return true if the event is handled or else return false.
974  *
975  * Return: bool
976  */
cm_subst_idle_due_to_link_switch_event(void * ctx,uint16_t event,uint16_t data_len,void * data)977 static bool cm_subst_idle_due_to_link_switch_event(void *ctx, uint16_t event,
978 						   uint16_t data_len,
979 						   void *data)
980 {
981 	struct cnx_mgr *cm_ctx = ctx;
982 	bool event_handled = true;
983 	QDF_STATUS status;
984 	enum wlan_cm_sm_state cm_state = WLAN_CM_SS_IDLE_DUE_TO_LINK_SWITCH;
985 
986 	switch (event) {
987 	case WLAN_CM_SM_EV_CONNECT_REQ:
988 		/*
989 		 * If the connect request is due to link switch then
990 		 * move the state to INIT to handle usual connect request.
991 		 * Connect request due to link switch are only allowed in
992 		 * INIT-IDLE and INIT-IDLE_DUE_TO_LINK_SWITCH states in all
993 		 * other states the connect request will be rejected.
994 		 *
995 		 * As VDEV is in INIT state due to link switch and if connect
996 		 * request is received from other than link switch, then
997 		 * forcefully move VDEV to CONNECTED state, so the event follows
998 		 * pre-link switch handling path.
999 		 */
1000 		if (cm_is_link_switch_connect_req(data))
1001 			cm_sm_transition_to(cm_ctx, WLAN_CM_S_INIT);
1002 		else
1003 			cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTED);
1004 
1005 		status = cm_sm_deliver_event_sync(cm_ctx, event, data_len,
1006 						  data);
1007 		if (QDF_IS_STATUS_ERROR(status))
1008 			event_handled = false;
1009 		break;
1010 	case WLAN_CM_SM_EV_DISCONNECT_REQ:
1011 		status = cm_handle_discon_req_in_non_connected_state(cm_ctx,
1012 								     data,
1013 								     cm_state);
1014 		/*
1015 		 * To handle the case where disconnect request is due to
1016 		 * link switch, return error so that link switch will abort.
1017 		 */
1018 		if (QDF_IS_STATUS_ERROR(status)) {
1019 			event_handled = false;
1020 			break;
1021 		}
1022 		/*
1023 		 * If disconnect request for non-connected state is success,
1024 		 * then forcefully move VDEV to disconnecting state and start
1025 		 * the disconnect sequence.
1026 		 */
1027 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
1028 		status = cm_sm_deliver_event_sync(cm_ctx,
1029 						  WLAN_CM_SM_EV_DISCONNECT_START,
1030 						  data_len, data);
1031 		if (QDF_IS_STATUS_ERROR(status))
1032 			event_handled = false;
1033 
1034 		break;
1035 	case WLAN_CM_SM_EV_ROAM_SYNC:
1036 		/*
1037 		 * If link switch fails on assoc VDEV and FW roams to new AP
1038 		 * then the ROAM_SYNC event will be dropped as ROAM_SYNC in
1039 		 * INIT state is only allowed for link VDEV. Hence move the VDEV
1040 		 * state to CONNECTED state to handle this event.
1041 		 */
1042 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTED);
1043 		status = cm_sm_deliver_event_sync(cm_ctx, event, data_len,
1044 						  data);
1045 		if (QDF_IS_STATUS_ERROR(status)) {
1046 			cm_sm_transition_to(cm_ctx, WLAN_CM_S_INIT);
1047 			event_handled = false;
1048 		}
1049 		break;
1050 	default:
1051 		/* Handle all other events in INIT state. */
1052 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_INIT);
1053 		status = cm_sm_deliver_event_sync(cm_ctx, event, data_len,
1054 						  data);
1055 		if (QDF_IS_STATUS_ERROR(status))
1056 			event_handled = false;
1057 		break;
1058 	}
1059 
1060 	return event_handled;
1061 }
1062 #else
cm_subst_idle_due_to_link_switch_entry(void * ctx)1063 static inline void cm_subst_idle_due_to_link_switch_entry(void *ctx)
1064 {
1065 }
1066 
cm_subst_idle_due_to_link_switch_exit(void * ctx)1067 static inline void cm_subst_idle_due_to_link_switch_exit(void *ctx)
1068 {
1069 }
1070 
1071 static inline bool
cm_subst_idle_due_to_link_switch_event(void * ctx,uint16_t event,uint16_t data_len,void * data)1072 cm_subst_idle_due_to_link_switch_event(void *ctx, uint16_t event,
1073 				       uint16_t data_len, void *data)
1074 {
1075 	return false;
1076 }
1077 #endif
1078 
1079 struct wlan_sm_state_info cm_sm_info[] = {
1080 	{
1081 		(uint8_t)WLAN_CM_S_INIT,
1082 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1083 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1084 		true,
1085 		"INIT",
1086 		cm_state_init_entry,
1087 		cm_state_init_exit,
1088 		cm_state_init_event
1089 	},
1090 	{
1091 		(uint8_t)WLAN_CM_S_CONNECTING,
1092 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1093 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1094 		true,
1095 		"CONNECTING",
1096 		cm_state_connecting_entry,
1097 		cm_state_connecting_exit,
1098 		cm_state_connecting_event
1099 	},
1100 	{
1101 		(uint8_t)WLAN_CM_S_CONNECTED,
1102 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1103 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1104 		true,
1105 		"CONNECTED",
1106 		cm_state_connected_entry,
1107 		cm_state_connected_exit,
1108 		cm_state_connected_event
1109 	},
1110 	{
1111 		(uint8_t)WLAN_CM_S_DISCONNECTING,
1112 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1113 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1114 		true,
1115 		"DISCONNECTING",
1116 		cm_state_disconnecting_entry,
1117 		cm_state_disconnecting_exit,
1118 		cm_state_disconnecting_event
1119 	},
1120 	{
1121 		(uint8_t)WLAN_CM_S_ROAMING,
1122 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1123 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1124 		true,
1125 		"ROAMING",
1126 		cm_state_roaming_entry,
1127 		cm_state_roaming_exit,
1128 		cm_state_roaming_event
1129 	},
1130 	{
1131 		(uint8_t)WLAN_CM_S_MAX,
1132 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1133 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1134 		false,
1135 		"INVALID",
1136 		NULL,
1137 		NULL,
1138 		NULL
1139 	},
1140 	{
1141 		(uint8_t)WLAN_CM_SS_IDLE,
1142 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1143 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1144 		false,
1145 		"IDLE",
1146 		NULL,
1147 		NULL,
1148 		NULL
1149 	},
1150 	{
1151 		(uint8_t)WLAN_CM_SS_JOIN_PENDING,
1152 		(uint8_t)WLAN_CM_S_CONNECTING,
1153 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1154 		false,
1155 		"JOIN_PENDING",
1156 		cm_subst_join_pending_entry,
1157 		cm_subst_join_pending_exit,
1158 		cm_subst_join_pending_event
1159 	},
1160 	{
1161 		(uint8_t)WLAN_CM_SS_SCAN,
1162 		(uint8_t)WLAN_CM_S_CONNECTING,
1163 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1164 		false,
1165 		"SCAN",
1166 		cm_subst_scan_entry,
1167 		cm_subst_scan_exit,
1168 		cm_subst_scan_event
1169 	},
1170 	{
1171 		(uint8_t)WLAN_CM_SS_JOIN_ACTIVE,
1172 		(uint8_t)WLAN_CM_S_CONNECTING,
1173 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1174 		false,
1175 		"JOIN_ACTIVE",
1176 		cm_subst_join_active_entry,
1177 		cm_subst_join_active_exit,
1178 		cm_subst_join_active_event
1179 	},
1180 	{
1181 		(uint8_t)WLAN_CM_SS_PREAUTH,
1182 		(uint8_t)WLAN_CM_S_ROAMING,
1183 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1184 		false,
1185 		"PREAUTH",
1186 		cm_subst_preauth_entry,
1187 		cm_subst_preauth_exit,
1188 		cm_subst_preauth_event
1189 	},
1190 	{
1191 		(uint8_t)WLAN_CM_SS_REASSOC,
1192 		(uint8_t)WLAN_CM_S_ROAMING,
1193 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1194 		false,
1195 		"REASSOC",
1196 		cm_subst_reassoc_entry,
1197 		cm_subst_reassoc_exit,
1198 		cm_subst_reassoc_event
1199 	},
1200 	{
1201 		(uint8_t)WLAN_CM_SS_ROAM_STARTED,
1202 		(uint8_t)WLAN_CM_S_ROAMING,
1203 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1204 		false,
1205 		"ROAM_START",
1206 		cm_subst_roam_start_entry,
1207 		cm_subst_roam_start_exit,
1208 		cm_subst_roam_start_event
1209 	},
1210 	{
1211 		(uint8_t)WLAN_CM_SS_ROAM_SYNC,
1212 		(uint8_t)WLAN_CM_S_ROAMING,
1213 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1214 		false,
1215 		"ROAM_SYNC",
1216 		cm_subst_roam_sync_entry,
1217 		cm_subst_roam_sync_exit,
1218 		cm_subst_roam_sync_event
1219 	},
1220 	{
1221 		(uint8_t)WLAN_CM_SS_IDLE_DUE_TO_LINK_SWITCH,
1222 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1223 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1224 		false,
1225 		"IDLE_DUE_TO_LINK_SWITCH",
1226 		cm_subst_idle_due_to_link_switch_entry,
1227 		cm_subst_idle_due_to_link_switch_exit,
1228 		cm_subst_idle_due_to_link_switch_event
1229 	},
1230 	{
1231 		(uint8_t)WLAN_CM_SS_MAX,
1232 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1233 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1234 		false,
1235 		"INVALID",
1236 		NULL,
1237 		NULL,
1238 		NULL
1239 	},
1240 };
1241 
1242 static const char *cm_sm_event_names[] = {
1243 	"EV_CONNECT_REQ",
1244 	"EV_SCAN",
1245 	"EV_SCAN_SUCCESS",
1246 	"EV_SCAN_FAILURE",
1247 	"EV_HW_MODE_SUCCESS",
1248 	"EV_HW_MODE_FAILURE",
1249 	"EV_CONNECT_START",
1250 	"EV_CONNECT_ACTIVE",
1251 	"EV_CONNECT_SUCCESS",
1252 	"EV_BSS_SELECT_IND_SUCCESS",
1253 	"EV_BSS_CREATE_PEER_SUCCESS",
1254 	"EV_CONNECT_GET_NXT_CANDIDATE",
1255 	"EV_CONNECT_FAILURE",
1256 	"EV_DISCONNECT_REQ",
1257 	"EV_DISCONNECT_START",
1258 	"EV_DISCONNECT_ACTIVE",
1259 	"EV_DISCONNECT_DONE",
1260 	"EV_ROAM_START",
1261 	"EV_ROAM_SYNC",
1262 	"EV_ROAM_INVOKE_FAIL",
1263 	"EV_ROAM_HO_FAIL",
1264 	"EV_PREAUTH_DONE",
1265 	"EV_GET_NEXT_PREAUTH_AP",
1266 	"EV_PREAUTH_FAIL",
1267 	"EV_START_REASSOC",
1268 	"EV_REASSOC_ACTIVE",
1269 	"EV_REASSOC_DONE",
1270 	"EV_REASSOC_FAILURE",
1271 	"EV_ROAM_COMPLETE",
1272 	"EV_ROAM_REQ",
1273 	"EV_ROAM_INVOKE",
1274 	"EV_ROAM_ABORT",
1275 	"EV_ROAM_DONE",
1276 	"EV_PREAUTH_ACTIVE",
1277 	"EV_PREAUTH_RESP",
1278 	"EV_REASSOC_TIMER",
1279 	"EV_HO_ROAM_DISCONNECT_DONE",
1280 	"EV_RSO_STOP_RSP",
1281 	"EV_BEARER_SWITCH_COMPLETE",
1282 };
1283 
cm_get_state(struct cnx_mgr * cm_ctx)1284 enum wlan_cm_sm_state cm_get_state(struct cnx_mgr *cm_ctx)
1285 {
1286 	enum QDF_OPMODE op_mode;
1287 
1288 	if (!cm_ctx || !cm_ctx->vdev)
1289 		return WLAN_CM_S_MAX;
1290 
1291 	op_mode = wlan_vdev_mlme_get_opmode(cm_ctx->vdev);
1292 
1293 	if (op_mode != QDF_STA_MODE && op_mode != QDF_P2P_CLIENT_MODE)
1294 		return WLAN_CM_S_MAX;
1295 
1296 	return cm_ctx->sm.cm_state;
1297 }
1298 
cm_get_sub_state(struct cnx_mgr * cm_ctx)1299 enum wlan_cm_sm_state cm_get_sub_state(struct cnx_mgr *cm_ctx)
1300 {
1301 	enum QDF_OPMODE op_mode;
1302 
1303 	if (!cm_ctx || !cm_ctx->vdev)
1304 		return WLAN_CM_SS_MAX;
1305 
1306 	op_mode = wlan_vdev_mlme_get_opmode(cm_ctx->vdev);
1307 
1308 	if (op_mode != QDF_STA_MODE && op_mode != QDF_P2P_CLIENT_MODE)
1309 		return WLAN_CM_SS_MAX;
1310 
1311 	return cm_ctx->sm.cm_substate;
1312 }
1313 
cm_sm_print_state_event(struct cnx_mgr * cm_ctx,enum wlan_cm_sm_evt event)1314 static void cm_sm_print_state_event(struct cnx_mgr *cm_ctx,
1315 				    enum wlan_cm_sm_evt event)
1316 {
1317 	enum wlan_cm_sm_state state;
1318 	enum wlan_cm_sm_state substate;
1319 
1320 	state = cm_get_state(cm_ctx);
1321 	substate = cm_get_sub_state(cm_ctx);
1322 
1323 	mlme_nofl_debug("[%s]%s - %s, %s", cm_ctx->sm.sm_hdl->name,
1324 			cm_sm_info[state].name, cm_sm_info[substate].name,
1325 			cm_sm_event_names[event]);
1326 }
1327 
cm_sm_print_state(struct cnx_mgr * cm_ctx)1328 static void cm_sm_print_state(struct cnx_mgr *cm_ctx)
1329 {
1330 	enum wlan_cm_sm_state state;
1331 	enum wlan_cm_sm_state substate;
1332 
1333 	state = cm_get_state(cm_ctx);
1334 	substate = cm_get_sub_state(cm_ctx);
1335 
1336 	mlme_nofl_debug("[%s]%s - %s", cm_ctx->sm.sm_hdl->name,
1337 			cm_sm_info[state].name, cm_sm_info[substate].name);
1338 }
1339 
cm_sm_deliver_event(struct wlan_objmgr_vdev * vdev,enum wlan_cm_sm_evt event,uint16_t data_len,void * data)1340 QDF_STATUS cm_sm_deliver_event(struct wlan_objmgr_vdev *vdev,
1341 			       enum wlan_cm_sm_evt event,
1342 			       uint16_t data_len, void *data)
1343 {
1344 	QDF_STATUS status;
1345 	enum wlan_cm_sm_state state_entry, state_exit;
1346 	enum wlan_cm_sm_state substate_entry, substate_exit;
1347 	enum QDF_OPMODE op_mode = wlan_vdev_mlme_get_opmode(vdev);
1348 	struct cnx_mgr *cm_ctx;
1349 
1350 	if (op_mode != QDF_STA_MODE && op_mode != QDF_P2P_CLIENT_MODE) {
1351 		mlme_err("vdev %d Invalid mode %d",
1352 			 wlan_vdev_get_id(vdev), op_mode);
1353 		return QDF_STATUS_E_NOSUPPORT;
1354 	}
1355 
1356 	cm_ctx = cm_get_cm_ctx(vdev);
1357 	if (!cm_ctx)
1358 		return QDF_STATUS_E_FAILURE;
1359 
1360 	cm_lock_acquire(cm_ctx);
1361 
1362 	/* store entry state and sub state for prints */
1363 	state_entry = cm_get_state(cm_ctx);
1364 	substate_entry = cm_get_sub_state(cm_ctx);
1365 	cm_sm_print_state_event(cm_ctx, event);
1366 
1367 	status = cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
1368 	/* Take exit state, exit substate for prints */
1369 	state_exit = cm_get_state(cm_ctx);
1370 	substate_exit = cm_get_sub_state(cm_ctx);
1371 	/* If no state and substate change, don't print */
1372 	if (!((state_entry == state_exit) && (substate_entry == substate_exit)))
1373 		cm_sm_print_state(cm_ctx);
1374 	cm_lock_release(cm_ctx);
1375 
1376 	return status;
1377 }
1378 
cm_sm_create(struct cnx_mgr * cm_ctx)1379 QDF_STATUS cm_sm_create(struct cnx_mgr *cm_ctx)
1380 {
1381 	struct wlan_sm *sm;
1382 	uint8_t name[WLAN_SM_ENGINE_MAX_NAME];
1383 
1384 	qdf_scnprintf(name, sizeof(name), "CM-PS_%d-VD_%d",
1385 		      wlan_psoc_get_id(wlan_vdev_get_psoc(cm_ctx->vdev)),
1386 		      wlan_vdev_get_id(cm_ctx->vdev));
1387 	sm = wlan_sm_create(name, cm_ctx,
1388 			    WLAN_CM_S_INIT,
1389 			    cm_sm_info,
1390 			    QDF_ARRAY_SIZE(cm_sm_info),
1391 			    cm_sm_event_names,
1392 			    QDF_ARRAY_SIZE(cm_sm_event_names));
1393 	if (!sm) {
1394 		mlme_err("vdev %d CM State Machine allocation failed",
1395 			 wlan_vdev_get_id(cm_ctx->vdev));
1396 		return QDF_STATUS_E_NOMEM;
1397 	}
1398 	cm_ctx->sm.sm_hdl = sm;
1399 
1400 	cm_lock_create(cm_ctx);
1401 
1402 	return QDF_STATUS_SUCCESS;
1403 }
1404 
cm_sm_destroy(struct cnx_mgr * cm_ctx)1405 QDF_STATUS cm_sm_destroy(struct cnx_mgr *cm_ctx)
1406 {
1407 	cm_lock_destroy(cm_ctx);
1408 	wlan_sm_delete(cm_ctx->sm.sm_hdl);
1409 
1410 	return QDF_STATUS_SUCCESS;
1411 }
1412 
1413 #ifdef SM_ENG_HIST_ENABLE
cm_sm_history_print(struct wlan_objmgr_vdev * vdev)1414 void cm_sm_history_print(struct wlan_objmgr_vdev *vdev)
1415 {
1416 	struct cnx_mgr *cm_ctx;
1417 
1418 	cm_ctx = cm_get_cm_ctx(vdev);
1419 	if (!cm_ctx) {
1420 		mlme_err("cm_ctx is NULL");
1421 		return;
1422 	}
1423 
1424 	return wlan_sm_print_history(cm_ctx->sm.sm_hdl);
1425 }
1426 #endif
1427