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