xref: /wlan-dirver/qca-wifi-host-cmn/umac/mlme/connection_mgr/core/src/wlan_cm_disconnect.c (revision 97f44cd39e4ff816eaa1710279d28cf6b9e65ad9)
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