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