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