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