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