xref: /wlan-dirver/qca-wifi-host-cmn/umac/mlme/connection_mgr/core/src/wlan_cm_sm.c (revision d0c05845839e5f2ba5a8dcebe0cd3e4cd4e8dfcf)
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 	default:
480 		event_handled = false;
481 		break;
482 	}
483 
484 	return event_handled;
485 }
486 
487 /**
488  * cm_subst_join_pending_entry() - Entry API for join pending sub-state for
489  * connection mgr
490  * @ctx: connection manager ctx
491  *
492  * API to perform operations on moving to join pending sub-state
493  *
494  * Return: void
495  */
496 static void cm_subst_join_pending_entry(void *ctx)
497 {
498 	struct cnx_mgr *cm_ctx = ctx;
499 
500 	if (cm_get_state(cm_ctx) != WLAN_CM_S_CONNECTING)
501 		QDF_BUG(0);
502 
503 	cm_set_substate(cm_ctx, WLAN_CM_SS_JOIN_PENDING);
504 }
505 
506 /**
507  * cm_subst_join_pending_exit() - Exit API for join pending sub-state for
508  * connection mgr
509  * @ctx: connection manager ctx
510  *
511  * API to perform operations on exiting from join pending sub-state
512  *
513  * Return: void
514  */
515 static void cm_subst_join_pending_exit(void *ctx)
516 {
517 }
518 
519 /**
520  * cm_subst_join_pending_event() - Join pending sub-state event handler for
521  * connection mgr
522  * @ctx: connection manager ctx
523  *
524  * API to handle events in Join pending sub-state
525  *
526  * Return: bool
527  */
528 static bool cm_subst_join_pending_event(void *ctx, uint16_t event,
529 					uint16_t data_len, void *data)
530 {
531 	struct cnx_mgr *cm_ctx = ctx;
532 	bool event_handled = true;
533 	QDF_STATUS status = QDF_STATUS_SUCCESS;
534 	struct wlan_cm_connect_resp *resp;
535 	struct cm_req *cm_req;
536 
537 	switch (event) {
538 	case WLAN_CM_SM_EV_CONNECT_REQ:
539 		status =
540 			cm_handle_connect_req_in_non_init_state(cm_ctx, data,
541 						WLAN_CM_SS_JOIN_PENDING);
542 		if (QDF_IS_STATUS_ERROR(status)) {
543 			event_handled = false;
544 			break;
545 		}
546 		cm_sm_deliver_event_sync(cm_ctx,
547 					 WLAN_CM_SM_EV_CONNECT_START,
548 					 data_len, data);
549 		break;
550 	case WLAN_CM_SM_EV_CONNECT_START:
551 		cm_connect_start(cm_ctx, data);
552 		break;
553 	case WLAN_CM_SM_EV_CONNECT_ACTIVE:
554 		/* check if cm id is valid for the current req */
555 		if (!cm_check_cmid_match_list_head(cm_ctx, data)) {
556 			event_handled = false;
557 			break;
558 		}
559 		cm_sm_transition_to(cm_ctx, WLAN_CM_SS_JOIN_ACTIVE);
560 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
561 		break;
562 	case WLAN_CM_SM_EV_HW_MODE_SUCCESS:
563 	case WLAN_CM_SM_EV_HW_MODE_FAILURE:
564 		/* check if cm id is valid for the current req */
565 		if (!cm_check_cmid_match_list_head(cm_ctx, data)) {
566 			event_handled = false;
567 			break;
568 		}
569 		cm_handle_hw_mode_change(cm_ctx, data, event);
570 		break;
571 	case WLAN_CM_SM_EV_SCAN:
572 		cm_sm_transition_to(cm_ctx, WLAN_CM_SS_SCAN);
573 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
574 		break;
575 	case WLAN_CM_SM_EV_SCAN_FAILURE:
576 		status = QDF_STATUS_E_FAILURE;
577 		/* Fall through after setting status failure */
578 		fallthrough;
579 	case WLAN_CM_SM_EV_SCAN_SUCCESS:
580 		cm_connect_scan_resp(cm_ctx, data, status);
581 		break;
582 	case WLAN_CM_SM_EV_CONNECT_FAILURE:
583 		/* check if connect resp cm id is valid for the current req */
584 		if (!cm_connect_resp_cmid_match_list_head(cm_ctx, data)) {
585 			event_handled = false;
586 			break;
587 		}
588 		/*
589 		 * On connect req failure (before serialization), if there is a
590 		 * pending disconnect req then move to disconnecting state and
591 		 * wait for disconnect to complete before moving to INIT state.
592 		 * Else directly transition to INIT state.
593 		 *
594 		 * On disconnect completion or a new connect/disconnect req in
595 		 * disconnnecting state, the failed connect req will be flushed.
596 		 * This will ensure SM moves to INIT state after completion of
597 		 * all operation.
598 		 */
599 		if (cm_ctx->disconnect_count) {
600 			resp = data;
601 
602 			mlme_debug(CM_PREFIX_FMT "disconnect_count %d",
603 				   CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
604 						 resp->cm_id),
605 				   cm_ctx->disconnect_count);
606 			cm_req = cm_get_req_by_cm_id(cm_ctx, resp->cm_id);
607 			if (cm_req)
608 				cm_req->failed_req = true;
609 			cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
610 			break;
611 		}
612 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_INIT);
613 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
614 		break;
615 	case WLAN_CM_SM_EV_DISCONNECT_ACTIVE:
616 		cm_disconnect_active(cm_ctx, data);
617 		break;
618 	case WLAN_CM_SM_EV_DISCONNECT_DONE:
619 		cm_disconnect_complete(cm_ctx, data);
620 		break;
621 	case WLAN_CM_SM_EV_DISCONNECT_REQ:
622 		status = cm_handle_discon_req_in_non_connected_state(cm_ctx,
623 						data, WLAN_CM_SS_JOIN_PENDING);
624 		if (QDF_IS_STATUS_ERROR(status)) {
625 			event_handled = false;
626 			break;
627 		}
628 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
629 		cm_sm_deliver_event_sync(cm_ctx,
630 					 WLAN_CM_SM_EV_DISCONNECT_START,
631 					 data_len, data);
632 		break;
633 	default:
634 		event_handled = false;
635 		break;
636 	}
637 
638 	return event_handled;
639 }
640 
641 /**
642  * cm_subst_scan_entry() - Entry API for scan sub-state for
643  * connection mgr
644  * @ctx: connection manager ctx
645  *
646  * API to perform operations on moving to scan sub-state
647  *
648  * Return: void
649  */
650 static void cm_subst_scan_entry(void *ctx)
651 {
652 	struct cnx_mgr *cm_ctx = ctx;
653 
654 	if (cm_get_state(cm_ctx) != WLAN_CM_S_CONNECTING)
655 		QDF_BUG(0);
656 
657 	cm_set_substate(cm_ctx, WLAN_CM_SS_SCAN);
658 }
659 
660 /**
661  * cm_subst_scan_exit() - Exit API for scan sub-state for
662  * connection mgr
663  * @ctx: connection manager ctx
664  *
665  * API to perform operations on exiting from scan sub-state
666  *
667  * Return: void
668  */
669 static void cm_subst_scan_exit(void *ctx)
670 {
671 }
672 
673 /**
674  * cm_subst_scan_event() - Scan sub-state event handler for
675  * connection mgr
676  * @ctx: connection manager ctx
677  *
678  * API to handle events in scan sub-state
679  *
680  * Return: bool
681  */
682 static bool cm_subst_scan_event(void *ctx, uint16_t event,
683 				uint16_t data_len, void *data)
684 {
685 	struct cnx_mgr *cm_ctx = ctx;
686 	bool event_handled = true;
687 	QDF_STATUS status;
688 
689 	switch (event) {
690 	case WLAN_CM_SM_EV_CONNECT_REQ:
691 		status = cm_handle_connect_req_in_non_init_state(cm_ctx, data,
692 							WLAN_CM_SS_SCAN);
693 		if (QDF_IS_STATUS_ERROR(status)) {
694 			event_handled = false;
695 			break;
696 		}
697 		cm_sm_transition_to(cm_ctx, WLAN_CM_SS_JOIN_PENDING);
698 		cm_sm_deliver_event_sync(cm_ctx,
699 					 WLAN_CM_SM_EV_CONNECT_START,
700 					 data_len, data);
701 		break;
702 	case WLAN_CM_SM_EV_SCAN:
703 		cm_connect_scan_start(cm_ctx, data);
704 		break;
705 	case WLAN_CM_SM_EV_SCAN_SUCCESS:
706 	case WLAN_CM_SM_EV_SCAN_FAILURE:
707 		/* check if scan id is valid for the current req */
708 		if (!cm_check_scanid_match_list_head(cm_ctx, data)) {
709 			event_handled = false;
710 			break;
711 		}
712 		cm_sm_transition_to(cm_ctx, WLAN_CM_SS_JOIN_PENDING);
713 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
714 		break;
715 	case WLAN_CM_SM_EV_DISCONNECT_ACTIVE:
716 		cm_disconnect_active(cm_ctx, data);
717 		break;
718 	case WLAN_CM_SM_EV_DISCONNECT_DONE:
719 		cm_disconnect_complete(cm_ctx, data);
720 		break;
721 	case WLAN_CM_SM_EV_DISCONNECT_REQ:
722 		status = cm_handle_discon_req_in_non_connected_state(cm_ctx,
723 						data, WLAN_CM_SS_SCAN);
724 		if (QDF_IS_STATUS_ERROR(status)) {
725 			event_handled = false;
726 			break;
727 		}
728 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
729 		cm_sm_deliver_event_sync(cm_ctx,
730 					 WLAN_CM_SM_EV_DISCONNECT_START,
731 					 data_len, data);
732 		break;
733 	default:
734 		event_handled = false;
735 		break;
736 	}
737 
738 	return event_handled;
739 }
740 
741 /**
742  * cm_subst_join_active_entry() - Entry API for join active sub-state for
743  * connection mgr
744  * @ctx: connection manager ctx
745  *
746  * API to perform operations on moving to join active sub-state
747  *
748  * Return: void
749  */
750 static void cm_subst_join_active_entry(void *ctx)
751 {
752 	struct cnx_mgr *cm_ctx = ctx;
753 
754 	if (cm_get_state(cm_ctx) != WLAN_CM_S_CONNECTING)
755 		QDF_BUG(0);
756 
757 	cm_set_substate(cm_ctx, WLAN_CM_SS_JOIN_ACTIVE);
758 }
759 
760 /**
761  * cm_subst_join_active_exit() - Exit API for join active sub-state for
762  * connection mgr
763  * @ctx: connection manager ctx
764  *
765  * API to perform operations on exiting from join active sub-state
766  *
767  * Return: void
768  */
769 static void cm_subst_join_active_exit(void *ctx)
770 {
771 }
772 
773 /**
774  * cm_subst_join_active_event() - Join active sub-state event handler for
775  * connection mgr
776  * @ctx: connection manager ctx
777  *
778  * API to handle events in join active sub-state
779  *
780  * Return: bool
781  */
782 static bool cm_subst_join_active_event(void *ctx, uint16_t event,
783 				       uint16_t data_len, void *data)
784 {
785 	struct cnx_mgr *cm_ctx = ctx;
786 	bool event_handled = true;
787 	QDF_STATUS status;
788 
789 	switch (event) {
790 	case WLAN_CM_SM_EV_CONNECT_REQ:
791 		status = cm_handle_connect_req_in_non_init_state(cm_ctx, data,
792 							WLAN_CM_SS_JOIN_ACTIVE);
793 		if (QDF_IS_STATUS_ERROR(status)) {
794 			event_handled = false;
795 			break;
796 		}
797 		cm_sm_transition_to(cm_ctx, WLAN_CM_SS_JOIN_PENDING);
798 		cm_sm_deliver_event_sync(cm_ctx,
799 					 WLAN_CM_SM_EV_CONNECT_START,
800 					 data_len, data);
801 		break;
802 	case WLAN_CM_SM_EV_CONNECT_ACTIVE:
803 		cm_connect_active(cm_ctx, data);
804 		break;
805 	case WLAN_CM_SM_EV_CONNECT_SUCCESS:
806 		/* check if connect resp cm id is valid for the current req */
807 		if (!cm_connect_resp_cmid_match_list_head(cm_ctx, data)) {
808 			event_handled = false;
809 			break;
810 		}
811 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTED);
812 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
813 		break;
814 	case WLAN_CM_SM_EV_CONNECT_GET_NEXT_CANDIDATE:
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_try_next_candidate(cm_ctx, data);
821 		break;
822 	case WLAN_CM_SM_EV_CONNECT_FAILURE:
823 		/* check if connect resp cm id is valid for the current req */
824 		if (!cm_connect_resp_cmid_match_list_head(cm_ctx, data)) {
825 			event_handled = false;
826 			break;
827 		}
828 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_INIT);
829 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
830 		break;
831 	case WLAN_CM_SM_EV_BSS_SELECT_IND_SUCCESS:
832 		/* check if cm id is valid for the current req */
833 		if (!cm_check_cmid_match_list_head(cm_ctx, data)) {
834 			event_handled = false;
835 			break;
836 		}
837 		cm_peer_create_on_bss_select_ind_resp(cm_ctx, data);
838 		break;
839 	case WLAN_CM_SM_EV_BSS_CREATE_PEER_SUCCESS:
840 		/* check if cm id is valid for the current req */
841 		if (!cm_check_cmid_match_list_head(cm_ctx, data)) {
842 			event_handled = false;
843 			break;
844 		}
845 		cm_resume_connect_after_peer_create(cm_ctx, data);
846 		break;
847 	case WLAN_CM_SM_EV_DISCONNECT_REQ:
848 		status = cm_handle_discon_req_in_non_connected_state(cm_ctx,
849 						data, WLAN_CM_SS_JOIN_ACTIVE);
850 		if (QDF_IS_STATUS_ERROR(status)) {
851 			event_handled = false;
852 			break;
853 		}
854 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
855 		cm_sm_deliver_event_sync(cm_ctx,
856 					 WLAN_CM_SM_EV_DISCONNECT_START,
857 					 data_len, data);
858 		break;
859 	default:
860 		event_handled = false;
861 		break;
862 	}
863 
864 	return event_handled;
865 }
866 
867 struct wlan_sm_state_info cm_sm_info[] = {
868 	{
869 		(uint8_t)WLAN_CM_S_INIT,
870 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
871 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
872 		true,
873 		"INIT",
874 		cm_state_init_entry,
875 		cm_state_init_exit,
876 		cm_state_init_event
877 	},
878 	{
879 		(uint8_t)WLAN_CM_S_CONNECTING,
880 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
881 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
882 		true,
883 		"CONNECTING",
884 		cm_state_connecting_entry,
885 		cm_state_connecting_exit,
886 		cm_state_connecting_event
887 	},
888 	{
889 		(uint8_t)WLAN_CM_S_CONNECTED,
890 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
891 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
892 		true,
893 		"CONNECTED",
894 		cm_state_connected_entry,
895 		cm_state_connected_exit,
896 		cm_state_connected_event
897 	},
898 	{
899 		(uint8_t)WLAN_CM_S_DISCONNECTING,
900 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
901 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
902 		true,
903 		"DISCONNECTING",
904 		cm_state_disconnecting_entry,
905 		cm_state_disconnecting_exit,
906 		cm_state_disconnecting_event
907 	},
908 	{
909 		(uint8_t)WLAN_CM_S_ROAMING,
910 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
911 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
912 		true,
913 		"ROAMING",
914 		cm_state_roaming_entry,
915 		cm_state_roaming_exit,
916 		cm_state_roaming_event
917 	},
918 	{
919 		(uint8_t)WLAN_CM_S_MAX,
920 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
921 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
922 		false,
923 		"INVALID",
924 		NULL,
925 		NULL,
926 		NULL
927 	},
928 	{
929 		(uint8_t)WLAN_CM_SS_IDLE,
930 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
931 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
932 		false,
933 		"IDLE",
934 		NULL,
935 		NULL,
936 		NULL
937 	},
938 	{
939 		(uint8_t)WLAN_CM_SS_JOIN_PENDING,
940 		(uint8_t)WLAN_CM_S_CONNECTING,
941 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
942 		false,
943 		"JOIN_PENDING",
944 		cm_subst_join_pending_entry,
945 		cm_subst_join_pending_exit,
946 		cm_subst_join_pending_event
947 	},
948 	{
949 		(uint8_t)WLAN_CM_SS_SCAN,
950 		(uint8_t)WLAN_CM_S_CONNECTING,
951 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
952 		false,
953 		"SCAN",
954 		cm_subst_scan_entry,
955 		cm_subst_scan_exit,
956 		cm_subst_scan_event
957 	},
958 	{
959 		(uint8_t)WLAN_CM_SS_JOIN_ACTIVE,
960 		(uint8_t)WLAN_CM_S_CONNECTING,
961 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
962 		false,
963 		"JOIN_ACTIVE",
964 		cm_subst_join_active_entry,
965 		cm_subst_join_active_exit,
966 		cm_subst_join_active_event
967 	},
968 	{
969 		(uint8_t)WLAN_CM_SS_PREAUTH,
970 		(uint8_t)WLAN_CM_S_ROAMING,
971 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
972 		false,
973 		"PREAUTH",
974 		cm_subst_preauth_entry,
975 		cm_subst_preauth_exit,
976 		cm_subst_preauth_event
977 	},
978 	{
979 		(uint8_t)WLAN_CM_SS_REASSOC,
980 		(uint8_t)WLAN_CM_S_ROAMING,
981 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
982 		false,
983 		"REASSOC",
984 		cm_subst_reassoc_entry,
985 		cm_subst_reassoc_exit,
986 		cm_subst_reassoc_event
987 	},
988 	{
989 		(uint8_t)WLAN_CM_SS_ROAM_STARTED,
990 		(uint8_t)WLAN_CM_S_ROAMING,
991 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
992 		false,
993 		"ROAM_START",
994 		cm_subst_roam_start_entry,
995 		cm_subst_roam_start_exit,
996 		cm_subst_roam_start_event
997 	},
998 	{
999 		(uint8_t)WLAN_CM_SS_ROAM_SYNC,
1000 		(uint8_t)WLAN_CM_S_ROAMING,
1001 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1002 		false,
1003 		"ROAM_SYNC",
1004 		cm_subst_roam_sync_entry,
1005 		cm_subst_roam_sync_exit,
1006 		cm_subst_roam_sync_event
1007 	},
1008 	{
1009 		(uint8_t)WLAN_CM_SS_MAX,
1010 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1011 		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
1012 		false,
1013 		"INVALID",
1014 		NULL,
1015 		NULL,
1016 		NULL
1017 	},
1018 };
1019 
1020 static const char *cm_sm_event_names[] = {
1021 	"EV_CONNECT_REQ",
1022 	"EV_SCAN",
1023 	"EV_SCAN_SUCCESS",
1024 	"EV_SCAN_FAILURE",
1025 	"EV_HW_MODE_SUCCESS",
1026 	"EV_HW_MODE_FAILURE",
1027 	"EV_CONNECT_START",
1028 	"EV_CONNECT_ACTIVE",
1029 	"EV_CONNECT_SUCCESS",
1030 	"EV_BSS_SELECT_IND_SUCCESS",
1031 	"EV_BSS_CREATE_PEER_SUCCESS",
1032 	"EV_CONNECT_GET_NXT_CANDIDATE",
1033 	"EV_CONNECT_FAILURE",
1034 	"EV_DISCONNECT_REQ",
1035 	"EV_DISCONNECT_START",
1036 	"EV_DISCONNECT_ACTIVE",
1037 	"EV_DISCONNECT_DONE",
1038 	"EV_ROAM_START",
1039 	"EV_ROAM_SYNC",
1040 	"EV_ROAM_INVOKE_FAIL",
1041 	"EV_ROAM_HO_FAIL",
1042 	"EV_PREAUTH_DONE",
1043 	"EV_GET_NEXT_PREAUTH_AP",
1044 	"EV_PREAUTH_FAIL",
1045 	"EV_START_REASSOC",
1046 	"EV_REASSOC_ACTIVE",
1047 	"EV_REASSOC_DONE",
1048 	"EV_REASSOC_FAILURE",
1049 	"EV_ROAM_COMPLETE",
1050 	"EV_ROAM_REQ",
1051 	"EV_ROAM_INVOKE",
1052 	"EV_ROAM_ABORT",
1053 	"EV_ROAM_DONE",
1054 	"EV_PREAUTH_ACTIVE",
1055 	"EV_PREAUTH_RESP",
1056 	"EV_REASSOC_TIMER",
1057 	"EV_HO_ROAM_DISCONNECT_DONE",
1058 };
1059 
1060 enum wlan_cm_sm_state cm_get_state(struct cnx_mgr *cm_ctx)
1061 {
1062 	enum QDF_OPMODE op_mode;
1063 
1064 	if (!cm_ctx || !cm_ctx->vdev)
1065 		return WLAN_CM_S_MAX;
1066 
1067 	op_mode = wlan_vdev_mlme_get_opmode(cm_ctx->vdev);
1068 
1069 	if (op_mode != QDF_STA_MODE && op_mode != QDF_P2P_CLIENT_MODE)
1070 		return WLAN_CM_S_MAX;
1071 
1072 	return cm_ctx->sm.cm_state;
1073 }
1074 
1075 enum wlan_cm_sm_state cm_get_sub_state(struct cnx_mgr *cm_ctx)
1076 {
1077 	enum QDF_OPMODE op_mode;
1078 
1079 	if (!cm_ctx || !cm_ctx->vdev)
1080 		return WLAN_CM_SS_MAX;
1081 
1082 	op_mode = wlan_vdev_mlme_get_opmode(cm_ctx->vdev);
1083 
1084 	if (op_mode != QDF_STA_MODE && op_mode != QDF_P2P_CLIENT_MODE)
1085 		return WLAN_CM_SS_MAX;
1086 
1087 	return cm_ctx->sm.cm_substate;
1088 }
1089 
1090 static void cm_sm_print_state_event(struct cnx_mgr *cm_ctx,
1091 				    enum wlan_cm_sm_evt event)
1092 {
1093 	enum wlan_cm_sm_state state;
1094 	enum wlan_cm_sm_state substate;
1095 
1096 	state = cm_get_state(cm_ctx);
1097 	substate = cm_get_sub_state(cm_ctx);
1098 
1099 	mlme_nofl_debug("[%s]%s - %s, %s", cm_ctx->sm.sm_hdl->name,
1100 			cm_sm_info[state].name, cm_sm_info[substate].name,
1101 			cm_sm_event_names[event]);
1102 }
1103 
1104 static void cm_sm_print_state(struct cnx_mgr *cm_ctx)
1105 {
1106 	enum wlan_cm_sm_state state;
1107 	enum wlan_cm_sm_state substate;
1108 
1109 	state = cm_get_state(cm_ctx);
1110 	substate = cm_get_sub_state(cm_ctx);
1111 
1112 	mlme_nofl_debug("[%s]%s - %s", cm_ctx->sm.sm_hdl->name,
1113 			cm_sm_info[state].name, cm_sm_info[substate].name);
1114 }
1115 
1116 QDF_STATUS cm_sm_deliver_event(struct wlan_objmgr_vdev *vdev,
1117 			       enum wlan_cm_sm_evt event,
1118 			       uint16_t data_len, void *data)
1119 {
1120 	QDF_STATUS status;
1121 	enum wlan_cm_sm_state state_entry, state_exit;
1122 	enum wlan_cm_sm_state substate_entry, substate_exit;
1123 	enum QDF_OPMODE op_mode = wlan_vdev_mlme_get_opmode(vdev);
1124 	struct cnx_mgr *cm_ctx;
1125 
1126 	if (op_mode != QDF_STA_MODE && op_mode != QDF_P2P_CLIENT_MODE) {
1127 		mlme_err("vdev %d Invalid mode %d",
1128 			 wlan_vdev_get_id(vdev), op_mode);
1129 		return QDF_STATUS_E_NOSUPPORT;
1130 	}
1131 
1132 	cm_ctx = cm_get_cm_ctx(vdev);
1133 	if (!cm_ctx)
1134 		return QDF_STATUS_E_FAILURE;
1135 
1136 	cm_lock_acquire(cm_ctx);
1137 
1138 	/* store entry state and sub state for prints */
1139 	state_entry = cm_get_state(cm_ctx);
1140 	substate_entry = cm_get_sub_state(cm_ctx);
1141 	cm_sm_print_state_event(cm_ctx, event);
1142 
1143 	status = cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
1144 	/* Take exit state, exit substate for prints */
1145 	state_exit = cm_get_state(cm_ctx);
1146 	substate_exit = cm_get_sub_state(cm_ctx);
1147 	/* If no state and substate change, don't print */
1148 	if (!((state_entry == state_exit) && (substate_entry == substate_exit)))
1149 		cm_sm_print_state(cm_ctx);
1150 	cm_lock_release(cm_ctx);
1151 
1152 	return status;
1153 }
1154 
1155 QDF_STATUS cm_sm_create(struct cnx_mgr *cm_ctx)
1156 {
1157 	struct wlan_sm *sm;
1158 	uint8_t name[WLAN_SM_ENGINE_MAX_NAME];
1159 
1160 	qdf_scnprintf(name, sizeof(name), "CM-VDEV-%d",
1161 		      wlan_vdev_get_id(cm_ctx->vdev));
1162 	sm = wlan_sm_create(name, cm_ctx,
1163 			    WLAN_CM_S_INIT,
1164 			    cm_sm_info,
1165 			    QDF_ARRAY_SIZE(cm_sm_info),
1166 			    cm_sm_event_names,
1167 			    QDF_ARRAY_SIZE(cm_sm_event_names));
1168 	if (!sm) {
1169 		mlme_err("vdev %d CM State Machine allocation failed",
1170 			 wlan_vdev_get_id(cm_ctx->vdev));
1171 		return QDF_STATUS_E_NOMEM;
1172 	}
1173 	cm_ctx->sm.sm_hdl = sm;
1174 
1175 	cm_lock_create(cm_ctx);
1176 
1177 	return QDF_STATUS_SUCCESS;
1178 }
1179 
1180 QDF_STATUS cm_sm_destroy(struct cnx_mgr *cm_ctx)
1181 {
1182 	cm_lock_destroy(cm_ctx);
1183 	wlan_sm_delete(cm_ctx->sm.sm_hdl);
1184 
1185 	return QDF_STATUS_SUCCESS;
1186 }
1187 
1188 #ifdef SM_ENG_HIST_ENABLE
1189 void cm_sm_history_print(struct wlan_objmgr_vdev *vdev)
1190 {
1191 	struct cnx_mgr *cm_ctx;
1192 
1193 	cm_ctx = cm_get_cm_ctx(vdev);
1194 	if (!cm_ctx) {
1195 		mlme_err("cm_ctx is NULL");
1196 		return;
1197 	}
1198 
1199 	return wlan_sm_print_history(cm_ctx->sm.sm_hdl);
1200 }
1201 #endif
1202