1 /* 2 * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2022-2024 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 cm_fill_roam_fail_resp_from_cm_id(struct cnx_mgr * cm_ctx,struct wlan_cm_connect_resp * resp,wlan_cm_id cm_id,enum wlan_cm_connect_fail_reason reason)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 cm_reassoc_fail_disconnect(struct wlan_objmgr_vdev * vdev,enum wlan_cm_source source,enum wlan_reason_code reason_code,struct qdf_mac_addr * bssid)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 cm_send_reassoc_start_fail(struct cnx_mgr * cm_ctx,wlan_cm_id cm_id,enum wlan_cm_connect_fail_reason reason,bool sync)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 cm_update_roam_scan_filter(struct wlan_objmgr_vdev * vdev,struct cm_roam_req * cm_req,struct scan_filter * filter,bool security_valid_for_6ghz)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 cm_update_roam_scan_filter(struct wlan_objmgr_vdev * vdev,struct cm_roam_req * cm_req,struct scan_filter * filter,bool security_valid_for_6ghz)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 cm_connect_prepare_scan_filter_for_roam(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req,struct scan_filter * filter,bool security_valid_for_6ghz)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 cm_roam_get_candidates(struct wlan_objmgr_pdev * pdev,struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)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 cm_handle_reassoc_timer(struct cnx_mgr * cm_ctx,wlan_cm_id * cm_id)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 cm_host_roam_start(struct cnx_mgr * cm_ctx,struct cm_req * cm_req)262 static QDF_STATUS cm_host_roam_start(struct cnx_mgr *cm_ctx, 263 struct cm_req *cm_req) 264 { 265 cm_req->roam_req.cur_candidate = NULL; 266 267 return cm_host_roam_preauth_start(cm_ctx, cm_req); 268 } 269 270 static cm_host_roam_start_fail(struct cnx_mgr * cm_ctx,struct cm_req * cm_req,enum wlan_cm_connect_fail_reason reason)271 QDF_STATUS cm_host_roam_start_fail(struct cnx_mgr *cm_ctx, 272 struct cm_req *cm_req, 273 enum wlan_cm_connect_fail_reason reason) 274 { 275 cm_send_preauth_start_fail(cm_ctx, cm_req->cm_id, reason); 276 277 return QDF_STATUS_SUCCESS; 278 } 279 #else cm_host_roam_start(struct cnx_mgr * cm_ctx,struct cm_req * cm_req)280 static QDF_STATUS cm_host_roam_start(struct cnx_mgr *cm_ctx, 281 struct cm_req *cm_req) 282 { 283 return cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_START_REASSOC, 284 sizeof(cm_req->roam_req), 285 &cm_req->roam_req); 286 } 287 288 static cm_host_roam_start_fail(struct cnx_mgr * cm_ctx,struct cm_req * cm_req,enum wlan_cm_connect_fail_reason reason)289 QDF_STATUS cm_host_roam_start_fail(struct cnx_mgr *cm_ctx, 290 struct cm_req *cm_req, 291 enum wlan_cm_connect_fail_reason reason) 292 { 293 return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true); 294 } 295 #endif 296 cm_host_roam_start_req(struct cnx_mgr * cm_ctx,struct cm_req * cm_req)297 QDF_STATUS cm_host_roam_start_req(struct cnx_mgr *cm_ctx, 298 struct cm_req *cm_req) 299 { 300 QDF_STATUS status; 301 struct wlan_objmgr_pdev *pdev; 302 enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE; 303 304 mlme_cm_roam_start_ind(cm_ctx->vdev, &cm_req->roam_req.req); 305 306 pdev = wlan_vdev_get_pdev(cm_ctx->vdev); 307 if (!pdev) { 308 reason = CM_GENERIC_FAILURE; 309 goto roam_err; 310 } 311 312 status = cm_roam_get_candidates(pdev, cm_ctx, &cm_req->roam_req); 313 if (QDF_IS_STATUS_ERROR(status)) { 314 reason = CM_NO_CANDIDATE_FOUND; 315 goto roam_err; 316 } 317 318 status = cm_host_roam_start(cm_ctx, cm_req); 319 if (QDF_IS_STATUS_SUCCESS(status)) 320 return status; 321 322 roam_err: 323 return cm_host_roam_start_fail(cm_ctx, cm_req, reason); 324 } 325 cm_roam_resp_cmid_match_list_head(struct cnx_mgr * cm_ctx,struct wlan_cm_connect_resp * resp)326 bool cm_roam_resp_cmid_match_list_head(struct cnx_mgr *cm_ctx, 327 struct wlan_cm_connect_resp *resp) 328 { 329 return cm_check_cmid_match_list_head(cm_ctx, &resp->cm_id); 330 } 331 cm_reassoc_active(struct cnx_mgr * cm_ctx,wlan_cm_id * cm_id)332 QDF_STATUS cm_reassoc_active(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id) 333 { 334 struct cm_req *cm_req; 335 struct wlan_cm_vdev_discon_req req; 336 struct cm_disconnect_req *discon_req; 337 338 cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id); 339 if (!cm_req) 340 return QDF_STATUS_E_INVAL; 341 342 cm_ctx->active_cm_id = *cm_id; 343 344 qdf_mem_zero(&req, sizeof(req)); 345 req.cm_id = *cm_id; 346 req.req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 347 req.req.source = CM_ROAM_DISCONNECT; 348 wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &req.req.bssid); 349 350 discon_req = qdf_mem_malloc(sizeof(*discon_req)); 351 if (!discon_req) 352 return QDF_STATUS_E_NOMEM; 353 354 discon_req->cm_id = *cm_id; 355 discon_req->req.vdev_id = req.req.vdev_id; 356 qdf_copy_macaddr(&discon_req->req.bssid, 357 &req.req.bssid); 358 cm_update_scan_mlme_on_disconnect(cm_ctx->vdev, discon_req); 359 qdf_mem_free(discon_req); 360 361 return mlme_cm_disconnect_req(cm_ctx->vdev, &req); 362 } 363 cm_reassoc_disconnect_complete(struct cnx_mgr * cm_ctx,struct wlan_cm_discon_rsp * resp)364 QDF_STATUS cm_reassoc_disconnect_complete(struct cnx_mgr *cm_ctx, 365 struct wlan_cm_discon_rsp *resp) 366 { 367 QDF_STATUS status; 368 struct cm_req *cm_req; 369 struct qdf_mac_addr *bssid; 370 uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 371 wlan_cm_id cm_id = cm_ctx->active_cm_id; 372 373 cm_req = cm_get_req_by_cm_id(cm_ctx, cm_id); 374 if (!cm_req) 375 return QDF_STATUS_E_INVAL; 376 377 mlme_cm_disconnect_complete_ind(cm_ctx->vdev, resp); 378 bssid = &cm_req->roam_req.cur_candidate->entry->bssid; 379 380 status = mlme_cm_bss_peer_create_req(cm_ctx->vdev, bssid, NULL, false); 381 if (QDF_IS_STATUS_ERROR(status)) { 382 mlme_err(CM_PREFIX_FMT "Peer create request failed", 383 CM_PREFIX_REF(vdev_id, cm_id)); 384 status = cm_send_reassoc_start_fail(cm_ctx, cm_id, 385 CM_PEER_CREATE_FAILED, 386 true); 387 } 388 389 return status; 390 } 391 392 QDF_STATUS cm_resume_reassoc_after_peer_create(struct cnx_mgr * cm_ctx,wlan_cm_id * cm_id)393 cm_resume_reassoc_after_peer_create(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id) 394 { 395 struct wlan_cm_vdev_reassoc_req *req; 396 struct cm_req *cm_req; 397 QDF_STATUS status; 398 399 cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id); 400 if (!cm_req) 401 return QDF_STATUS_E_FAILURE; 402 403 req = qdf_mem_malloc(sizeof(*req)); 404 if (!req) 405 return QDF_STATUS_E_NOMEM; 406 407 req->vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 408 req->cm_id = *cm_id; 409 req->bss = cm_req->roam_req.cur_candidate; 410 411 mlme_nofl_info(CM_PREFIX_FMT "Reassoc to " QDF_SSID_FMT " " QDF_MAC_ADDR_FMT " rssi: %d freq: %d source %d", 412 CM_PREFIX_REF(req->vdev_id, req->cm_id), 413 QDF_SSID_REF(req->bss->entry->ssid.length, 414 req->bss->entry->ssid.ssid), 415 QDF_MAC_ADDR_REF(req->bss->entry->bssid.bytes), 416 req->bss->entry->rssi_raw, 417 req->bss->entry->channel.chan_freq, 418 cm_req->roam_req.req.source); 419 420 status = mlme_cm_reassoc_req(cm_ctx->vdev, req); 421 if (QDF_IS_STATUS_ERROR(status)) { 422 mlme_err(CM_PREFIX_FMT "Reassoc request failed", 423 CM_PREFIX_REF(req->vdev_id, req->cm_id)); 424 mlme_cm_bss_peer_delete_req(cm_ctx->vdev); 425 status = cm_send_reassoc_start_fail(cm_ctx, *cm_id, 426 CM_JOIN_FAILED, true); 427 } 428 429 qdf_mem_free(req); 430 return status; 431 } 432 cm_reassoc_complete(struct cnx_mgr * cm_ctx,struct wlan_cm_connect_resp * resp)433 QDF_STATUS cm_reassoc_complete(struct cnx_mgr *cm_ctx, 434 struct wlan_cm_connect_resp *resp) 435 { 436 /* 437 * If the entry is not present in the list, it must have been cleared 438 * already. 439 */ 440 if (!cm_get_req_by_cm_id(cm_ctx, resp->cm_id)) 441 return QDF_STATUS_SUCCESS; 442 443 resp->is_reassoc = true; 444 cm_connect_complete(cm_ctx, resp); 445 /* 446 * If roaming fails and conn_sm is in ROAMING state, then 447 * initiate disconnect to cleanup and move conn_sm to INIT state 448 */ 449 if (QDF_IS_STATUS_ERROR(resp->connect_status) && 450 cm_get_state(cm_ctx) == WLAN_CM_S_ROAMING) { 451 cm_reassoc_fail_disconnect(cm_ctx->vdev, CM_ROAM_DISCONNECT, 452 REASON_UNSPEC_FAILURE, 453 &resp->bssid); 454 } 455 456 return QDF_STATUS_SUCCESS; 457 } 458 459 static void cm_reassoc_handle_event_post_fail(struct cnx_mgr * cm_ctx,wlan_cm_id cm_id)460 cm_reassoc_handle_event_post_fail(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id) 461 { 462 struct wlan_cm_connect_resp *resp; 463 464 resp = qdf_mem_malloc(sizeof(*resp)); 465 if (!resp) 466 return; 467 468 cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id, 469 CM_GENERIC_FAILURE); 470 cm_reassoc_complete(cm_ctx, resp); 471 qdf_mem_free(resp); 472 } 473 cm_reassoc_cmd_timeout(struct cnx_mgr * cm_ctx,wlan_cm_id cm_id)474 static QDF_STATUS cm_reassoc_cmd_timeout(struct cnx_mgr *cm_ctx, 475 wlan_cm_id cm_id) 476 { 477 struct wlan_cm_connect_resp *resp; 478 QDF_STATUS status; 479 480 resp = qdf_mem_malloc(sizeof(*resp)); 481 if (!resp) 482 return QDF_STATUS_E_NOMEM; 483 484 cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id, CM_SER_TIMEOUT); 485 status = cm_sm_deliver_event(cm_ctx->vdev, 486 WLAN_CM_SM_EV_REASSOC_FAILURE, 487 sizeof(*resp), resp); 488 if (QDF_IS_STATUS_ERROR(status)) 489 cm_reassoc_complete(cm_ctx, resp); 490 491 qdf_mem_free(resp); 492 493 return status; 494 } 495 496 #ifdef WLAN_CM_USE_SPINLOCK cm_activate_reassoc_req_sched_cb(struct scheduler_msg * msg)497 static QDF_STATUS cm_activate_reassoc_req_sched_cb(struct scheduler_msg *msg) 498 { 499 struct wlan_serialization_command *cmd = msg->bodyptr; 500 struct wlan_objmgr_vdev *vdev; 501 struct cnx_mgr *cm_ctx; 502 QDF_STATUS ret = QDF_STATUS_E_FAILURE; 503 504 if (!cmd || !cmd->vdev) { 505 mlme_err("Invalid Input"); 506 return QDF_STATUS_E_INVAL; 507 } 508 509 vdev = cmd->vdev; 510 cm_ctx = cm_get_cm_ctx(vdev); 511 if (!cm_ctx) 512 return QDF_STATUS_E_INVAL; 513 514 ret = cm_sm_deliver_event(vdev, 515 WLAN_CM_SM_EV_REASSOC_ACTIVE, 516 sizeof(wlan_cm_id), 517 &cmd->cmd_id); 518 /* 519 * Called from scheduler context hence posting failure 520 */ 521 if (QDF_IS_STATUS_ERROR(ret)) 522 cm_reassoc_handle_event_post_fail(cm_ctx, cmd->cmd_id); 523 524 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 525 return ret; 526 } 527 528 static QDF_STATUS cm_activate_reassoc_req(struct wlan_serialization_command * cmd)529 cm_activate_reassoc_req(struct wlan_serialization_command *cmd) 530 { 531 struct wlan_objmgr_vdev *vdev = cmd->vdev; 532 struct scheduler_msg msg = {0}; 533 QDF_STATUS ret; 534 535 msg.bodyptr = cmd; 536 msg.callback = cm_activate_reassoc_req_sched_cb; 537 msg.flush_callback = cm_activate_cmd_req_flush_cb; 538 539 ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_MLME_CM_ID); 540 if (QDF_IS_STATUS_ERROR(ret)) 541 return ret; 542 543 ret = scheduler_post_message(QDF_MODULE_ID_MLME, 544 QDF_MODULE_ID_MLME, 545 QDF_MODULE_ID_MLME, &msg); 546 547 if (QDF_IS_STATUS_ERROR(ret)) { 548 mlme_err(CM_PREFIX_FMT "Failed to post scheduler_msg", 549 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id)); 550 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 551 return ret; 552 } 553 554 return ret; 555 } 556 #else 557 static QDF_STATUS cm_activate_reassoc_req(struct wlan_serialization_command * cmd)558 cm_activate_reassoc_req(struct wlan_serialization_command *cmd) 559 { 560 return cm_sm_deliver_event(cmd->vdev, 561 WLAN_CM_SM_EV_REASSOC_ACTIVE, 562 sizeof(wlan_cm_id), 563 &cmd->cmd_id); 564 } 565 #endif 566 567 static QDF_STATUS cm_ser_reassoc_cb(struct wlan_serialization_command * cmd,enum wlan_serialization_cb_reason reason)568 cm_ser_reassoc_cb(struct wlan_serialization_command *cmd, 569 enum wlan_serialization_cb_reason reason) 570 { 571 QDF_STATUS status = QDF_STATUS_SUCCESS; 572 struct wlan_objmgr_vdev *vdev; 573 struct cnx_mgr *cm_ctx; 574 enum qdf_hang_reason hang_reason = 575 QDF_VDEV_ACTIVE_SER_REASSOC_TIMEOUT; 576 577 if (!cmd) { 578 mlme_err("cmd is NULL, reason: %d", reason); 579 QDF_ASSERT(0); 580 return QDF_STATUS_E_NULL_VALUE; 581 } 582 583 vdev = cmd->vdev; 584 cm_ctx = cm_get_cm_ctx(vdev); 585 if (!cm_ctx) 586 return QDF_STATUS_E_NULL_VALUE; 587 588 switch (reason) { 589 case WLAN_SER_CB_ACTIVATE_CMD: 590 /* 591 * For pending to active reason, use async api to take lock. 592 * For direct activation use sync api to avoid taking lock 593 * as lock is already acquired by the requester. 594 */ 595 if (cmd->activation_reason == SER_PENDING_TO_ACTIVE) 596 status = cm_activate_reassoc_req(cmd); 597 else 598 status = cm_sm_deliver_event_sync( 599 cm_ctx, WLAN_CM_SM_EV_REASSOC_ACTIVE, 600 sizeof(wlan_cm_id), &cmd->cmd_id); 601 602 if (QDF_IS_STATUS_SUCCESS(status)) 603 break; 604 /* 605 * Handle failure if posting fails, i.e. the SM state has 606 * changed or head cm_id doesn't match the active cm_id. 607 * connect active should be handled only in JOIN_PENDING. If 608 * new command has been received connect activation should be 609 * aborted from here with connect req cleanup. 610 */ 611 cm_reassoc_handle_event_post_fail(cm_ctx, cmd->cmd_id); 612 break; 613 case WLAN_SER_CB_CANCEL_CMD: 614 /* command removed from pending list. */ 615 break; 616 case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: 617 mlme_err(CM_PREFIX_FMT "Active command timeout", 618 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id)); 619 cm_trigger_panic_on_cmd_timeout(cm_ctx->vdev, hang_reason); 620 cm_reassoc_cmd_timeout(cm_ctx, cmd->cmd_id); 621 break; 622 case WLAN_SER_CB_RELEASE_MEM_CMD: 623 cm_reset_active_cm_id(vdev, cmd->cmd_id); 624 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 625 break; 626 default: 627 QDF_ASSERT(0); 628 status = QDF_STATUS_E_INVAL; 629 break; 630 } 631 632 return status; 633 } 634 635 #define REASSOC_TIMEOUT 10000 cm_ser_reassoc_req(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)636 static QDF_STATUS cm_ser_reassoc_req(struct cnx_mgr *cm_ctx, 637 struct cm_roam_req *cm_req) 638 { 639 struct wlan_serialization_command cmd = {0, }; 640 enum wlan_serialization_status ser_cmd_status; 641 QDF_STATUS status; 642 uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 643 644 mlme_cm_osif_roam_sync_ind(cm_ctx->vdev); 645 646 status = wlan_objmgr_vdev_try_get_ref(cm_ctx->vdev, WLAN_MLME_CM_ID); 647 if (QDF_IS_STATUS_ERROR(status)) { 648 mlme_err(CM_PREFIX_FMT "unable to get reference", 649 CM_PREFIX_REF(vdev_id, cm_req->cm_id)); 650 return status; 651 } 652 653 cmd.cmd_type = WLAN_SER_CMD_VDEV_ROAM; 654 cmd.cmd_id = cm_req->cm_id; 655 cmd.cmd_cb = cm_ser_reassoc_cb; 656 cmd.source = WLAN_UMAC_COMP_MLME; 657 cmd.is_high_priority = false; 658 cmd.cmd_timeout_duration = REASSOC_TIMEOUT; 659 cmd.vdev = cm_ctx->vdev; 660 cmd.is_blocking = cm_ser_get_blocking_cmd(); 661 662 ser_cmd_status = wlan_serialization_request(&cmd); 663 switch (ser_cmd_status) { 664 case WLAN_SER_CMD_PENDING: 665 /* command moved to pending list.Do nothing */ 666 break; 667 case WLAN_SER_CMD_ACTIVE: 668 /* command moved to active list. Do nothing */ 669 break; 670 default: 671 mlme_err(CM_PREFIX_FMT "ser cmd status %d", 672 CM_PREFIX_REF(vdev_id, cm_req->cm_id), ser_cmd_status); 673 wlan_objmgr_vdev_release_ref(cm_ctx->vdev, WLAN_MLME_CM_ID); 674 675 return QDF_STATUS_E_FAILURE; 676 } 677 678 return QDF_STATUS_SUCCESS; 679 } 680 681 #ifdef WLAN_POLICY_MGR_ENABLE 682 QDF_STATUS cm_handle_reassoc_hw_mode_change(struct cnx_mgr * cm_ctx,wlan_cm_id * cm_id,enum wlan_cm_sm_evt event)683 cm_handle_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id, 684 enum wlan_cm_sm_evt event) 685 { 686 struct cm_req *cm_req; 687 enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE; 688 struct wlan_objmgr_pdev *pdev; 689 QDF_STATUS status; 690 691 if (!cm_id) 692 return QDF_STATUS_E_FAILURE; 693 694 cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id); 695 if (!cm_req) 696 return QDF_STATUS_E_INVAL; 697 698 pdev = wlan_vdev_get_pdev(cm_ctx->vdev); 699 if (!pdev) { 700 mlme_err(CM_PREFIX_FMT "Failed to find pdev", 701 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), 702 cm_req->cm_id)); 703 goto send_failure; 704 } 705 706 if (event == WLAN_CM_SM_EV_HW_MODE_SUCCESS) { 707 status = cm_ser_reassoc_req(cm_ctx, &cm_req->roam_req); 708 if (QDF_IS_STATUS_ERROR(status)) { 709 reason = CM_SER_FAILURE; 710 goto send_failure; 711 } 712 return status; 713 } 714 715 /* Set reason HW mode fail for event WLAN_CM_SM_EV_HW_MODE_FAILURE */ 716 reason = CM_HW_MODE_FAILURE; 717 718 send_failure: 719 return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true); 720 } 721 cm_reassoc_hw_mode_change_resp(struct wlan_objmgr_pdev * pdev,uint8_t vdev_id,wlan_cm_id cm_id,QDF_STATUS status)722 void cm_reassoc_hw_mode_change_resp(struct wlan_objmgr_pdev *pdev, 723 uint8_t vdev_id, wlan_cm_id cm_id, 724 QDF_STATUS status) 725 { 726 struct wlan_objmgr_vdev *vdev; 727 QDF_STATUS qdf_status; 728 enum wlan_cm_sm_evt event = WLAN_CM_SM_EV_HW_MODE_SUCCESS; 729 struct cnx_mgr *cm_ctx; 730 731 mlme_debug(CM_PREFIX_FMT "Continue Reassoc after HW mode change, status %d", 732 CM_PREFIX_REF(vdev_id, cm_id), status); 733 734 vdev = wlan_objmgr_get_vdev_by_id_from_pdev(pdev, vdev_id, 735 WLAN_MLME_CM_ID); 736 if (!vdev) 737 return; 738 739 cm_ctx = cm_get_cm_ctx(vdev); 740 if (!cm_ctx) { 741 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 742 return; 743 } 744 745 if (QDF_IS_STATUS_ERROR(status)) 746 event = WLAN_CM_SM_EV_HW_MODE_FAILURE; 747 qdf_status = cm_sm_deliver_event(vdev, event, sizeof(wlan_cm_id), 748 &cm_id); 749 750 /* 751 * Handle failure if posting fails, i.e. the SM state has 752 * changed or head cm_id doesn't match the active cm_id. 753 * hw mode change resp should be handled in REASSOC state. If 754 * new command has been received reassoc should be 755 * aborted from here with reassoc req cleanup. 756 */ 757 if (QDF_IS_STATUS_ERROR(qdf_status)) 758 cm_reassoc_handle_event_post_fail(cm_ctx, cm_id); 759 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 760 } 761 762 static QDF_STATUS cm_check_for_reassoc_hw_mode_change(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)763 cm_check_for_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx, 764 struct cm_roam_req *cm_req) 765 { 766 qdf_freq_t candidate_freq; 767 struct wlan_objmgr_psoc *psoc; 768 QDF_STATUS status; 769 770 psoc = wlan_vdev_get_psoc(cm_ctx->vdev); 771 if (!psoc) 772 return QDF_STATUS_E_INVAL; 773 774 if (!cm_req->cur_candidate) 775 return QDF_STATUS_E_EMPTY; 776 777 candidate_freq = cm_req->cur_candidate->entry->channel.chan_freq; 778 status = policy_mgr_handle_conc_multiport( 779 psoc, cm_req->req.vdev_id, 780 candidate_freq, POLICY_MGR_UPDATE_REASON_LFR2_ROAM, 781 cm_req->cm_id); 782 if (status == QDF_STATUS_E_NOSUPPORT) 783 status = QDF_STATUS_E_ALREADY; 784 785 return status; 786 } 787 #else 788 static inline QDF_STATUS cm_check_for_reassoc_hw_mode_change(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)789 cm_check_for_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx, 790 struct cm_roam_req *cm_req) 791 { 792 return QDF_STATUS_E_ALREADY; 793 } 794 #endif 795 cm_reassoc_start(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)796 QDF_STATUS cm_reassoc_start(struct cnx_mgr *cm_ctx, 797 struct cm_roam_req *cm_req) 798 { 799 QDF_STATUS status = QDF_STATUS_E_FAILURE; 800 uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 801 enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE; 802 803 status = cm_check_for_reassoc_hw_mode_change(cm_ctx, cm_req); 804 if (QDF_IS_STATUS_ERROR(status) && status != QDF_STATUS_E_ALREADY) { 805 reason = CM_HW_MODE_FAILURE; 806 mlme_err(CM_PREFIX_FMT "Failed to set HW mode change status %d", 807 CM_PREFIX_REF(vdev_id, cm_req->cm_id), status); 808 goto err; 809 } else if (QDF_IS_STATUS_SUCCESS(status)) { 810 mlme_debug(CM_PREFIX_FMT "Reassoc will continue after HW mode change", 811 CM_PREFIX_REF(vdev_id, cm_req->cm_id)); 812 return QDF_STATUS_SUCCESS; 813 } 814 815 status = cm_ser_reassoc_req(cm_ctx, cm_req); 816 if (QDF_IS_STATUS_SUCCESS(status)) 817 return status; 818 819 reason = CM_SER_FAILURE; 820 mlme_err(CM_PREFIX_FMT "Serialization of reassoc failed", 821 CM_PREFIX_REF(vdev_id, cm_req->cm_id)); 822 err: 823 return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true); 824 } 825 cm_reassoc_rsp(struct wlan_objmgr_vdev * vdev,struct wlan_cm_connect_resp * resp)826 QDF_STATUS cm_reassoc_rsp(struct wlan_objmgr_vdev *vdev, 827 struct wlan_cm_connect_resp *resp) 828 { 829 struct cnx_mgr *cm_ctx; 830 QDF_STATUS qdf_status; 831 wlan_cm_id cm_id; 832 uint32_t prefix; 833 enum wlan_cm_sm_evt event; 834 struct qdf_mac_addr pmksa_mac = QDF_MAC_ADDR_ZERO_INIT; 835 836 cm_ctx = cm_get_cm_ctx(vdev); 837 if (!cm_ctx) 838 return QDF_STATUS_E_INVAL; 839 840 cm_id = cm_ctx->active_cm_id; 841 prefix = CM_ID_GET_PREFIX(cm_id); 842 843 if (prefix != ROAM_REQ_PREFIX || 844 cm_id != resp->cm_id) { 845 mlme_err(CM_PREFIX_FMT " Active cm_id 0x%x is different", 846 CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->cm_id), 847 cm_id); 848 qdf_status = QDF_STATUS_E_FAILURE; 849 goto post_err; 850 } 851 852 cm_connect_rsp_get_mld_addr_or_bssid(resp, &pmksa_mac); 853 854 if (QDF_IS_STATUS_SUCCESS(resp->connect_status)) { 855 /* 856 * On successful connection to sae single pmk AP, 857 * clear all the single pmk AP. 858 */ 859 if (cm_is_cm_id_current_candidate_single_pmk(cm_ctx, cm_id)) 860 wlan_crypto_selective_clear_sae_single_pmk_entries( 861 vdev, &pmksa_mac); 862 event = WLAN_CM_SM_EV_REASSOC_DONE; 863 } else { 864 event = WLAN_CM_SM_EV_REASSOC_FAILURE; 865 } 866 867 qdf_status = cm_sm_deliver_event(cm_ctx->vdev, event, sizeof(*resp), 868 resp); 869 if (QDF_IS_STATUS_SUCCESS(qdf_status)) 870 return qdf_status; 871 post_err: 872 cm_reassoc_complete(cm_ctx, resp); 873 874 return qdf_status; 875 } 876 cm_roam_bss_peer_create_rsp(struct wlan_objmgr_vdev * vdev,QDF_STATUS status,struct qdf_mac_addr * peer_mac)877 QDF_STATUS cm_roam_bss_peer_create_rsp(struct wlan_objmgr_vdev *vdev, 878 QDF_STATUS status, 879 struct qdf_mac_addr *peer_mac) 880 { 881 struct cnx_mgr *cm_ctx; 882 QDF_STATUS qdf_status; 883 wlan_cm_id cm_id; 884 uint32_t prefix; 885 struct cm_req *cm_req; 886 887 cm_ctx = cm_get_cm_ctx(vdev); 888 if (!cm_ctx) 889 return QDF_STATUS_E_INVAL; 890 891 cm_id = cm_ctx->active_cm_id; 892 prefix = CM_ID_GET_PREFIX(cm_id); 893 894 if (prefix != ROAM_REQ_PREFIX) { 895 mlme_err(CM_PREFIX_FMT "Active req is not roam req", 896 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), cm_id)); 897 mlme_cm_bss_peer_delete_req(vdev); 898 return QDF_STATUS_E_INVAL; 899 } 900 901 if (QDF_IS_STATUS_SUCCESS(status)) { 902 qdf_status = cm_bss_peer_create_resp_mlo_attach(vdev, peer_mac); 903 if (QDF_IS_STATUS_ERROR(qdf_status)) { 904 mlme_cm_bss_peer_delete_req(vdev); 905 goto send_err; 906 } 907 908 qdf_status = 909 cm_sm_deliver_event(vdev, 910 WLAN_CM_SM_EV_BSS_CREATE_PEER_SUCCESS, 911 sizeof(wlan_cm_id), &cm_id); 912 913 if (QDF_IS_STATUS_ERROR(qdf_status)) { 914 mlme_cm_bss_peer_delete_req(vdev); 915 cm_reassoc_handle_event_post_fail(cm_ctx, cm_id); 916 } 917 918 return qdf_status; 919 } 920 921 send_err: 922 cm_req = cm_get_req_by_cm_id(cm_ctx, cm_id); 923 if (!cm_req) 924 return QDF_STATUS_E_INVAL; 925 926 return cm_send_reassoc_start_fail(cm_ctx, cm_id, 927 CM_PEER_CREATE_FAILED, false); 928 } 929 cm_roam_disconnect_rsp(struct wlan_objmgr_vdev * vdev,struct wlan_cm_discon_rsp * resp)930 QDF_STATUS cm_roam_disconnect_rsp(struct wlan_objmgr_vdev *vdev, 931 struct wlan_cm_discon_rsp *resp) 932 { 933 struct cnx_mgr *cm_ctx; 934 QDF_STATUS qdf_status; 935 wlan_cm_id cm_id; 936 uint32_t prefix; 937 938 cm_ctx = cm_get_cm_ctx(vdev); 939 if (!cm_ctx) 940 return QDF_STATUS_E_INVAL; 941 942 cm_id = cm_ctx->active_cm_id; 943 prefix = CM_ID_GET_PREFIX(cm_id); 944 945 if (prefix != ROAM_REQ_PREFIX || cm_id != resp->req.cm_id) { 946 mlme_err(CM_PREFIX_FMT "Active cm_id 0x%x is different", 947 CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->req.cm_id), 948 cm_id); 949 qdf_status = QDF_STATUS_E_FAILURE; 950 goto disconnect_complete; 951 } 952 qdf_status = 953 cm_sm_deliver_event(vdev, 954 WLAN_CM_SM_EV_HO_ROAM_DISCONNECT_DONE, 955 sizeof(*resp), resp); 956 if (QDF_IS_STATUS_SUCCESS(qdf_status)) 957 return qdf_status; 958 959 disconnect_complete: 960 cm_reassoc_handle_event_post_fail(cm_ctx, cm_id); 961 return qdf_status; 962 } 963