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