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