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