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