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 disconnect specific apis of connection manager 19 */ 20 #include "wlan_cm_main_api.h" 21 #include "wlan_cm_sm.h" 22 #include "wlan_cm_roam.h" 23 #include <wlan_serialization_api.h> 24 #include "wlan_utility.h" 25 #include "wlan_scan_api.h" 26 #include "wlan_crypto_global_api.h" 27 #ifdef CONN_MGR_ADV_FEATURE 28 #include "wlan_blm_api.h" 29 #endif 30 #include <wlan_mlo_mgr_sta.h> 31 32 void cm_send_disconnect_resp(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id) 33 { 34 struct wlan_cm_discon_rsp resp; 35 QDF_STATUS status; 36 37 qdf_mem_zero(&resp, sizeof(resp)); 38 status = cm_fill_disconnect_resp_from_cm_id(cm_ctx, cm_id, &resp); 39 if (QDF_IS_STATUS_SUCCESS(status)) 40 cm_disconnect_complete(cm_ctx, &resp); 41 } 42 43 #ifdef WLAN_CM_USE_SPINLOCK 44 static QDF_STATUS cm_activate_disconnect_req_sched_cb(struct scheduler_msg *msg) 45 { 46 struct wlan_serialization_command *cmd = msg->bodyptr; 47 struct wlan_objmgr_vdev *vdev; 48 struct cnx_mgr *cm_ctx; 49 QDF_STATUS ret = QDF_STATUS_E_FAILURE; 50 51 if (!cmd) { 52 mlme_err("cmd is null"); 53 return QDF_STATUS_E_INVAL; 54 } 55 56 vdev = cmd->vdev; 57 if (!vdev) { 58 mlme_err("vdev is null"); 59 return QDF_STATUS_E_INVAL; 60 } 61 62 cm_ctx = cm_get_cm_ctx(vdev); 63 if (!cm_ctx) 64 return QDF_STATUS_E_INVAL; 65 66 ret = cm_sm_deliver_event( 67 cm_ctx->vdev, 68 WLAN_CM_SM_EV_DISCONNECT_ACTIVE, 69 sizeof(wlan_cm_id), 70 &cmd->cmd_id); 71 72 /* 73 * Called from scheduler context hence 74 * handle failure if posting fails 75 */ 76 if (QDF_IS_STATUS_ERROR(ret)) { 77 mlme_err(CM_PREFIX_FMT "Activation failed for cmd:%d", 78 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id), 79 cmd->cmd_type); 80 cm_send_disconnect_resp(cm_ctx, cmd->cmd_id); 81 } 82 83 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 84 return ret; 85 } 86 87 static QDF_STATUS 88 cm_activate_disconnect_req(struct wlan_serialization_command *cmd) 89 { 90 struct wlan_objmgr_vdev *vdev = cmd->vdev; 91 struct scheduler_msg msg = {0}; 92 QDF_STATUS ret; 93 94 msg.bodyptr = cmd; 95 msg.callback = cm_activate_disconnect_req_sched_cb; 96 msg.flush_callback = cm_activate_cmd_req_flush_cb; 97 98 ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_MLME_CM_ID); 99 if (QDF_IS_STATUS_ERROR(ret)) 100 return ret; 101 102 ret = scheduler_post_message(QDF_MODULE_ID_MLME, 103 QDF_MODULE_ID_MLME, 104 QDF_MODULE_ID_MLME, &msg); 105 106 if (QDF_IS_STATUS_ERROR(ret)) { 107 mlme_err(CM_PREFIX_FMT "Failed to post scheduler_msg", 108 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id)); 109 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 110 return ret; 111 } 112 mlme_debug(CM_PREFIX_FMT "Cmd act in sched cmd type:%d", 113 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id), 114 cmd->cmd_type); 115 116 return ret; 117 } 118 #else 119 static QDF_STATUS 120 cm_activate_disconnect_req(struct wlan_serialization_command *cmd) 121 { 122 return cm_sm_deliver_event( 123 cmd->vdev, 124 WLAN_CM_SM_EV_DISCONNECT_ACTIVE, 125 sizeof(wlan_cm_id), 126 &cmd->cmd_id); 127 } 128 #endif 129 130 static QDF_STATUS 131 cm_sm_deliver_disconnect_event(struct cnx_mgr *cm_ctx, 132 struct wlan_serialization_command *cmd) 133 { 134 /* 135 * For pending to active, use async cmnd to take lock. 136 * Use sync command for direct activation as lock is already 137 * acquired. 138 */ 139 if (cmd->activation_reason == SER_PENDING_TO_ACTIVE) 140 return cm_activate_disconnect_req(cmd); 141 else 142 return cm_sm_deliver_event_sync( 143 cm_ctx, 144 WLAN_CM_SM_EV_DISCONNECT_ACTIVE, 145 sizeof(wlan_cm_id), 146 &cmd->cmd_id); 147 } 148 149 static QDF_STATUS 150 cm_ser_disconnect_cb(struct wlan_serialization_command *cmd, 151 enum wlan_serialization_cb_reason reason) 152 { 153 QDF_STATUS status = QDF_STATUS_SUCCESS; 154 struct wlan_objmgr_vdev *vdev; 155 struct cnx_mgr *cm_ctx; 156 157 if (!cmd) { 158 mlme_err("cmd is NULL, reason: %d", reason); 159 QDF_ASSERT(0); 160 return QDF_STATUS_E_NULL_VALUE; 161 } 162 163 vdev = cmd->vdev; 164 165 cm_ctx = cm_get_cm_ctx(vdev); 166 if (!cm_ctx) 167 return QDF_STATUS_E_NULL_VALUE; 168 169 switch (reason) { 170 case WLAN_SER_CB_ACTIVATE_CMD: 171 status = cm_sm_deliver_disconnect_event(cm_ctx, cmd); 172 if (QDF_IS_STATUS_SUCCESS(status)) 173 break; 174 /* 175 * Handle failure if posting fails, i.e. the SM state has 176 * changes. Disconnect should be handled in JOIN_PENDING, 177 * JOIN-SCAN state as well apart from DISCONNECTING. 178 * Also no need to check for head list as diconnect needs to be 179 * completed always once active. 180 */ 181 182 cm_send_disconnect_resp(cm_ctx, cmd->cmd_id); 183 break; 184 case WLAN_SER_CB_CANCEL_CMD: 185 /* command removed from pending list. */ 186 break; 187 case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: 188 mlme_err(CM_PREFIX_FMT "Active command timeout", 189 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id)); 190 cm_trigger_panic_on_cmd_timeout(cm_ctx->vdev); 191 cm_send_disconnect_resp(cm_ctx, cmd->cmd_id); 192 break; 193 case WLAN_SER_CB_RELEASE_MEM_CMD: 194 cm_reset_active_cm_id(vdev, cmd->cmd_id); 195 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 196 break; 197 default: 198 QDF_ASSERT(0); 199 status = QDF_STATUS_E_INVAL; 200 break; 201 } 202 203 return status; 204 } 205 206 static QDF_STATUS cm_ser_disconnect_req(struct wlan_objmgr_pdev *pdev, 207 struct cnx_mgr *cm_ctx, 208 struct cm_disconnect_req *req) 209 { 210 struct wlan_serialization_command cmd = {0, }; 211 enum wlan_serialization_status ser_cmd_status; 212 QDF_STATUS status; 213 uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 214 215 status = wlan_objmgr_vdev_try_get_ref(cm_ctx->vdev, WLAN_MLME_CM_ID); 216 if (QDF_IS_STATUS_ERROR(status)) { 217 mlme_err(CM_PREFIX_FMT "unable to get reference", 218 CM_PREFIX_REF(vdev_id, req->cm_id)); 219 return status; 220 } 221 222 cmd.cmd_type = WLAN_SER_CMD_VDEV_DISCONNECT; 223 cmd.cmd_id = req->cm_id; 224 cmd.cmd_cb = cm_ser_disconnect_cb; 225 cmd.source = WLAN_UMAC_COMP_MLME; 226 cmd.is_high_priority = false; 227 cmd.cmd_timeout_duration = DISCONNECT_TIMEOUT; 228 cmd.vdev = cm_ctx->vdev; 229 cmd.is_blocking = cm_ser_get_blocking_cmd(); 230 231 ser_cmd_status = wlan_serialization_request(&cmd); 232 switch (ser_cmd_status) { 233 case WLAN_SER_CMD_PENDING: 234 /* command moved to pending list.Do nothing */ 235 break; 236 case WLAN_SER_CMD_ACTIVE: 237 /* command moved to active list. Do nothing */ 238 break; 239 default: 240 mlme_err(CM_PREFIX_FMT "ser cmd status %d", 241 CM_PREFIX_REF(vdev_id, req->cm_id), ser_cmd_status); 242 wlan_objmgr_vdev_release_ref(cm_ctx->vdev, WLAN_MLME_CM_ID); 243 244 return QDF_STATUS_E_FAILURE; 245 } 246 247 return QDF_STATUS_SUCCESS; 248 } 249 250 static void 251 cm_if_mgr_inform_disconnect_complete(struct wlan_objmgr_vdev *vdev) 252 { 253 struct if_mgr_event_data *disconnect_complete; 254 255 disconnect_complete = qdf_mem_malloc(sizeof(*disconnect_complete)); 256 if (!disconnect_complete) 257 return; 258 259 disconnect_complete->status = QDF_STATUS_SUCCESS; 260 261 if_mgr_deliver_event(vdev, WLAN_IF_MGR_EV_DISCONNECT_COMPLETE, 262 disconnect_complete); 263 qdf_mem_free(disconnect_complete); 264 } 265 266 static void 267 cm_if_mgr_inform_disconnect_start(struct wlan_objmgr_vdev *vdev) 268 { 269 struct if_mgr_event_data *disconnect_start; 270 271 disconnect_start = qdf_mem_malloc(sizeof(*disconnect_start)); 272 if (!disconnect_start) 273 return; 274 275 disconnect_start->status = QDF_STATUS_SUCCESS; 276 277 if_mgr_deliver_event(vdev, WLAN_IF_MGR_EV_DISCONNECT_START, 278 disconnect_start); 279 qdf_mem_free(disconnect_start); 280 } 281 282 void cm_initiate_internal_disconnect(struct cnx_mgr *cm_ctx) 283 { 284 struct cm_req *cm_req; 285 struct cm_disconnect_req *disconnect_req; 286 QDF_STATUS status; 287 288 cm_req = qdf_mem_malloc(sizeof(*cm_req)); 289 290 if (!cm_req) 291 return; 292 293 disconnect_req = &cm_req->discon_req; 294 disconnect_req->req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 295 disconnect_req->req.source = CM_INTERNAL_DISCONNECT; 296 297 status = cm_add_disconnect_req_to_list(cm_ctx, disconnect_req); 298 if (QDF_IS_STATUS_ERROR(status)) { 299 mlme_err(CM_PREFIX_FMT "failed to add disconnect req", 300 CM_PREFIX_REF(disconnect_req->req.vdev_id, 301 disconnect_req->cm_id)); 302 qdf_mem_free(cm_req); 303 return; 304 } 305 306 cm_disconnect_start(cm_ctx, disconnect_req); 307 } 308 309 QDF_STATUS cm_disconnect_start(struct cnx_mgr *cm_ctx, 310 struct cm_disconnect_req *req) 311 { 312 struct wlan_objmgr_pdev *pdev; 313 QDF_STATUS status; 314 315 pdev = wlan_vdev_get_pdev(cm_ctx->vdev); 316 if (!pdev) { 317 cm_send_disconnect_resp(cm_ctx, req->cm_id); 318 return QDF_STATUS_E_INVAL; 319 } 320 cm_vdev_scan_cancel(pdev, cm_ctx->vdev); 321 mlme_cm_disconnect_start_ind(cm_ctx->vdev, &req->req); 322 cm_if_mgr_inform_disconnect_start(cm_ctx->vdev); 323 mlme_cm_osif_disconnect_start_ind(cm_ctx->vdev); 324 325 /* Serialize disconnect req, Handle failure status */ 326 status = cm_ser_disconnect_req(pdev, cm_ctx, req); 327 328 if (QDF_IS_STATUS_ERROR(status)) 329 cm_send_disconnect_resp(cm_ctx, req->cm_id); 330 331 return QDF_STATUS_SUCCESS; 332 } 333 334 void 335 cm_update_scan_mlme_on_disconnect(struct wlan_objmgr_vdev *vdev, 336 struct cm_disconnect_req *req) 337 { 338 struct wlan_objmgr_pdev *pdev; 339 struct bss_info bss_info; 340 struct mlme_info mlme; 341 struct wlan_channel *chan; 342 QDF_STATUS status; 343 344 pdev = wlan_vdev_get_pdev(vdev); 345 if (!pdev) { 346 mlme_err(CM_PREFIX_FMT "failed to find pdev", 347 CM_PREFIX_REF(req->req.vdev_id, req->cm_id)); 348 return; 349 } 350 351 chan = wlan_vdev_get_active_channel(vdev); 352 if (!chan) { 353 mlme_err(CM_PREFIX_FMT "failed to get active channel", 354 CM_PREFIX_REF(req->req.vdev_id, req->cm_id)); 355 return; 356 } 357 358 status = wlan_vdev_mlme_get_ssid(vdev, bss_info.ssid.ssid, 359 &bss_info.ssid.length); 360 361 if (QDF_IS_STATUS_ERROR(status)) { 362 mlme_err(CM_PREFIX_FMT "failed to get ssid", 363 CM_PREFIX_REF(req->req.vdev_id, req->cm_id)); 364 return; 365 } 366 367 mlme.assoc_state = SCAN_ENTRY_CON_STATE_NONE; 368 qdf_copy_macaddr(&bss_info.bssid, &req->req.bssid); 369 370 bss_info.freq = chan->ch_freq; 371 372 wlan_scan_update_mlme_by_bssinfo(pdev, &bss_info, &mlme); 373 } 374 375 QDF_STATUS cm_disconnect_active(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id) 376 { 377 struct wlan_cm_vdev_discon_req *req; 378 struct cm_req *cm_req; 379 struct qdf_mac_addr bssid = QDF_MAC_ADDR_ZERO_INIT; 380 QDF_STATUS status; 381 382 cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id); 383 if (!cm_req) 384 return QDF_STATUS_E_INVAL; 385 386 req = qdf_mem_malloc(sizeof(*req)); 387 if (!req) 388 return QDF_STATUS_E_NOMEM; 389 390 cm_ctx->active_cm_id = *cm_id; 391 wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &bssid); 392 /* 393 * for northbound req, bssid is not provided so update it from vdev 394 * in case bssid is not present 395 */ 396 if (qdf_is_macaddr_zero(&cm_req->discon_req.req.bssid) || 397 qdf_is_macaddr_broadcast(&cm_req->discon_req.req.bssid)) 398 qdf_copy_macaddr(&cm_req->discon_req.req.bssid, &bssid); 399 400 qdf_copy_macaddr(&req->req.bssid, &bssid); 401 402 req->cm_id = *cm_id; 403 req->req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 404 req->req.source = cm_req->discon_req.req.source; 405 req->req.reason_code = cm_req->discon_req.req.reason_code; 406 req->req.is_no_disassoc_disconnect = 407 cm_req->discon_req.req.is_no_disassoc_disconnect; 408 409 cm_update_scan_mlme_on_disconnect(cm_ctx->vdev, 410 &cm_req->discon_req); 411 412 mlme_debug(CM_PREFIX_FMT "disconnect " QDF_MAC_ADDR_FMT " source %d reason %d", 413 CM_PREFIX_REF(req->req.vdev_id, req->cm_id), 414 QDF_MAC_ADDR_REF(req->req.bssid.bytes), 415 req->req.source, req->req.reason_code); 416 status = mlme_cm_disconnect_req(cm_ctx->vdev, req); 417 if (QDF_IS_STATUS_ERROR(status)) { 418 mlme_err(CM_PREFIX_FMT "disconnect req fail", 419 CM_PREFIX_REF(req->req.vdev_id, req->cm_id)); 420 cm_send_disconnect_resp(cm_ctx, req->cm_id); 421 } 422 qdf_mem_free(req); 423 424 return status; 425 } 426 427 #ifdef CONN_MGR_ADV_FEATURE 428 static void 429 cm_inform_blm_disconnect_complete(struct wlan_objmgr_vdev *vdev, 430 struct wlan_cm_discon_rsp *resp) 431 { 432 struct wlan_objmgr_pdev *pdev; 433 434 pdev = wlan_vdev_get_pdev(vdev); 435 if (!pdev) { 436 mlme_err(CM_PREFIX_FMT "failed to find pdev", 437 CM_PREFIX_REF(wlan_vdev_get_id(vdev), 438 resp->req.cm_id)); 439 return; 440 } 441 442 wlan_blm_update_bssid_connect_params(pdev, resp->req.req.bssid, 443 BLM_AP_DISCONNECTED); 444 } 445 #else 446 static inline void 447 cm_inform_blm_disconnect_complete(struct wlan_objmgr_vdev *vdev, 448 struct wlan_cm_discon_rsp *resp) 449 {} 450 #endif 451 452 QDF_STATUS cm_notify_disconnect_complete(struct cnx_mgr *cm_ctx, 453 struct wlan_cm_discon_rsp *resp) 454 { 455 mlme_cm_disconnect_complete_ind(cm_ctx->vdev, resp); 456 mlo_sta_link_disconn_notify(cm_ctx->vdev, resp); 457 mlme_cm_osif_disconnect_complete(cm_ctx->vdev, resp); 458 cm_if_mgr_inform_disconnect_complete(cm_ctx->vdev); 459 cm_inform_blm_disconnect_complete(cm_ctx->vdev, resp); 460 461 return QDF_STATUS_SUCCESS; 462 } 463 464 QDF_STATUS cm_disconnect_complete(struct cnx_mgr *cm_ctx, 465 struct wlan_cm_discon_rsp *resp) 466 { 467 /* 468 * If the entry is not present in the list, it must have been cleared 469 * already. 470 */ 471 if (!cm_get_req_by_cm_id(cm_ctx, resp->req.cm_id)) 472 return QDF_STATUS_SUCCESS; 473 474 cm_notify_disconnect_complete(cm_ctx, resp); 475 476 /* 477 * Remove all pending disconnect if this is an active disconnect 478 * complete. 479 */ 480 if (resp->req.cm_id == cm_ctx->active_cm_id) 481 cm_flush_pending_request(cm_ctx, DISCONNECT_REQ_PREFIX, false); 482 483 cm_remove_cmd(cm_ctx, &resp->req.cm_id); 484 mlme_debug(CM_PREFIX_FMT "disconnect count %d connect count %d", 485 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), 486 resp->req.cm_id), 487 cm_ctx->disconnect_count, cm_ctx->connect_count); 488 /* Flush failed connect req as pending disconnect is completed */ 489 if (!cm_ctx->disconnect_count && cm_ctx->connect_count) 490 cm_flush_pending_request(cm_ctx, CONNECT_REQ_PREFIX, true); 491 492 /* Set the disconnect wait event once all disconnect are completed */ 493 if (!cm_ctx->disconnect_count) 494 qdf_event_set(&cm_ctx->disconnect_complete); 495 496 return QDF_STATUS_SUCCESS; 497 } 498 499 QDF_STATUS 500 cm_handle_discon_req_in_non_connected_state(struct cnx_mgr *cm_ctx, 501 struct cm_disconnect_req *cm_req, 502 enum wlan_cm_sm_state cm_state_substate) 503 { 504 enum wlan_cm_sm_state cur_state = cm_get_state(cm_ctx); 505 506 /* 507 * South bound and peer disconnect requests are meant for only in 508 * connected state, so if the state is connecting a new connect has 509 * been received, hence skip the non-osif disconnect request. 510 */ 511 if (cur_state == WLAN_CM_S_CONNECTING && 512 (cm_req->req.source != CM_OSIF_DISCONNECT && 513 cm_req->req.source != CM_OSIF_CFG_DISCONNECT)) { 514 mlme_info("Vdev %d ignore disconnect req from source %d in state %d", 515 wlan_vdev_get_id(cm_ctx->vdev), cm_req->req.source, 516 cm_state_substate); 517 return QDF_STATUS_E_INVAL; 518 } 519 520 switch (cm_state_substate) { 521 case WLAN_CM_S_DISCONNECTING: 522 /* 523 * There would be pending disconnect requests in the list, and 524 * if they are flushed as part of new disconnect 525 * (cm_flush_pending_request), OS_IF would inform the kernel 526 * about the disconnect done even though the disconnect is still 527 * pending. So update OS_IF with invalid CM_ID so that the resp 528 * of only the new disconnect req is given to kernel. 529 */ 530 mlme_cm_osif_update_id_and_src(cm_ctx->vdev, 531 CM_SOURCE_INVALID, 532 CM_ID_INVALID); 533 cm_flush_pending_request(cm_ctx, DISCONNECT_REQ_PREFIX, false); 534 /* 535 * Flush failed pending connect req as new req is received 536 * and its no longer the latest one. 537 */ 538 if (cm_ctx->connect_count) 539 cm_flush_pending_request(cm_ctx, CONNECT_REQ_PREFIX, 540 true); 541 break; 542 case WLAN_CM_S_ROAMING: 543 /* for FW roam/LFR3 remove the req from the list */ 544 if (cm_roam_offload_enabled(wlan_vdev_get_psoc(cm_ctx->vdev))) 545 cm_flush_pending_request(cm_ctx, ROAM_REQ_PREFIX, 546 false); 547 /* fallthrough */ 548 case WLAN_CM_SS_JOIN_ACTIVE: 549 /* 550 * In join active/roaming state, there would be no pending 551 * command, so no action required. so for new disconnect 552 * request, queue disconnect and move the state to 553 * disconnecting. 554 */ 555 break; 556 case WLAN_CM_SS_SCAN: 557 /* In the scan state abort the ongoing scan */ 558 cm_vdev_scan_cancel(wlan_vdev_get_pdev(cm_ctx->vdev), 559 cm_ctx->vdev); 560 /* fallthrough */ 561 case WLAN_CM_SS_JOIN_PENDING: 562 /* 563 * There would be pending disconnect requests in the list, and 564 * if they are flushed as part of new disconnect 565 * (cm_flush_pending_request), OS_IF would inform the kernel 566 * about the disconnect done even though the disconnect is still 567 * pending. So update OS_IF with invalid CM_ID so that the resp 568 * of only the new disconnect req is given to kernel. 569 */ 570 mlme_cm_osif_update_id_and_src(cm_ctx->vdev, 571 CM_SOURCE_INVALID, 572 CM_ID_INVALID); 573 /* 574 * In case of scan or join pending there could be a connect and 575 * disconnect requests pending, so flush all the requests except 576 * the activated request. 577 */ 578 cm_flush_pending_request(cm_ctx, CONNECT_REQ_PREFIX, false); 579 cm_flush_pending_request(cm_ctx, DISCONNECT_REQ_PREFIX, false); 580 break; 581 default: 582 mlme_err("Vdev %d disconnect req in invalid state %d", 583 wlan_vdev_get_id(cm_ctx->vdev), 584 cm_state_substate); 585 return QDF_STATUS_E_FAILURE; 586 }; 587 588 /* Queue the new disconnect request after state specific actions */ 589 return cm_add_disconnect_req_to_list(cm_ctx, cm_req); 590 } 591 592 QDF_STATUS cm_add_disconnect_req_to_list(struct cnx_mgr *cm_ctx, 593 struct cm_disconnect_req *req) 594 { 595 QDF_STATUS status; 596 struct cm_req *cm_req; 597 598 cm_req = qdf_container_of(req, struct cm_req, discon_req); 599 req->cm_id = cm_get_cm_id(cm_ctx, req->req.source); 600 cm_req->cm_id = req->cm_id; 601 status = cm_add_req_to_list_and_indicate_osif(cm_ctx, cm_req, 602 req->req.source); 603 604 return status; 605 } 606 607 QDF_STATUS cm_disconnect_start_req(struct wlan_objmgr_vdev *vdev, 608 struct wlan_cm_disconnect_req *req) 609 { 610 struct cnx_mgr *cm_ctx; 611 struct cm_req *cm_req; 612 struct cm_disconnect_req *disconnect_req; 613 QDF_STATUS status; 614 615 cm_ctx = cm_get_cm_ctx(vdev); 616 if (!cm_ctx) 617 return QDF_STATUS_E_INVAL; 618 619 /* 620 * This would be freed as part of removal from cm req list if adding 621 * to list is success after posting WLAN_CM_SM_EV_DISCONNECT_REQ. 622 */ 623 cm_req = qdf_mem_malloc(sizeof(*cm_req)); 624 625 if (!cm_req) 626 return QDF_STATUS_E_NOMEM; 627 628 if (wlan_vdev_mlme_is_mlo_vdev(vdev) && 629 !wlan_vdev_mlme_is_mlo_link_vdev(vdev)) 630 req->is_no_disassoc_disconnect = 1; 631 632 disconnect_req = &cm_req->discon_req; 633 disconnect_req->req = *req; 634 635 status = cm_sm_deliver_event(vdev, WLAN_CM_SM_EV_DISCONNECT_REQ, 636 sizeof(*disconnect_req), disconnect_req); 637 /* free the req if disconnect is not handled */ 638 if (QDF_IS_STATUS_ERROR(status)) 639 qdf_mem_free(cm_req); 640 641 return status; 642 } 643 644 QDF_STATUS cm_disconnect_start_req_sync(struct wlan_objmgr_vdev *vdev, 645 struct wlan_cm_disconnect_req *req) 646 { 647 struct cnx_mgr *cm_ctx; 648 QDF_STATUS status; 649 650 cm_ctx = cm_get_cm_ctx(vdev); 651 if (!cm_ctx) 652 return QDF_STATUS_E_INVAL; 653 654 if (wlan_vdev_mlme_is_mlo_vdev(vdev) && 655 !wlan_vdev_mlme_is_mlo_link_vdev(vdev)) 656 req->is_no_disassoc_disconnect = 1; 657 658 qdf_event_reset(&cm_ctx->disconnect_complete); 659 status = cm_disconnect_start_req(vdev, req); 660 if (QDF_IS_STATUS_ERROR(status)) { 661 mlme_err("Disconnect failed with status %d", status); 662 return status; 663 } 664 665 status = qdf_wait_single_event(&cm_ctx->disconnect_complete, 666 CM_DISCONNECT_CMD_TIMEOUT); 667 if (QDF_IS_STATUS_ERROR(status)) 668 mlme_err("Disconnect timeout with status %d", status); 669 670 return status; 671 } 672 673 QDF_STATUS cm_disconnect_rsp(struct wlan_objmgr_vdev *vdev, 674 struct wlan_cm_discon_rsp *resp) 675 { 676 struct cnx_mgr *cm_ctx; 677 QDF_STATUS qdf_status; 678 wlan_cm_id cm_id; 679 uint32_t prefix; 680 681 cm_ctx = cm_get_cm_ctx(vdev); 682 if (!cm_ctx) 683 return QDF_STATUS_E_INVAL; 684 685 cm_id = cm_ctx->active_cm_id; 686 prefix = CM_ID_GET_PREFIX(cm_id); 687 688 if (prefix != DISCONNECT_REQ_PREFIX || cm_id != resp->req.cm_id) { 689 mlme_err(CM_PREFIX_FMT "Active cm_id 0x%x is different", 690 CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->req.cm_id), 691 cm_id); 692 qdf_status = QDF_STATUS_E_FAILURE; 693 goto disconnect_complete; 694 } 695 qdf_status = 696 cm_sm_deliver_event(vdev, 697 WLAN_CM_SM_EV_DISCONNECT_DONE, 698 sizeof(*resp), resp); 699 if (QDF_IS_STATUS_ERROR(qdf_status)) 700 goto disconnect_complete; 701 702 return qdf_status; 703 704 disconnect_complete: 705 /* 706 * If there is a event posting error it means the SM state is not in 707 * DISCONNECTING (some new cmd has changed the state of SM), so just 708 * complete the disconnect command. 709 */ 710 return cm_disconnect_complete(cm_ctx, resp); 711 } 712 713 QDF_STATUS cm_bss_peer_delete_req(struct wlan_objmgr_vdev *vdev, 714 struct qdf_mac_addr *peer_mac) 715 { 716 mlme_debug("vdev-id %d, delete peer" QDF_MAC_ADDR_FMT, 717 wlan_vdev_get_id(vdev), QDF_MAC_ADDR_REF(peer_mac->bytes)); 718 719 return mlme_cm_bss_peer_delete_req(vdev); 720 } 721 722 QDF_STATUS cm_vdev_down_req(struct wlan_objmgr_vdev *vdev, uint32_t status) 723 { 724 mlme_debug("vdev id %d down req status %d", 725 wlan_vdev_get_id(vdev), status); 726 727 return mlme_cm_vdev_down_req(vdev); 728 } 729