1 /* 2 * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved. 3 * Copyright (c) 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 #include "wlan_cm_main.h" 19 #include "wlan_cm_roam_sm.h" 20 #include "wlan_cm_sm.h" 21 #include <include/wlan_mlme_cmn.h> 22 #include "wlan_cm_main_api.h" 23 #include <wlan_scan_api.h> 24 #include <wlan_serialization_api.h> 25 #include <wlan_utility.h> 26 #include <wlan_cm_api.h> 27 #ifdef WLAN_POLICY_MGR_ENABLE 28 #include "wlan_policy_mgr_api.h" 29 #endif 30 31 static void 32 cm_fill_roam_fail_resp_from_cm_id(struct cnx_mgr *cm_ctx, 33 struct wlan_cm_connect_resp *resp, 34 wlan_cm_id cm_id, 35 enum wlan_cm_connect_fail_reason reason) 36 { 37 resp->connect_status = QDF_STATUS_E_FAILURE; 38 resp->cm_id = cm_id; 39 resp->vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 40 resp->reason = reason; 41 cm_fill_bss_info_in_roam_rsp_by_cm_id(cm_ctx, cm_id, resp); 42 } 43 44 static QDF_STATUS 45 cm_reassoc_fail_disconnect(struct wlan_objmgr_vdev *vdev, 46 enum wlan_cm_source source, 47 enum wlan_reason_code reason_code, 48 struct qdf_mac_addr *bssid) 49 { 50 struct cnx_mgr *cm_ctx; 51 struct cm_req *cm_req; 52 struct cm_disconnect_req *disconnect_req; 53 struct wlan_cm_disconnect_req req = {0}; 54 QDF_STATUS status; 55 56 cm_ctx = cm_get_cm_ctx(vdev); 57 if (!cm_ctx) 58 return QDF_STATUS_E_INVAL; 59 60 /* 61 * This would be freed as part of removal from cm req list if adding 62 * to list is success after posting WLAN_CM_SM_EV_DISCONNECT_REQ. 63 */ 64 cm_req = qdf_mem_malloc(sizeof(*cm_req)); 65 if (!cm_req) 66 return QDF_STATUS_E_NOMEM; 67 68 req.vdev_id = wlan_vdev_get_id(vdev); 69 req.source = source; 70 req.reason_code = reason_code; 71 if (bssid) 72 qdf_copy_macaddr(&req.bssid, bssid); 73 74 disconnect_req = &cm_req->discon_req; 75 disconnect_req->req = req; 76 77 status = cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_DISCONNECT_REQ, 78 sizeof(*disconnect_req), 79 disconnect_req); 80 if (QDF_IS_STATUS_ERROR(status)) 81 qdf_mem_free(cm_req); 82 83 return status; 84 } 85 86 QDF_STATUS 87 cm_send_reassoc_start_fail(struct cnx_mgr *cm_ctx, 88 wlan_cm_id cm_id, 89 enum wlan_cm_connect_fail_reason reason, 90 bool sync) 91 { 92 struct wlan_cm_connect_resp *resp; 93 QDF_STATUS status; 94 95 resp = qdf_mem_malloc(sizeof(*resp)); 96 if (!resp) 97 return QDF_STATUS_E_NOMEM; 98 99 cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id, reason); 100 if (sync) 101 status = cm_sm_deliver_event_sync( 102 cm_ctx, WLAN_CM_SM_EV_REASSOC_FAILURE, 103 sizeof(*resp), resp); 104 else 105 status = cm_sm_deliver_event(cm_ctx->vdev, 106 WLAN_CM_SM_EV_REASSOC_FAILURE, 107 sizeof(*resp), resp); 108 109 if (QDF_IS_STATUS_ERROR(status)) 110 cm_reassoc_complete(cm_ctx, resp); 111 112 qdf_mem_free(resp); 113 114 return status; 115 } 116 117 #ifdef CONN_MGR_ADV_FEATURE 118 static QDF_STATUS 119 cm_update_roam_scan_filter( 120 struct wlan_objmgr_vdev *vdev, struct cm_roam_req *cm_req, 121 struct scan_filter *filter, bool security_valid_for_6ghz) 122 { 123 return cm_update_advance_roam_scan_filter(vdev, filter); 124 } 125 #else 126 static QDF_STATUS 127 cm_update_roam_scan_filter( 128 struct wlan_objmgr_vdev *vdev, struct cm_roam_req *cm_req, 129 struct scan_filter *filter, bool security_valid_for_6ghz) 130 { 131 uint16_t rsn_caps; 132 133 filter->num_of_ssid = 1; 134 wlan_vdev_mlme_get_ssid(vdev, filter->ssid_list[0].ssid, 135 &filter->ssid_list[0].length); 136 137 if (cm_req->req.chan_freq) { 138 filter->num_of_channels = 1; 139 filter->chan_freq_list[0] = cm_req->req.chan_freq; 140 } 141 142 /* Security is not valid for 6Ghz so ignore 6Ghz APs */ 143 if (!security_valid_for_6ghz) 144 filter->ignore_6ghz_channel = true; 145 146 if (!QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_WAPI) && 147 !QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_RSNA) && 148 !QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_WPA)) { 149 filter->ignore_auth_enc_type = 1; 150 } 151 152 rsn_caps = 153 wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_RSN_CAP); 154 155 if (rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED) 156 filter->pmf_cap = WLAN_PMF_REQUIRED; 157 else if (rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_ENABLED) 158 filter->pmf_cap = WLAN_PMF_CAPABLE; 159 else 160 filter->pmf_cap = WLAN_PMF_DISABLED; 161 return QDF_STATUS_SUCCESS; 162 } 163 #endif 164 165 static QDF_STATUS cm_connect_prepare_scan_filter_for_roam( 166 struct cnx_mgr *cm_ctx, struct cm_roam_req *cm_req, 167 struct scan_filter *filter, bool security_valid_for_6ghz) 168 { 169 struct wlan_objmgr_vdev *vdev = cm_ctx->vdev; 170 171 if (!qdf_is_macaddr_zero(&cm_req->req.bssid)) { 172 filter->num_of_bssid = 1; 173 qdf_copy_macaddr(&filter->bssid_list[0], &cm_req->req.bssid); 174 } 175 176 filter->authmodeset = 177 wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_AUTH_MODE); 178 179 filter->ucastcipherset = 180 wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_UCAST_CIPHER); 181 182 filter->mcastcipherset = 183 wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_MCAST_CIPHER); 184 185 filter->key_mgmt = 186 wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT); 187 188 filter->mgmtcipherset = 189 wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_MGMT_CIPHER); 190 191 return cm_update_roam_scan_filter(vdev, cm_req, filter, 192 security_valid_for_6ghz); 193 } 194 195 static QDF_STATUS cm_roam_get_candidates(struct wlan_objmgr_pdev *pdev, 196 struct cnx_mgr *cm_ctx, 197 struct cm_roam_req *cm_req) 198 { 199 struct scan_filter *filter; 200 uint32_t num_bss = 0; 201 enum QDF_OPMODE op_mode; 202 qdf_list_t *candidate_list; 203 uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 204 qdf_list_node_t *cur_node = NULL; 205 bool security_valid_for_6ghz = true; 206 207 filter = qdf_mem_malloc(sizeof(*filter)); 208 if (!filter) 209 return QDF_STATUS_E_NOMEM; 210 211 cm_connect_prepare_scan_filter_for_roam(cm_ctx, cm_req, filter, 212 security_valid_for_6ghz); 213 214 candidate_list = wlan_scan_get_result(pdev, filter); 215 if (candidate_list) { 216 num_bss = qdf_list_size(candidate_list); 217 mlme_debug(CM_PREFIX_FMT "num_entries found %d", 218 CM_PREFIX_REF(vdev_id, cm_req->cm_id), num_bss); 219 } 220 221 op_mode = wlan_vdev_mlme_get_opmode(cm_ctx->vdev); 222 if (num_bss && op_mode == QDF_STA_MODE) 223 cm_calculate_scores(cm_ctx, pdev, filter, candidate_list); 224 225 qdf_mem_free(filter); 226 227 if (!candidate_list || !qdf_list_size(candidate_list)) { 228 if (candidate_list) 229 wlan_scan_purge_results(candidate_list); 230 231 mlme_info(CM_PREFIX_FMT "no valid candidate found, num_bss %d", 232 CM_PREFIX_REF(vdev_id, cm_req->cm_id), num_bss); 233 cm_req->candidate_list = NULL; 234 return QDF_STATUS_E_EMPTY; 235 } 236 237 qdf_list_peek_front(candidate_list, &cur_node); 238 cm_req->candidate_list = candidate_list; 239 cm_req->cur_candidate = qdf_container_of(cur_node, 240 struct scan_cache_node, 241 node); 242 return QDF_STATUS_SUCCESS; 243 } 244 245 #ifdef WLAN_FEATURE_PREAUTH_ENABLE 246 QDF_STATUS cm_handle_reassoc_timer(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id) 247 { 248 struct cm_req *cm_req; 249 250 if (!cm_id) 251 return QDF_STATUS_E_INVAL; 252 253 cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id); 254 if (!cm_req) 255 return QDF_STATUS_E_INVAL; 256 257 return cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_START_REASSOC, 258 sizeof(cm_req->roam_req), 259 &cm_req->roam_req); 260 } 261 262 static QDF_STATUS cm_host_roam_start(struct cnx_mgr *cm_ctx, 263 struct cm_req *cm_req) 264 { 265 struct wlan_cm_roam_req *req; 266 struct qdf_mac_addr connected_bssid; 267 268 req = &cm_req->roam_req.req; 269 270 wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &connected_bssid); 271 if (qdf_is_macaddr_equal(&req->bssid, &connected_bssid)) { 272 mlme_info(CM_PREFIX_FMT "Self reassoc with" QDF_MAC_ADDR_FMT, 273 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), 274 cm_req->cm_id), 275 QDF_MAC_ADDR_REF(req->bssid.bytes)); 276 req->self_reassoc = true; 277 } 278 279 /* if self reassoc continue with reassoc and skip preauth */ 280 if (req->self_reassoc) 281 return cm_sm_deliver_event_sync(cm_ctx, 282 WLAN_CM_SM_EV_START_REASSOC, 283 sizeof(cm_req->roam_req), 284 &cm_req->roam_req); 285 /* 286 * if not self reassoc reset cur candidate to perform preauth with 287 * all candidate. 288 */ 289 cm_req->roam_req.cur_candidate = NULL; 290 return cm_host_roam_preauth_start(cm_ctx, cm_req); 291 } 292 293 static 294 QDF_STATUS cm_host_roam_start_fail(struct cnx_mgr *cm_ctx, 295 struct cm_req *cm_req, 296 enum wlan_cm_connect_fail_reason reason) 297 { 298 cm_send_preauth_start_fail(cm_ctx, cm_req->cm_id, reason); 299 300 return QDF_STATUS_SUCCESS; 301 } 302 #else 303 static QDF_STATUS cm_host_roam_start(struct cnx_mgr *cm_ctx, 304 struct cm_req *cm_req) 305 { 306 return cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_START_REASSOC, 307 sizeof(cm_req->roam_req), 308 &cm_req->roam_req); 309 } 310 311 static 312 QDF_STATUS cm_host_roam_start_fail(struct cnx_mgr *cm_ctx, 313 struct cm_req *cm_req, 314 enum wlan_cm_connect_fail_reason reason) 315 { 316 return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true); 317 } 318 #endif 319 320 QDF_STATUS cm_host_roam_start_req(struct cnx_mgr *cm_ctx, 321 struct cm_req *cm_req) 322 { 323 QDF_STATUS status; 324 struct wlan_objmgr_pdev *pdev; 325 enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE; 326 327 mlme_cm_roam_start_ind(cm_ctx->vdev, &cm_req->roam_req.req); 328 329 pdev = wlan_vdev_get_pdev(cm_ctx->vdev); 330 if (!pdev) { 331 reason = CM_GENERIC_FAILURE; 332 goto roam_err; 333 } 334 335 status = cm_roam_get_candidates(pdev, cm_ctx, &cm_req->roam_req); 336 if (QDF_IS_STATUS_ERROR(status)) { 337 reason = CM_NO_CANDIDATE_FOUND; 338 goto roam_err; 339 } 340 341 status = cm_host_roam_start(cm_ctx, cm_req); 342 if (QDF_IS_STATUS_SUCCESS(status)) 343 return status; 344 345 roam_err: 346 return cm_host_roam_start_fail(cm_ctx, cm_req, reason); 347 } 348 349 bool cm_roam_resp_cmid_match_list_head(struct cnx_mgr *cm_ctx, 350 struct wlan_cm_connect_resp *resp) 351 { 352 return cm_check_cmid_match_list_head(cm_ctx, &resp->cm_id); 353 } 354 355 QDF_STATUS cm_reassoc_active(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id) 356 { 357 struct cm_req *cm_req; 358 struct wlan_cm_vdev_discon_req req; 359 struct cm_disconnect_req *discon_req; 360 361 cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id); 362 if (!cm_req) 363 return QDF_STATUS_E_INVAL; 364 365 cm_ctx->active_cm_id = *cm_id; 366 367 /* For self reassoc no need to disconnect or create peer */ 368 if (cm_req->roam_req.req.self_reassoc) 369 return cm_resume_reassoc_after_peer_create(cm_ctx, cm_id); 370 371 qdf_mem_zero(&req, sizeof(req)); 372 req.cm_id = *cm_id; 373 req.req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 374 req.req.source = CM_ROAM_DISCONNECT; 375 wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &req.req.bssid); 376 377 discon_req = qdf_mem_malloc(sizeof(*discon_req)); 378 if (!discon_req) 379 return QDF_STATUS_E_NOMEM; 380 381 discon_req->cm_id = *cm_id; 382 discon_req->req.vdev_id = req.req.vdev_id; 383 qdf_copy_macaddr(&discon_req->req.bssid, 384 &req.req.bssid); 385 cm_update_scan_mlme_on_disconnect(cm_ctx->vdev, discon_req); 386 qdf_mem_free(discon_req); 387 388 return mlme_cm_disconnect_req(cm_ctx->vdev, &req); 389 } 390 391 QDF_STATUS cm_reassoc_disconnect_complete(struct cnx_mgr *cm_ctx, 392 struct wlan_cm_discon_rsp *resp) 393 { 394 QDF_STATUS status; 395 struct cm_req *cm_req; 396 struct qdf_mac_addr *bssid; 397 uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 398 wlan_cm_id cm_id = cm_ctx->active_cm_id; 399 400 cm_req = cm_get_req_by_cm_id(cm_ctx, cm_id); 401 if (!cm_req) 402 return QDF_STATUS_E_INVAL; 403 404 mlme_cm_disconnect_complete_ind(cm_ctx->vdev, resp); 405 bssid = &cm_req->roam_req.cur_candidate->entry->bssid; 406 407 status = mlme_cm_bss_peer_create_req(cm_ctx->vdev, bssid, NULL, false); 408 if (QDF_IS_STATUS_ERROR(status)) { 409 mlme_err(CM_PREFIX_FMT "Peer create request failed", 410 CM_PREFIX_REF(vdev_id, cm_id)); 411 status = cm_send_reassoc_start_fail(cm_ctx, cm_id, 412 CM_PEER_CREATE_FAILED, 413 true); 414 } 415 416 return status; 417 } 418 419 QDF_STATUS 420 cm_resume_reassoc_after_peer_create(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id) 421 { 422 struct wlan_cm_vdev_reassoc_req *req; 423 struct cm_req *cm_req; 424 QDF_STATUS status; 425 426 cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id); 427 if (!cm_req) 428 return QDF_STATUS_E_FAILURE; 429 430 req = qdf_mem_malloc(sizeof(*req)); 431 if (!req) 432 return QDF_STATUS_E_NOMEM; 433 434 req->vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 435 req->cm_id = *cm_id; 436 req->self_reassoc = cm_req->roam_req.req.self_reassoc; 437 req->bss = cm_req->roam_req.cur_candidate; 438 439 mlme_nofl_info(CM_PREFIX_FMT "Reassoc to " QDF_SSID_FMT " " QDF_MAC_ADDR_FMT " rssi: %d freq: %d source %d", 440 CM_PREFIX_REF(req->vdev_id, req->cm_id), 441 QDF_SSID_REF(req->bss->entry->ssid.length, 442 req->bss->entry->ssid.ssid), 443 QDF_MAC_ADDR_REF(req->bss->entry->bssid.bytes), 444 req->bss->entry->rssi_raw, 445 req->bss->entry->channel.chan_freq, 446 cm_req->roam_req.req.source); 447 448 status = mlme_cm_reassoc_req(cm_ctx->vdev, req); 449 if (QDF_IS_STATUS_ERROR(status)) { 450 mlme_err(CM_PREFIX_FMT "Reassoc request failed", 451 CM_PREFIX_REF(req->vdev_id, req->cm_id)); 452 /* Delete peer only if not self reassoc */ 453 if (!cm_req->roam_req.req.self_reassoc) 454 mlme_cm_bss_peer_delete_req(cm_ctx->vdev); 455 status = cm_send_reassoc_start_fail(cm_ctx, *cm_id, 456 CM_JOIN_FAILED, true); 457 } 458 459 qdf_mem_free(req); 460 return status; 461 } 462 463 QDF_STATUS cm_reassoc_complete(struct cnx_mgr *cm_ctx, 464 struct wlan_cm_connect_resp *resp) 465 { 466 /* 467 * If the entry is not present in the list, it must have been cleared 468 * already. 469 */ 470 if (!cm_get_req_by_cm_id(cm_ctx, resp->cm_id)) 471 return QDF_STATUS_SUCCESS; 472 473 resp->is_reassoc = true; 474 cm_connect_complete(cm_ctx, resp); 475 /* 476 * If roaming fails and conn_sm is in ROAMING state, then 477 * initiate disconnect to cleanup and move conn_sm to INIT state 478 */ 479 if (QDF_IS_STATUS_ERROR(resp->connect_status) && 480 cm_get_state(cm_ctx) == WLAN_CM_S_ROAMING) { 481 cm_reassoc_fail_disconnect(cm_ctx->vdev, CM_ROAM_DISCONNECT, 482 REASON_UNSPEC_FAILURE, 483 &resp->bssid); 484 } 485 486 return QDF_STATUS_SUCCESS; 487 } 488 489 static void 490 cm_reassoc_handle_event_post_fail(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id) 491 { 492 struct wlan_cm_connect_resp *resp; 493 494 resp = qdf_mem_malloc(sizeof(*resp)); 495 if (!resp) 496 return; 497 498 cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id, 499 CM_GENERIC_FAILURE); 500 cm_reassoc_complete(cm_ctx, resp); 501 qdf_mem_free(resp); 502 } 503 504 static QDF_STATUS cm_reassoc_cmd_timeout(struct cnx_mgr *cm_ctx, 505 wlan_cm_id cm_id) 506 { 507 struct wlan_cm_connect_resp *resp; 508 QDF_STATUS status; 509 510 resp = qdf_mem_malloc(sizeof(*resp)); 511 if (!resp) 512 return QDF_STATUS_E_NOMEM; 513 514 cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id, CM_SER_TIMEOUT); 515 status = cm_sm_deliver_event(cm_ctx->vdev, 516 WLAN_CM_SM_EV_REASSOC_FAILURE, 517 sizeof(*resp), resp); 518 if (QDF_IS_STATUS_ERROR(status)) 519 cm_reassoc_complete(cm_ctx, resp); 520 521 qdf_mem_free(resp); 522 523 return status; 524 } 525 526 #ifdef WLAN_CM_USE_SPINLOCK 527 static QDF_STATUS cm_activate_reassoc_req_sched_cb(struct scheduler_msg *msg) 528 { 529 struct wlan_serialization_command *cmd = msg->bodyptr; 530 struct wlan_objmgr_vdev *vdev; 531 struct cnx_mgr *cm_ctx; 532 QDF_STATUS ret = QDF_STATUS_E_FAILURE; 533 534 if (!cmd || !cmd->vdev) { 535 mlme_err("Invalid Input"); 536 return QDF_STATUS_E_INVAL; 537 } 538 539 vdev = cmd->vdev; 540 cm_ctx = cm_get_cm_ctx(vdev); 541 if (!cm_ctx) 542 return QDF_STATUS_E_INVAL; 543 544 ret = cm_sm_deliver_event(vdev, 545 WLAN_CM_SM_EV_REASSOC_ACTIVE, 546 sizeof(wlan_cm_id), 547 &cmd->cmd_id); 548 /* 549 * Called from scheduler context hence posting failure 550 */ 551 if (QDF_IS_STATUS_ERROR(ret)) 552 cm_reassoc_handle_event_post_fail(cm_ctx, cmd->cmd_id); 553 554 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 555 return ret; 556 } 557 558 static QDF_STATUS 559 cm_activate_reassoc_req(struct wlan_serialization_command *cmd) 560 { 561 struct wlan_objmgr_vdev *vdev = cmd->vdev; 562 struct scheduler_msg msg = {0}; 563 QDF_STATUS ret; 564 565 msg.bodyptr = cmd; 566 msg.callback = cm_activate_reassoc_req_sched_cb; 567 msg.flush_callback = cm_activate_cmd_req_flush_cb; 568 569 ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_MLME_CM_ID); 570 if (QDF_IS_STATUS_ERROR(ret)) 571 return ret; 572 573 ret = scheduler_post_message(QDF_MODULE_ID_MLME, 574 QDF_MODULE_ID_MLME, 575 QDF_MODULE_ID_MLME, &msg); 576 577 if (QDF_IS_STATUS_ERROR(ret)) { 578 mlme_err(CM_PREFIX_FMT "Failed to post scheduler_msg", 579 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id)); 580 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 581 return ret; 582 } 583 584 return ret; 585 } 586 #else 587 static QDF_STATUS 588 cm_activate_reassoc_req(struct wlan_serialization_command *cmd) 589 { 590 return cm_sm_deliver_event(cmd->vdev, 591 WLAN_CM_SM_EV_REASSOC_ACTIVE, 592 sizeof(wlan_cm_id), 593 &cmd->cmd_id); 594 } 595 #endif 596 597 static QDF_STATUS 598 cm_ser_reassoc_cb(struct wlan_serialization_command *cmd, 599 enum wlan_serialization_cb_reason reason) 600 { 601 QDF_STATUS status = QDF_STATUS_SUCCESS; 602 struct wlan_objmgr_vdev *vdev; 603 struct cnx_mgr *cm_ctx; 604 605 if (!cmd) { 606 mlme_err("cmd is NULL, reason: %d", reason); 607 QDF_ASSERT(0); 608 return QDF_STATUS_E_NULL_VALUE; 609 } 610 611 vdev = cmd->vdev; 612 cm_ctx = cm_get_cm_ctx(vdev); 613 if (!cm_ctx) 614 return QDF_STATUS_E_NULL_VALUE; 615 616 switch (reason) { 617 case WLAN_SER_CB_ACTIVATE_CMD: 618 /* 619 * For pending to active reason, use async api to take lock. 620 * For direct activation use sync api to avoid taking lock 621 * as lock is already acquired by the requester. 622 */ 623 if (cmd->activation_reason == SER_PENDING_TO_ACTIVE) 624 status = cm_activate_reassoc_req(cmd); 625 else 626 status = cm_sm_deliver_event_sync( 627 cm_ctx, WLAN_CM_SM_EV_REASSOC_ACTIVE, 628 sizeof(wlan_cm_id), &cmd->cmd_id); 629 630 if (QDF_IS_STATUS_SUCCESS(status)) 631 break; 632 /* 633 * Handle failure if posting fails, i.e. the SM state has 634 * changed or head cm_id doesn't match the active cm_id. 635 * connect active should be handled only in JOIN_PENDING. If 636 * new command has been received connect activation should be 637 * aborted from here with connect req cleanup. 638 */ 639 cm_reassoc_handle_event_post_fail(cm_ctx, cmd->cmd_id); 640 break; 641 case WLAN_SER_CB_CANCEL_CMD: 642 /* command removed from pending list. */ 643 break; 644 case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: 645 mlme_err(CM_PREFIX_FMT "Active command timeout", 646 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id)); 647 cm_trigger_panic_on_cmd_timeout(cm_ctx->vdev); 648 cm_reassoc_cmd_timeout(cm_ctx, cmd->cmd_id); 649 break; 650 case WLAN_SER_CB_RELEASE_MEM_CMD: 651 cm_reset_active_cm_id(vdev, cmd->cmd_id); 652 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 653 break; 654 default: 655 QDF_ASSERT(0); 656 status = QDF_STATUS_E_INVAL; 657 break; 658 } 659 660 return status; 661 } 662 663 #define REASSOC_TIMEOUT 10000 664 static QDF_STATUS cm_ser_reassoc_req(struct cnx_mgr *cm_ctx, 665 struct cm_roam_req *cm_req) 666 { 667 struct wlan_serialization_command cmd = {0, }; 668 enum wlan_serialization_status ser_cmd_status; 669 QDF_STATUS status; 670 uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 671 672 mlme_cm_osif_roam_sync_ind(cm_ctx->vdev); 673 674 status = wlan_objmgr_vdev_try_get_ref(cm_ctx->vdev, WLAN_MLME_CM_ID); 675 if (QDF_IS_STATUS_ERROR(status)) { 676 mlme_err(CM_PREFIX_FMT "unable to get reference", 677 CM_PREFIX_REF(vdev_id, cm_req->cm_id)); 678 return status; 679 } 680 681 cmd.cmd_type = WLAN_SER_CMD_VDEV_ROAM; 682 cmd.cmd_id = cm_req->cm_id; 683 cmd.cmd_cb = cm_ser_reassoc_cb; 684 cmd.source = WLAN_UMAC_COMP_MLME; 685 cmd.is_high_priority = false; 686 cmd.cmd_timeout_duration = REASSOC_TIMEOUT; 687 cmd.vdev = cm_ctx->vdev; 688 cmd.is_blocking = cm_ser_get_blocking_cmd(); 689 690 ser_cmd_status = wlan_serialization_request(&cmd); 691 switch (ser_cmd_status) { 692 case WLAN_SER_CMD_PENDING: 693 /* command moved to pending list.Do nothing */ 694 break; 695 case WLAN_SER_CMD_ACTIVE: 696 /* command moved to active list. Do nothing */ 697 break; 698 default: 699 mlme_err(CM_PREFIX_FMT "ser cmd status %d", 700 CM_PREFIX_REF(vdev_id, cm_req->cm_id), ser_cmd_status); 701 wlan_objmgr_vdev_release_ref(cm_ctx->vdev, WLAN_MLME_CM_ID); 702 703 return QDF_STATUS_E_FAILURE; 704 } 705 706 return QDF_STATUS_SUCCESS; 707 } 708 709 #ifdef WLAN_POLICY_MGR_ENABLE 710 QDF_STATUS 711 cm_handle_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id, 712 enum wlan_cm_sm_evt event) 713 { 714 struct cm_req *cm_req; 715 enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE; 716 struct wlan_objmgr_pdev *pdev; 717 QDF_STATUS status; 718 719 if (!cm_id) 720 return QDF_STATUS_E_FAILURE; 721 722 cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id); 723 if (!cm_req) 724 return QDF_STATUS_E_INVAL; 725 726 pdev = wlan_vdev_get_pdev(cm_ctx->vdev); 727 if (!pdev) { 728 mlme_err(CM_PREFIX_FMT "Failed to find pdev", 729 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), 730 cm_req->cm_id)); 731 goto send_failure; 732 } 733 734 if (event == WLAN_CM_SM_EV_HW_MODE_SUCCESS) { 735 status = cm_ser_reassoc_req(cm_ctx, &cm_req->roam_req); 736 if (QDF_IS_STATUS_ERROR(status)) { 737 reason = CM_SER_FAILURE; 738 goto send_failure; 739 } 740 return status; 741 } 742 743 /* Set reason HW mode fail for event WLAN_CM_SM_EV_HW_MODE_FAILURE */ 744 reason = CM_HW_MODE_FAILURE; 745 746 send_failure: 747 return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true); 748 } 749 750 void cm_reassoc_hw_mode_change_resp(struct wlan_objmgr_pdev *pdev, 751 uint8_t vdev_id, wlan_cm_id cm_id, 752 QDF_STATUS status) 753 { 754 struct wlan_objmgr_vdev *vdev; 755 QDF_STATUS qdf_status; 756 enum wlan_cm_sm_evt event = WLAN_CM_SM_EV_HW_MODE_SUCCESS; 757 struct cnx_mgr *cm_ctx; 758 759 mlme_debug(CM_PREFIX_FMT "Continue Reassoc after HW mode change, status %d", 760 CM_PREFIX_REF(vdev_id, cm_id), status); 761 762 vdev = wlan_objmgr_get_vdev_by_id_from_pdev(pdev, vdev_id, 763 WLAN_MLME_CM_ID); 764 if (!vdev) 765 return; 766 767 cm_ctx = cm_get_cm_ctx(vdev); 768 if (!cm_ctx) { 769 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 770 return; 771 } 772 773 if (QDF_IS_STATUS_ERROR(status)) 774 event = WLAN_CM_SM_EV_HW_MODE_FAILURE; 775 qdf_status = cm_sm_deliver_event(vdev, event, sizeof(wlan_cm_id), 776 &cm_id); 777 778 /* 779 * Handle failure if posting fails, i.e. the SM state has 780 * changed or head cm_id doesn't match the active cm_id. 781 * hw mode change resp should be handled in REASSOC state. If 782 * new command has been received reassoc should be 783 * aborted from here with reassoc req cleanup. 784 */ 785 if (QDF_IS_STATUS_ERROR(status)) 786 cm_reassoc_handle_event_post_fail(cm_ctx, cm_id); 787 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 788 } 789 790 static QDF_STATUS 791 cm_check_for_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx, 792 struct cm_roam_req *cm_req) 793 { 794 qdf_freq_t candidate_freq; 795 struct wlan_objmgr_psoc *psoc; 796 QDF_STATUS status; 797 798 psoc = wlan_vdev_get_psoc(cm_ctx->vdev); 799 if (!psoc) 800 return QDF_STATUS_E_INVAL; 801 802 if (!cm_req->cur_candidate) 803 return QDF_STATUS_E_EMPTY; 804 805 /* HW mode change not required for self reassoc */ 806 if (cm_req->req.self_reassoc) 807 return QDF_STATUS_E_ALREADY; 808 809 candidate_freq = cm_req->cur_candidate->entry->channel.chan_freq; 810 status = policy_mgr_handle_conc_multiport( 811 psoc, cm_req->req.vdev_id, 812 candidate_freq, POLICY_MGR_UPDATE_REASON_LFR2_ROAM, 813 cm_req->cm_id); 814 if (status == QDF_STATUS_E_NOSUPPORT) 815 status = QDF_STATUS_E_ALREADY; 816 817 return status; 818 } 819 #else 820 static inline QDF_STATUS 821 cm_check_for_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx, 822 struct cm_roam_req *cm_req) 823 { 824 return QDF_STATUS_E_ALREADY; 825 } 826 #endif 827 828 QDF_STATUS cm_reassoc_start(struct cnx_mgr *cm_ctx, 829 struct cm_roam_req *cm_req) 830 { 831 QDF_STATUS status = QDF_STATUS_E_FAILURE; 832 uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 833 enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE; 834 835 status = cm_check_for_reassoc_hw_mode_change(cm_ctx, cm_req); 836 if (QDF_IS_STATUS_ERROR(status) && status != QDF_STATUS_E_ALREADY) { 837 reason = CM_HW_MODE_FAILURE; 838 mlme_err(CM_PREFIX_FMT "Failed to set HW mode change status %d", 839 CM_PREFIX_REF(vdev_id, cm_req->cm_id), status); 840 goto err; 841 } else if (QDF_IS_STATUS_SUCCESS(status)) { 842 mlme_debug(CM_PREFIX_FMT "Reassoc will continue after HW mode change", 843 CM_PREFIX_REF(vdev_id, cm_req->cm_id)); 844 return QDF_STATUS_SUCCESS; 845 } 846 847 status = cm_ser_reassoc_req(cm_ctx, cm_req); 848 if (QDF_IS_STATUS_SUCCESS(status)) 849 return status; 850 851 reason = CM_SER_FAILURE; 852 mlme_err(CM_PREFIX_FMT "Serialization of reassoc failed", 853 CM_PREFIX_REF(vdev_id, cm_req->cm_id)); 854 err: 855 return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true); 856 } 857 858 QDF_STATUS cm_reassoc_rsp(struct wlan_objmgr_vdev *vdev, 859 struct wlan_cm_connect_resp *resp) 860 { 861 struct cnx_mgr *cm_ctx; 862 QDF_STATUS qdf_status; 863 wlan_cm_id cm_id; 864 uint32_t prefix; 865 enum wlan_cm_sm_evt event; 866 867 cm_ctx = cm_get_cm_ctx(vdev); 868 if (!cm_ctx) 869 return QDF_STATUS_E_INVAL; 870 871 cm_id = cm_ctx->active_cm_id; 872 prefix = CM_ID_GET_PREFIX(cm_id); 873 874 if (prefix != ROAM_REQ_PREFIX || 875 cm_id != resp->cm_id) { 876 mlme_err(CM_PREFIX_FMT " Active cm_id 0x%x is different", 877 CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->cm_id), 878 cm_id); 879 qdf_status = QDF_STATUS_E_FAILURE; 880 goto post_err; 881 } 882 883 if (QDF_IS_STATUS_SUCCESS(resp->connect_status)) { 884 /* 885 * On successful connection to sae single pmk AP, 886 * clear all the single pmk AP. 887 */ 888 if (cm_is_cm_id_current_candidate_single_pmk(cm_ctx, cm_id)) 889 wlan_crypto_selective_clear_sae_single_pmk_entries( 890 vdev, &resp->bssid); 891 event = WLAN_CM_SM_EV_REASSOC_DONE; 892 } else { 893 event = WLAN_CM_SM_EV_REASSOC_FAILURE; 894 } 895 896 qdf_status = cm_sm_deliver_event(cm_ctx->vdev, event, sizeof(*resp), 897 resp); 898 if (QDF_IS_STATUS_SUCCESS(qdf_status)) 899 return qdf_status; 900 post_err: 901 cm_reassoc_complete(cm_ctx, resp); 902 903 return qdf_status; 904 } 905 906 QDF_STATUS cm_roam_bss_peer_create_rsp(struct wlan_objmgr_vdev *vdev, 907 QDF_STATUS status, 908 struct qdf_mac_addr *peer_mac) 909 { 910 struct cnx_mgr *cm_ctx; 911 QDF_STATUS qdf_status; 912 wlan_cm_id cm_id; 913 uint32_t prefix; 914 struct cm_req *cm_req; 915 916 cm_ctx = cm_get_cm_ctx(vdev); 917 if (!cm_ctx) 918 return QDF_STATUS_E_INVAL; 919 920 cm_id = cm_ctx->active_cm_id; 921 prefix = CM_ID_GET_PREFIX(cm_id); 922 923 if (prefix != ROAM_REQ_PREFIX) { 924 mlme_err(CM_PREFIX_FMT "Active req is not roam req", 925 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), cm_id)); 926 mlme_cm_bss_peer_delete_req(vdev); 927 return QDF_STATUS_E_INVAL; 928 } 929 930 if (QDF_IS_STATUS_ERROR(status)) { 931 cm_req = cm_get_req_by_cm_id(cm_ctx, cm_id); 932 if (!cm_req) 933 return QDF_STATUS_E_INVAL; 934 935 return cm_send_reassoc_start_fail( 936 cm_ctx, cm_id, 937 CM_PEER_CREATE_FAILED, false); 938 } 939 940 qdf_status = cm_sm_deliver_event( 941 vdev, WLAN_CM_SM_EV_BSS_CREATE_PEER_SUCCESS, 942 sizeof(wlan_cm_id), &cm_id); 943 if (QDF_IS_STATUS_SUCCESS(qdf_status)) 944 return qdf_status; 945 946 mlme_cm_bss_peer_delete_req(vdev); 947 cm_reassoc_handle_event_post_fail(cm_ctx, cm_id); 948 949 return qdf_status; 950 } 951 952 QDF_STATUS cm_roam_disconnect_rsp(struct wlan_objmgr_vdev *vdev, 953 struct wlan_cm_discon_rsp *resp) 954 { 955 struct cnx_mgr *cm_ctx; 956 QDF_STATUS qdf_status; 957 wlan_cm_id cm_id; 958 uint32_t prefix; 959 960 cm_ctx = cm_get_cm_ctx(vdev); 961 if (!cm_ctx) 962 return QDF_STATUS_E_INVAL; 963 964 cm_id = cm_ctx->active_cm_id; 965 prefix = CM_ID_GET_PREFIX(cm_id); 966 967 if (prefix != ROAM_REQ_PREFIX || cm_id != resp->req.cm_id) { 968 mlme_err(CM_PREFIX_FMT "Active cm_id 0x%x is different", 969 CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->req.cm_id), 970 cm_id); 971 qdf_status = QDF_STATUS_E_FAILURE; 972 goto disconnect_complete; 973 } 974 qdf_status = 975 cm_sm_deliver_event(vdev, 976 WLAN_CM_SM_EV_HO_ROAM_DISCONNECT_DONE, 977 sizeof(*resp), resp); 978 if (QDF_IS_STATUS_SUCCESS(qdf_status)) 979 return qdf_status; 980 981 disconnect_complete: 982 cm_reassoc_handle_event_post_fail(cm_ctx, cm_id); 983 return qdf_status; 984 } 985