xref: /wlan-dirver/qcacld-3.0/core/hdd/src/wlan_hdd_cm_disconnect.c (revision e22f9adc774eb030715d9a453ec9ea346916ed3b)
1 /*
2  * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-2023 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 /**
19  * DOC: hdd_cm_disconnect.c
20  *
21  * WLAN Host Device Driver disconnect APIs implementation
22  *
23  */
24 
25 #include "wlan_hdd_main.h"
26 #include "wlan_hdd_object_manager.h"
27 #include "wlan_hdd_trace.h"
28 #include <osif_cm_req.h>
29 #include "wlan_hdd_cm_api.h"
30 #include "wlan_ipa_ucfg_api.h"
31 #include "wlan_hdd_stats.h"
32 #include "wlan_hdd_scan.h"
33 #include "sme_power_save_api.h"
34 #include <wlan_logging_sock_svc.h>
35 #include "wlan_hdd_ftm_time_sync.h"
36 #include "wlan_hdd_bcn_recv.h"
37 #include "wlan_hdd_assoc.h"
38 #include "wlan_hdd_ipa.h"
39 #include "wlan_hdd_green_ap.h"
40 #include "wlan_hdd_lpass.h"
41 #include "wlan_hdd_bootup_marker.h"
42 #include "wlan_p2p_ucfg_api.h"
43 #include "wlan_crypto_global_api.h"
44 #include "wlan_mlme_vdev_mgr_interface.h"
45 #include "hif.h"
46 #include "wlan_hdd_power.h"
47 #include "wlan_hdd_napi.h"
48 #include "wlan_hdd_cfr.h"
49 #include "wlan_roam_debug.h"
50 #include "wma_api.h"
51 #include "wlan_hdd_hostapd.h"
52 #include "wlan_dp_ucfg_api.h"
53 #include "wma.h"
54 
55 void hdd_handle_disassociation_event(struct wlan_hdd_link_info *link_info,
56 				     struct qdf_mac_addr *peer_macaddr)
57 {
58 	struct hdd_adapter *adapter = link_info->adapter;
59 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
60 	struct hdd_station_ctx *sta_ctx;
61 	ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC);
62 	struct wlan_objmgr_vdev *vdev;
63 
64 	sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info);
65 	hdd_green_ap_start_state_mc(hdd_ctx, adapter->device_mode, false);
66 
67 	wlan_hdd_auto_shutdown_enable(hdd_ctx, true);
68 
69 	if ((adapter->device_mode == QDF_STA_MODE) ||
70 	    (adapter->device_mode == QDF_P2P_CLIENT_MODE))
71 		/* send peer status indication to oem app */
72 		hdd_send_peer_status_ind_to_app(peer_macaddr,
73 						ePeerDisconnected, 0,
74 						link_info->vdev_id, NULL,
75 						adapter->device_mode);
76 
77 	hdd_lpass_notify_disconnect(link_info);
78 
79 	vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_DP_ID);
80 	if (vdev) {
81 		ucfg_dp_del_latency_critical_client(vdev,
82 			hdd_convert_cfgdot11mode_to_80211mode(
83 				sta_ctx->conn_info.dot11mode));
84 		/* stop timer in sta/p2p_cli */
85 		ucfg_dp_bus_bw_compute_reset_prev_txrx_stats(vdev);
86 		hdd_objmgr_put_vdev_by_user(vdev, WLAN_DP_ID);
87 	}
88 
89 	ucfg_dp_bus_bw_compute_timer_try_stop(hdd_ctx->psoc);
90 	cdp_display_txrx_hw_info(soc);
91 }
92 
93 /**
94  * hdd_cm_print_bss_info() - print bss info
95  * @hdd_sta_ctx: pointer to hdd station context
96  *
97  * Return: None
98  */
99 static void hdd_cm_print_bss_info(struct hdd_station_ctx *hdd_sta_ctx)
100 {
101 	uint32_t *ht_cap_info;
102 	uint32_t *vht_cap_info;
103 	struct hdd_connection_info *conn_info;
104 
105 	conn_info = &hdd_sta_ctx->conn_info;
106 
107 	hdd_nofl_debug("*********** WIFI DATA LOGGER **************");
108 	hdd_nofl_debug("freq: %d dot11mode %d AKM %d ssid: \"" QDF_SSID_FMT "\" ,roam_count %d nss %d legacy %d mcs %d signal %d noise: %d",
109 		       conn_info->chan_freq, conn_info->dot11mode,
110 		       conn_info->last_auth_type,
111 		       QDF_SSID_REF(conn_info->last_ssid.SSID.length,
112 				    conn_info->last_ssid.SSID.ssId),
113 		       conn_info->roam_count,
114 		       conn_info->txrate.nss, conn_info->txrate.legacy,
115 		       conn_info->txrate.mcs, conn_info->signal,
116 		       conn_info->noise);
117 	ht_cap_info = (uint32_t *)&conn_info->ht_caps;
118 	vht_cap_info = (uint32_t *)&conn_info->vht_caps;
119 	hdd_nofl_debug("HT 0x%x VHT 0x%x ht20 info 0x%x",
120 		       conn_info->conn_flag.ht_present ? *ht_cap_info : 0,
121 		       conn_info->conn_flag.vht_present ? *vht_cap_info : 0,
122 		       conn_info->conn_flag.hs20_present ?
123 		       conn_info->hs20vendor_ie.release_num : 0);
124 }
125 
126 void
127 __hdd_cm_disconnect_handler_pre_user_update(struct wlan_hdd_link_info *link_info)
128 {
129 	struct hdd_adapter *adapter = link_info->adapter;
130 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
131 	struct hdd_station_ctx *sta_ctx;
132 	uint32_t time_buffer_size;
133 	struct wlan_objmgr_vdev *vdev;
134 
135 	sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info);
136 	hdd_stop_tsf_sync(adapter);
137 	time_buffer_size = sizeof(sta_ctx->conn_info.connect_time);
138 	qdf_mem_zero(sta_ctx->conn_info.connect_time, time_buffer_size);
139 	if (ucfg_ipa_is_enabled() &&
140 	    QDF_IS_STATUS_SUCCESS(wlan_hdd_validate_mac_address(
141 				  &sta_ctx->conn_info.bssid)))
142 		ucfg_ipa_wlan_evt(hdd_ctx->pdev, adapter->dev,
143 				  adapter->device_mode,
144 				  link_info->vdev_id,
145 				  WLAN_IPA_STA_DISCONNECT,
146 				  sta_ctx->conn_info.bssid.bytes,
147 				  false);
148 
149 	vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_DP_ID);
150 	if (vdev) {
151 		ucfg_dp_periodic_sta_stats_stop(vdev);
152 		hdd_objmgr_put_vdev_by_user(vdev, WLAN_DP_ID);
153 	}
154 
155 	wlan_hdd_auto_shutdown_enable(hdd_ctx, true);
156 
157 	DPTRACE(qdf_dp_trace_mgmt_pkt(QDF_DP_TRACE_MGMT_PACKET_RECORD,
158 		link_info->vdev_id,
159 		QDF_TRACE_DEFAULT_PDEV_ID,
160 		QDF_PROTO_TYPE_MGMT, QDF_PROTO_MGMT_DISASSOC));
161 
162 	hdd_wmm_dscp_initial_state(adapter);
163 	wlan_deregister_txrx_packetdump(OL_TXRX_PDEV_ID);
164 
165 	hdd_place_marker(adapter, "DISCONNECTED", NULL);
166 }
167 
168 void
169 __hdd_cm_disconnect_handler_post_user_update(struct wlan_hdd_link_info *link_info,
170 					     struct wlan_objmgr_vdev *vdev,
171 					     enum wlan_cm_source source)
172 {
173 	struct hdd_adapter *adapter = link_info->adapter;
174 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
175 	struct hdd_station_ctx *sta_ctx;
176 	mac_handle_t mac_handle;
177 	struct hdd_adapter *link_adapter;
178 	struct hdd_station_ctx *link_sta_ctx;
179 	bool is_link_switch =
180 			wlan_vdev_mlme_is_mlo_link_switch_in_progress(vdev);
181 
182 	mac_handle = hdd_ctx->mac_handle;
183 	sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info);
184 
185 	/* update P2P connection status */
186 	ucfg_p2p_status_disconnect(vdev);
187 	hdd_cfr_disconnect(vdev);
188 
189 	hdd_wmm_adapter_clear(adapter);
190 	ucfg_cm_ft_reset(vdev);
191 	ucfg_cm_reset_key(hdd_ctx->pdev, link_info->vdev_id);
192 	hdd_clear_roam_profile_ie(adapter);
193 
194 	if (adapter->device_mode == QDF_STA_MODE)
195 		wlan_crypto_reset_vdev_params(vdev);
196 
197 	hdd_remove_beacon_filter(adapter);
198 	if (sme_is_beacon_report_started(mac_handle, link_info->vdev_id)) {
199 		hdd_debug("Sending beacon pause indication to userspace");
200 		hdd_beacon_recv_pause_indication((hdd_handle_t)hdd_ctx,
201 						 link_info->vdev_id,
202 						 SCAN_EVENT_TYPE_MAX, true);
203 	}
204 
205 	if (adapter->device_mode == QDF_STA_MODE &&
206 	    hdd_adapter_is_ml_adapter(adapter)) {
207 		/* Clear connection info in assoc link adapter as well */
208 		link_adapter = hdd_get_assoc_link_adapter(adapter);
209 		if (link_adapter) {
210 			link_sta_ctx =
211 				WLAN_HDD_GET_STATION_CTX_PTR(link_adapter->deflink);
212 			hdd_conn_remove_connect_info(link_sta_ctx);
213 		}
214 	}
215 
216 	if (!is_link_switch && source != CM_MLO_ROAM_INTERNAL_DISCONNECT) {
217 		/* Clear saved connection information in HDD */
218 		hdd_conn_remove_connect_info(sta_ctx);
219 
220 		/*
221 		 * Reset the IEEE link ID to invalid when disconnect is not
222 		 * due to link switch. This API resets link id for all the
223 		 * valid link_info for the given adapter. So avoid this reset
224 		 * for Link Switch disconnect/internal disconnect
225 		 */
226 		hdd_adapter_reset_station_ctx(adapter);
227 	}
228 
229 	ucfg_dp_remove_conn_info(vdev);
230 
231 	/* Setting the RTS profile to original value */
232 	if (sme_cli_set_command(link_info->vdev_id,
233 				wmi_vdev_param_enable_rtscts,
234 				cfg_get(hdd_ctx->psoc,
235 					CFG_ENABLE_FW_RTS_PROFILE),
236 				VDEV_CMD))
237 		hdd_debug("Failed to set RTS_PROFILE");
238 
239 	hdd_init_scan_reject_params(hdd_ctx);
240 	ucfg_pmo_flush_gtk_offload_req(vdev);
241 
242 	if ((QDF_STA_MODE == adapter->device_mode) ||
243 	    (QDF_P2P_CLIENT_MODE == adapter->device_mode)) {
244 		sme_ps_disable_auto_ps_timer(mac_handle, link_info->vdev_id);
245 		adapter->send_mode_change = true;
246 	}
247 	wlan_hdd_clear_link_layer_stats(adapter);
248 
249 	ucfg_dp_reset_cont_txtimeout_cnt(vdev);
250 
251 	ucfg_dp_nud_reset_tracking(vdev);
252 	hdd_reset_limit_off_chan(adapter);
253 
254 	hdd_cm_print_bss_info(sta_ctx);
255 }
256 
257 #ifdef WLAN_FEATURE_MSCS
258 void reset_mscs_params(struct wlan_hdd_link_info *link_info)
259 {
260 	mlme_set_is_mscs_req_sent(link_info->vdev, false);
261 	link_info->mscs_counter = 0;
262 }
263 #endif
264 
265 QDF_STATUS
266 wlan_hdd_cm_issue_disconnect(struct wlan_hdd_link_info *link_info,
267 			     enum wlan_reason_code reason, bool sync)
268 {
269 	QDF_STATUS status;
270 	struct wlan_objmgr_vdev *vdev;
271 	struct hdd_station_ctx *sta_ctx;
272 	struct hdd_adapter *adapter = link_info->adapter;
273 
274 	vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_OSIF_CM_ID);
275 	if (!vdev)
276 		return QDF_STATUS_E_INVAL;
277 
278 	sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info);
279 	hdd_place_marker(adapter, "TRY TO DISCONNECT", NULL);
280 	reset_mscs_params(link_info);
281 	hdd_conn_set_authenticated(link_info, false);
282 	wlan_hdd_netif_queue_control(adapter,
283 				     WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER,
284 				     WLAN_CONTROL_PATH);
285 
286 	qdf_rtpm_sync_resume();
287 
288 	wlan_rec_conn_info(link_info->vdev_id, DEBUG_CONN_DISCONNECT,
289 			   sta_ctx->conn_info.bssid.bytes, 0, reason);
290 
291 	if (sync)
292 		status = osif_cm_disconnect_sync(vdev, reason);
293 	else
294 		status = osif_cm_disconnect(adapter->dev, vdev, reason);
295 
296 	hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_CM_ID);
297 
298 	return status;
299 }
300 
301 int wlan_hdd_cm_disconnect(struct wiphy *wiphy,
302 			   struct net_device *dev, u16 reason)
303 {
304 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
305 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
306 	QDF_STATUS status;
307 	int ret;
308 	struct wlan_hdd_link_info *link_info = adapter->deflink;
309 
310 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
311 		hdd_err("Command not allowed in FTM mode");
312 		return -EINVAL;
313 	}
314 
315 	ret = wlan_hdd_validate_context(hdd_ctx);
316 	if (ret)
317 		return ret;
318 
319 	if (wlan_hdd_validate_vdev_id(link_info->vdev_id))
320 		return -EINVAL;
321 
322 	if (hdd_ctx->is_wiphy_suspended) {
323 		hdd_info_rl("wiphy is suspended retry disconnect");
324 		return -EAGAIN;
325 	}
326 
327 	qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
328 		   TRACE_CODE_HDD_CFG80211_DISCONNECT,
329 		   link_info->vdev_id, reason);
330 
331 	hdd_print_netdev_txq_status(dev);
332 
333 	if (reason == WLAN_REASON_DEAUTH_LEAVING)
334 		qdf_dp_trace_dump_all(
335 				WLAN_DEAUTH_DPTRACE_DUMP_COUNT,
336 				QDF_TRACE_DEFAULT_PDEV_ID);
337 	/*
338 	 * for Supplicant initiated disconnect always wait for complete,
339 	 * as for WPS connection or back to back connect, supplicant initiate a
340 	 * disconnect which is followed by connect and if kernel is not yet
341 	 * disconnected, this new connect will be rejected by kernel with status
342 	 * EALREADY. In case connect is rejected with EALREADY, supplicant will
343 	 * queue one more disconnect followed by connect immediately, Now if
344 	 * driver is not disconnected by this time, the kernel will again reject
345 	 * connect and thus the failing the connect req in supplicant.
346 	 * Thus we need to wait for disconnect to complete in this case,
347 	 * and thus use sync API here.
348 	 */
349 	status = wlan_hdd_cm_issue_disconnect(link_info, reason, true);
350 
351 	return qdf_status_to_os_return(status);
352 }
353 
354 static QDF_STATUS
355 hdd_cm_disconnect_complete_pre_user_update(struct wlan_objmgr_vdev *vdev,
356 					   struct wlan_cm_discon_rsp *rsp)
357 {
358 	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
359 	struct hdd_adapter *adapter;
360 	struct wlan_hdd_link_info *link_info;
361 
362 	if (!hdd_ctx) {
363 		hdd_err("hdd_ctx is NULL");
364 		return QDF_STATUS_E_INVAL;
365 	}
366 
367 	link_info = hdd_get_link_info_by_vdev(hdd_ctx, wlan_vdev_get_id(vdev));
368 	if (!link_info) {
369 		hdd_err("adapter is NULL for vdev %d", wlan_vdev_get_id(vdev));
370 		return QDF_STATUS_E_INVAL;
371 	}
372 
373 	adapter = link_info->adapter;
374 	hdd_conn_set_authenticated(link_info, false);
375 	hdd_napi_serialize(0);
376 	hdd_disable_and_flush_mc_addr_list(adapter, pmo_peer_disconnect);
377 	__hdd_cm_disconnect_handler_pre_user_update(link_info);
378 
379 	hdd_handle_disassociation_event(link_info, &rsp->req.req.bssid);
380 
381 	wlan_rec_conn_info(link_info->vdev_id,
382 			   DEBUG_CONN_DISCONNECT_HANDLER,
383 			   rsp->req.req.bssid.bytes,
384 			   rsp->req.cm_id,
385 			   rsp->req.req.reason_code << 16 |
386 			   rsp->req.req.source);
387 	wlan_hdd_set_tx_flow_info();
388 	/*
389 	 * Convert and cache internal reason code in adapter. This can be
390 	 * sent to userspace with a vendor event.
391 	 */
392 	adapter->last_disconnect_reason =
393 			osif_cm_mac_to_qca_reason(rsp->req.req.reason_code);
394 
395 	return QDF_STATUS_SUCCESS;
396 }
397 
398 /**
399  * hdd_cm_set_default_wlm_mode - reset the default wlm mode if
400  *				 wlm_latency_reset_on_disconnect is set.
401  *@adapter: adapter pointer
402  *
403  * return: None.
404  */
405 static void hdd_cm_set_default_wlm_mode(struct hdd_adapter *adapter)
406 {
407 	QDF_STATUS status;
408 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
409 	bool reset;
410 	uint8_t def_level;
411 	uint32_t client_id_bitmap;
412 
413 	if (!hdd_ctx) {
414 		hdd_err("hdd_ctx is NULL");
415 		return;
416 	}
417 
418 	status = ucfg_mlme_cfg_get_wlm_reset(hdd_ctx->psoc, &reset);
419 	if (QDF_IS_STATUS_ERROR(status)) {
420 		hdd_err("could not get wlm reset flag");
421 		return;
422 	}
423 	if (!reset)
424 		return;
425 
426 	status = ucfg_mlme_cfg_get_wlm_level(hdd_ctx->psoc, &def_level);
427 	if (QDF_IS_STATUS_ERROR(status))
428 		def_level = QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL;
429 
430 	if (hdd_get_multi_client_ll_support(adapter)) {
431 		client_id_bitmap = wlan_hdd_get_client_id_bitmap(adapter);
432 		hdd_debug("client_id_bitmap: 0x%x", client_id_bitmap);
433 		status = wlan_hdd_set_wlm_latency_level(adapter, def_level,
434 							client_id_bitmap, true);
435 		wlan_hdd_deinit_multi_client_info_table(adapter);
436 	} else {
437 		status =
438 			sme_set_wlm_latency_level(hdd_ctx->mac_handle,
439 						  adapter->deflink->vdev_id,
440 						  def_level, 0, false);
441 		if (QDF_IS_STATUS_SUCCESS(status)) {
442 			hdd_debug("reset wlm mode %x on disconnection",
443 				  def_level);
444 			adapter->latency_level = def_level;
445 		} else {
446 			hdd_err("reset wlm mode failed: %d", status);
447 		}
448 	}
449 }
450 
451 /**
452  * hdd_cm_reset_udp_qos_upgrade_config() - Reset the threshold for UDP packet
453  * QoS upgrade.
454  * @adapter: adapter for which this configuration is to be applied
455  *
456  * Return: None
457  */
458 static void hdd_cm_reset_udp_qos_upgrade_config(struct hdd_adapter *adapter)
459 {
460 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
461 	bool reset;
462 	QDF_STATUS status;
463 
464 	if (!hdd_ctx) {
465 		hdd_err("hdd_ctx is NULL");
466 		return;
467 	}
468 
469 	status = ucfg_mlme_cfg_get_wlm_reset(hdd_ctx->psoc, &reset);
470 	if (QDF_IS_STATUS_ERROR(status)) {
471 		hdd_err("could not get the wlm reset flag");
472 		return;
473 	}
474 
475 	if (reset) {
476 		adapter->upgrade_udp_qos_threshold = QCA_WLAN_AC_BK;
477 		hdd_debug("UDP packets qos upgrade to: %d",
478 			  adapter->upgrade_udp_qos_threshold);
479 	}
480 }
481 
482 #ifdef WLAN_FEATURE_11BE
483 static inline enum eSirMacHTChannelWidth get_max_bw(void)
484 {
485 	uint32_t max_bw = wma_get_eht_ch_width();
486 
487 	if (max_bw == WNI_CFG_EHT_CHANNEL_WIDTH_320MHZ)
488 		return eHT_CHANNEL_WIDTH_320MHZ;
489 	else if (max_bw == WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ)
490 		return eHT_CHANNEL_WIDTH_160MHZ;
491 	else if (max_bw == WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ)
492 		return eHT_CHANNEL_WIDTH_80P80MHZ;
493 	else if (max_bw == WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ)
494 		return eHT_CHANNEL_WIDTH_80MHZ;
495 	else
496 		return eHT_CHANNEL_WIDTH_40MHZ;
497 }
498 #else
499 static inline enum eSirMacHTChannelWidth get_max_bw(void)
500 {
501 	uint32_t max_bw = wma_get_vht_ch_width();
502 
503 	if (max_bw == WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ)
504 		return eHT_CHANNEL_WIDTH_160MHZ;
505 	else if (max_bw == WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ)
506 		return eHT_CHANNEL_WIDTH_80P80MHZ;
507 	else if (max_bw == WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ)
508 		return eHT_CHANNEL_WIDTH_80MHZ;
509 	else
510 		return eHT_CHANNEL_WIDTH_40MHZ;
511 }
512 #endif
513 
514 static void hdd_cm_restore_ch_width(struct wlan_objmgr_vdev *vdev,
515 				    struct hdd_adapter *adapter)
516 {
517 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
518 	struct mlme_legacy_priv *mlme_priv;
519 	enum eSirMacHTChannelWidth max_bw;
520 	struct wlan_channel *des_chan;
521 	uint8_t link_id = 0xFF;
522 	int ret;
523 	uint8_t vdev_id = wlan_vdev_get_id(vdev);
524 	enum phy_ch_width assoc_ch_width;
525 
526 	mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev);
527 	if (!mlme_priv)
528 		return;
529 
530 	des_chan = wlan_vdev_mlme_get_des_chan(vdev);
531 	if (!des_chan)
532 		return;
533 
534 	assoc_ch_width = mlme_priv->connect_info.assoc_chan_info.assoc_ch_width;
535 	if (!ucfg_mlme_is_chwidth_with_notify_supported(hdd_ctx->psoc) ||
536 	    assoc_ch_width == CH_WIDTH_INVALID)
537 		return;
538 
539 	cm_update_associated_ch_info(vdev, false);
540 
541 	max_bw = get_max_bw();
542 	ret = hdd_set_mac_chan_width(adapter, max_bw, link_id, true);
543 	if (ret) {
544 		hdd_err("vdev %d : fail to set max ch width", vdev_id);
545 		return;
546 	}
547 
548 	hdd_debug("vdev %d : updated ch width to: %d on disconnection", vdev_id,
549 		  max_bw);
550 }
551 
552 static QDF_STATUS
553 hdd_cm_disconnect_complete_post_user_update(struct wlan_objmgr_vdev *vdev,
554 					    struct wlan_cm_discon_rsp *rsp)
555 {
556 	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
557 	struct hdd_adapter *adapter;
558 	struct wlan_hdd_link_info *link_info;
559 
560 	if (!hdd_ctx) {
561 		hdd_err("hdd_ctx is NULL");
562 		return QDF_STATUS_E_INVAL;
563 	}
564 
565 	link_info = hdd_get_link_info_by_vdev(hdd_ctx, wlan_vdev_get_id(vdev));
566 	if (!link_info) {
567 		hdd_err("adapter is NULL for vdev %d", wlan_vdev_get_id(vdev));
568 		return QDF_STATUS_E_INVAL;
569 	}
570 
571 	adapter = link_info->adapter;
572 	if (adapter->device_mode == QDF_STA_MODE) {
573 	/* Inform FTM TIME SYNC about the disconnection with the AP */
574 		hdd_ftm_time_sync_sta_state_notify(
575 				adapter, FTM_TIME_SYNC_STA_DISCONNECTED);
576 	}
577 
578 	/*
579 	 * via the SET_MAX_BANDWIDTH command, the upper layer can update channel
580 	 * width. The host should update channel bandwidth to the max supported
581 	 * bandwidth on disconnection so that post disconnection DUT can
582 	 * connect in max BW.
583 	 */
584 	hdd_cm_restore_ch_width(vdev, adapter);
585 	hdd_cm_set_default_wlm_mode(adapter);
586 	__hdd_cm_disconnect_handler_post_user_update(link_info, vdev,
587 						     rsp->req.req.source);
588 	wlan_twt_concurrency_update(hdd_ctx);
589 	hdd_cm_reset_udp_qos_upgrade_config(adapter);
590 	ucfg_mlme_set_ml_link_control_mode(hdd_ctx->psoc,
591 					   vdev->vdev_objmgr.vdev_id, 0);
592 
593 	return QDF_STATUS_SUCCESS;
594 }
595 
596 #ifdef FEATURE_RUNTIME_PM
597 static void
598 wlan_hdd_runtime_pm_wow_disconnect_handler(struct hdd_context *hdd_ctx)
599 {
600 	struct hif_opaque_softc *hif_ctx;
601 
602 	if (!hdd_ctx) {
603 		hdd_err("hdd_ctx is NULL");
604 		return;
605 	}
606 
607 	hif_ctx = cds_get_context(QDF_MODULE_ID_HIF);
608 	if (!hif_ctx) {
609 		hdd_err("hif_ctx is NULL");
610 		return;
611 	}
612 
613 	if (hdd_is_any_sta_connected(hdd_ctx)) {
614 		hdd_debug("active connections: runtime pm prevented: %d",
615 			  hdd_ctx->runtime_pm_prevented);
616 		return;
617 	}
618 
619 	hdd_debug("Runtime allowed : %d", hdd_ctx->runtime_pm_prevented);
620 	qdf_spin_lock_irqsave(&hdd_ctx->pm_qos_lock);
621 	if (hdd_ctx->runtime_pm_prevented) {
622 		qdf_rtpm_put(QDF_RTPM_PUT, QDF_RTPM_ID_PM_QOS_NOTIFY);
623 		hdd_ctx->runtime_pm_prevented = false;
624 	}
625 	qdf_spin_unlock_irqrestore(&hdd_ctx->pm_qos_lock);
626 }
627 #else
628 static void
629 wlan_hdd_runtime_pm_wow_disconnect_handler(struct hdd_context *hdd_ctx)
630 {
631 }
632 #endif
633 
634 QDF_STATUS hdd_cm_disconnect_complete(struct wlan_objmgr_vdev *vdev,
635 				      struct wlan_cm_discon_rsp *rsp,
636 				      enum osif_cb_type type)
637 {
638 	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
639 
640 	switch (type) {
641 	case OSIF_PRE_USERSPACE_UPDATE:
642 		return hdd_cm_disconnect_complete_pre_user_update(vdev, rsp);
643 	case OSIF_POST_USERSPACE_UPDATE:
644 		hdd_debug("Wifi disconnected: vdev id %d",
645 			  vdev->vdev_objmgr.vdev_id);
646 		wlan_hdd_runtime_pm_wow_disconnect_handler(hdd_ctx);
647 
648 		return hdd_cm_disconnect_complete_post_user_update(vdev, rsp);
649 	default:
650 		hdd_cm_disconnect_complete_pre_user_update(vdev, rsp);
651 		hdd_cm_disconnect_complete_post_user_update(vdev, rsp);
652 		return QDF_STATUS_SUCCESS;
653 	}
654 }
655 
656 QDF_STATUS hdd_cm_netif_queue_control(struct wlan_objmgr_vdev *vdev,
657 				      enum netif_action_type action,
658 				      enum netif_reason_type reason)
659 {
660 	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
661 	struct wlan_hdd_link_info *link_info;
662 
663 	if (!hdd_ctx) {
664 		hdd_err("hdd_ctx is NULL");
665 		return QDF_STATUS_E_INVAL;
666 	}
667 
668 	link_info = hdd_get_link_info_by_vdev(hdd_ctx, wlan_vdev_get_id(vdev));
669 	if (!link_info) {
670 		hdd_err("adapter is NULL for vdev %d", wlan_vdev_get_id(vdev));
671 		return QDF_STATUS_E_INVAL;
672 	}
673 
674 	wlan_hdd_netif_queue_control(link_info->adapter, action, reason);
675 
676 	return QDF_STATUS_SUCCESS;
677 }
678 
679 QDF_STATUS hdd_cm_napi_serialize_control(bool action)
680 {
681 	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
682 
683 	if (!hdd_ctx) {
684 		hdd_err("hdd_ctx is NULL");
685 		return QDF_STATUS_E_INVAL;
686 	}
687 
688 	hdd_napi_serialize(action);
689 
690 	/* reinit scan reject params for napi off (roam abort/ho fail) */
691 	if (!action)
692 		hdd_init_scan_reject_params(hdd_ctx);
693 
694 	return QDF_STATUS_SUCCESS;
695 }
696 
697 #ifdef WLAN_BOOST_CPU_FREQ_IN_ROAM
698 QDF_STATUS hdd_cm_perfd_set_cpufreq(bool action)
699 {
700 	struct wlan_core_minfreq req;
701 	struct hdd_context *hdd_ctx;
702 
703 	hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
704 	if (unlikely(!hdd_ctx)) {
705 		hdd_err("cannot get hdd_context");
706 		return QDF_STATUS_E_INVAL;
707 	}
708 
709 	if (action) {
710 		req.magic    = WLAN_CORE_MINFREQ_MAGIC;
711 		req.reserved = 0; /* unused */
712 		req.coremask = 0x00ff;/* big and little cluster */
713 		req.freq     = 0xfff;/* set to max freq */
714 	} else {
715 		req.magic    = WLAN_CORE_MINFREQ_MAGIC;
716 		req.reserved = 0; /* unused */
717 		req.coremask = 0; /* not valid */
718 		req.freq     = 0; /* reset */
719 	}
720 
721 	hdd_debug("CPU min freq to 0x%x coremask 0x%x", req.freq, req.coremask);
722 	/* the following service function returns void */
723 	wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index,
724 				    WLAN_SVC_CORE_MINFREQ,
725 				    &req, sizeof(struct wlan_core_minfreq));
726 	return QDF_STATUS_SUCCESS;
727 }
728 #endif
729