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