1 /*
2  * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-2024 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 
hdd_handle_disassociation_event(struct wlan_hdd_link_info * link_info,struct qdf_mac_addr * peer_macaddr)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  */
hdd_cm_print_bss_info(struct hdd_station_ctx * hdd_sta_ctx)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
__hdd_cm_disconnect_handler_pre_user_update(struct wlan_hdd_link_info * link_info)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 /**
169  * hdd_reset_sta_keep_alive_interval() - Reset STA keep alive interval
170  * @link_info: Link info pointer.
171  * @hdd_ctx: HDD context pointer.
172  *
173  * Return: None.
174  */
175 static void
hdd_reset_sta_keep_alive_interval(struct wlan_hdd_link_info * link_info,struct hdd_context * hdd_ctx)176 hdd_reset_sta_keep_alive_interval(struct wlan_hdd_link_info *link_info,
177 				  struct hdd_context *hdd_ctx)
178 {
179 	enum QDF_OPMODE device_mode = link_info->adapter->device_mode;
180 	uint32_t keep_alive_interval;
181 
182 	if (!link_info->adapter->keep_alive_interval)
183 		return;
184 
185 	if (device_mode != QDF_STA_MODE) {
186 		hdd_debug("Not supported for device mode %s = ",
187 			  device_mode_to_string(device_mode));
188 		return;
189 	}
190 
191 	if (!wlan_vdev_mlme_get_is_mlo_link(hdd_ctx->psoc,
192 					    link_info->vdev_id))
193 		wlan_hdd_save_sta_keep_alive_interval(link_info->adapter, 0);
194 
195 	ucfg_mlme_get_sta_keep_alive_period(hdd_ctx->psoc,
196 					    &keep_alive_interval);
197 	hdd_vdev_send_sta_keep_alive_interval(link_info, hdd_ctx,
198 					      keep_alive_interval);
199 }
200 
201 void
__hdd_cm_disconnect_handler_post_user_update(struct wlan_hdd_link_info * link_info,struct wlan_objmgr_vdev * vdev,enum wlan_cm_source source)202 __hdd_cm_disconnect_handler_post_user_update(struct wlan_hdd_link_info *link_info,
203 					     struct wlan_objmgr_vdev *vdev,
204 					     enum wlan_cm_source source)
205 {
206 	struct hdd_adapter *adapter = link_info->adapter;
207 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
208 	struct hdd_station_ctx *sta_ctx;
209 	mac_handle_t mac_handle;
210 	struct hdd_adapter *link_adapter;
211 	struct hdd_station_ctx *link_sta_ctx;
212 	bool is_link_switch =
213 			wlan_vdev_mlme_is_mlo_link_switch_in_progress(vdev);
214 
215 	mac_handle = hdd_ctx->mac_handle;
216 	sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info);
217 
218 	/* update P2P connection status */
219 	ucfg_p2p_status_disconnect(vdev);
220 	hdd_cfr_disconnect(vdev);
221 
222 	hdd_wmm_adapter_clear(adapter);
223 	ucfg_cm_ft_reset(vdev);
224 	ucfg_cm_reset_key(hdd_ctx->pdev, link_info->vdev_id);
225 	hdd_clear_roam_profile_ie(adapter);
226 
227 	if (adapter->device_mode == QDF_STA_MODE)
228 		wlan_crypto_reset_vdev_params(vdev);
229 
230 	hdd_remove_beacon_filter(adapter);
231 	if (sme_is_beacon_report_started(mac_handle, link_info->vdev_id)) {
232 		hdd_debug("Sending beacon pause indication to userspace");
233 		hdd_beacon_recv_pause_indication((hdd_handle_t)hdd_ctx,
234 						 link_info->vdev_id,
235 						 SCAN_EVENT_TYPE_MAX, true);
236 	}
237 
238 	if (adapter->device_mode == QDF_STA_MODE &&
239 	    hdd_adapter_is_ml_adapter(adapter)) {
240 		/* Clear connection info in assoc link adapter as well */
241 		link_adapter = hdd_get_assoc_link_adapter(adapter);
242 		if (link_adapter) {
243 			link_sta_ctx =
244 				WLAN_HDD_GET_STATION_CTX_PTR(link_adapter->deflink);
245 			hdd_conn_remove_connect_info(link_sta_ctx);
246 		}
247 	}
248 
249 	if (!is_link_switch && source != CM_MLO_ROAM_INTERNAL_DISCONNECT) {
250 		/* Clear saved connection information in HDD */
251 		hdd_conn_remove_connect_info(sta_ctx);
252 
253 		/*
254 		 * Reset the IEEE link ID to invalid when disconnect is not
255 		 * due to link switch. This API resets link id for all the
256 		 * valid link_info for the given adapter. So avoid this reset
257 		 * for Link Switch disconnect/internal disconnect
258 		 */
259 		hdd_adapter_reset_station_ctx(adapter);
260 	}
261 
262 	ucfg_dp_remove_conn_info(vdev);
263 
264 	/* Setting the RTS profile to original value */
265 	if (sme_cli_set_command(link_info->vdev_id,
266 				wmi_vdev_param_enable_rtscts,
267 				cfg_get(hdd_ctx->psoc,
268 					CFG_ENABLE_FW_RTS_PROFILE),
269 				VDEV_CMD))
270 		hdd_debug("Failed to set RTS_PROFILE");
271 
272 	hdd_init_scan_reject_params(hdd_ctx);
273 	ucfg_pmo_flush_gtk_offload_req(vdev);
274 
275 	if ((QDF_STA_MODE == adapter->device_mode) ||
276 	    (QDF_P2P_CLIENT_MODE == adapter->device_mode)) {
277 		sme_ps_disable_auto_ps_timer(mac_handle, link_info->vdev_id);
278 		adapter->send_mode_change = true;
279 	}
280 	wlan_hdd_clear_link_layer_stats(adapter);
281 
282 	ucfg_dp_reset_cont_txtimeout_cnt(vdev);
283 
284 	ucfg_dp_nud_reset_tracking(vdev);
285 	hdd_reset_limit_off_chan(adapter);
286 
287 	if (!is_link_switch)
288 		hdd_reset_sta_keep_alive_interval(link_info, hdd_ctx);
289 
290 	hdd_cm_print_bss_info(sta_ctx);
291 }
292 
293 #ifdef WLAN_FEATURE_MSCS
reset_mscs_params(struct wlan_hdd_link_info * link_info)294 void reset_mscs_params(struct wlan_hdd_link_info *link_info)
295 {
296 	mlme_set_is_mscs_req_sent(link_info->vdev, false);
297 	link_info->mscs_counter = 0;
298 }
299 #endif
300 
301 QDF_STATUS
wlan_hdd_cm_issue_disconnect(struct wlan_hdd_link_info * link_info,enum wlan_reason_code reason,bool sync)302 wlan_hdd_cm_issue_disconnect(struct wlan_hdd_link_info *link_info,
303 			     enum wlan_reason_code reason, bool sync)
304 {
305 	QDF_STATUS status;
306 	struct wlan_objmgr_vdev *vdev;
307 	struct hdd_station_ctx *sta_ctx;
308 	struct hdd_adapter *adapter = link_info->adapter;
309 
310 	vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_OSIF_CM_ID);
311 	if (!vdev)
312 		return QDF_STATUS_E_INVAL;
313 
314 	sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info);
315 	hdd_place_marker(adapter, "TRY TO DISCONNECT", NULL);
316 	reset_mscs_params(link_info);
317 	hdd_conn_set_authenticated(link_info, false);
318 	wlan_hdd_netif_queue_control(adapter,
319 				     WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER,
320 				     WLAN_CONTROL_PATH);
321 
322 	qdf_rtpm_sync_resume();
323 
324 	wlan_rec_conn_info(link_info->vdev_id, DEBUG_CONN_DISCONNECT,
325 			   sta_ctx->conn_info.bssid.bytes, 0, reason);
326 
327 	if (sync)
328 		status = osif_cm_disconnect_sync(vdev, reason);
329 	else
330 		status = osif_cm_disconnect(adapter->dev, vdev, reason);
331 
332 	hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_CM_ID);
333 
334 	return status;
335 }
336 
wlan_hdd_cm_disconnect(struct wiphy * wiphy,struct net_device * dev,u16 reason)337 int wlan_hdd_cm_disconnect(struct wiphy *wiphy,
338 			   struct net_device *dev, u16 reason)
339 {
340 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
341 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
342 	QDF_STATUS status;
343 	int ret;
344 	struct wlan_hdd_link_info *link_info = adapter->deflink;
345 
346 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
347 		hdd_err("Command not allowed in FTM mode");
348 		return -EINVAL;
349 	}
350 
351 	ret = wlan_hdd_validate_context(hdd_ctx);
352 	if (ret)
353 		return ret;
354 
355 	if (wlan_hdd_validate_vdev_id(link_info->vdev_id))
356 		return -EINVAL;
357 
358 	if (hdd_ctx->is_wiphy_suspended) {
359 		hdd_info_rl("wiphy is suspended retry disconnect");
360 		return -EAGAIN;
361 	}
362 
363 	qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
364 		   TRACE_CODE_HDD_CFG80211_DISCONNECT,
365 		   link_info->vdev_id, reason);
366 
367 	hdd_print_netdev_txq_status(dev);
368 
369 	if (reason == WLAN_REASON_DEAUTH_LEAVING)
370 		qdf_dp_trace_dump_all(
371 				WLAN_DEAUTH_DPTRACE_DUMP_COUNT,
372 				QDF_TRACE_DEFAULT_PDEV_ID);
373 	/*
374 	 * for Supplicant initiated disconnect always wait for complete,
375 	 * as for WPS connection or back to back connect, supplicant initiate a
376 	 * disconnect which is followed by connect and if kernel is not yet
377 	 * disconnected, this new connect will be rejected by kernel with status
378 	 * EALREADY. In case connect is rejected with EALREADY, supplicant will
379 	 * queue one more disconnect followed by connect immediately, Now if
380 	 * driver is not disconnected by this time, the kernel will again reject
381 	 * connect and thus the failing the connect req in supplicant.
382 	 * Thus we need to wait for disconnect to complete in this case,
383 	 * and thus use sync API here.
384 	 */
385 	status = wlan_hdd_cm_issue_disconnect(link_info, reason, true);
386 
387 	return qdf_status_to_os_return(status);
388 }
389 
390 static QDF_STATUS
hdd_cm_disconnect_complete_pre_user_update(struct wlan_objmgr_vdev * vdev,struct wlan_cm_discon_rsp * rsp)391 hdd_cm_disconnect_complete_pre_user_update(struct wlan_objmgr_vdev *vdev,
392 					   struct wlan_cm_discon_rsp *rsp)
393 {
394 	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
395 	struct hdd_adapter *adapter;
396 	struct wlan_hdd_link_info *link_info;
397 
398 	if (!hdd_ctx) {
399 		hdd_err("hdd_ctx is NULL");
400 		return QDF_STATUS_E_INVAL;
401 	}
402 
403 	link_info = hdd_get_link_info_by_vdev(hdd_ctx, wlan_vdev_get_id(vdev));
404 	if (!link_info) {
405 		hdd_err("adapter is NULL for vdev %d", wlan_vdev_get_id(vdev));
406 		return QDF_STATUS_E_INVAL;
407 	}
408 
409 	adapter = link_info->adapter;
410 	hdd_conn_set_authenticated(link_info, false);
411 	hdd_napi_serialize(0);
412 	hdd_disable_and_flush_mc_addr_list(adapter, pmo_peer_disconnect);
413 	__hdd_cm_disconnect_handler_pre_user_update(link_info);
414 
415 	hdd_handle_disassociation_event(link_info, &rsp->req.req.bssid);
416 
417 	wlan_rec_conn_info(link_info->vdev_id,
418 			   DEBUG_CONN_DISCONNECT_HANDLER,
419 			   rsp->req.req.bssid.bytes,
420 			   rsp->req.cm_id,
421 			   rsp->req.req.reason_code << 16 |
422 			   rsp->req.req.source);
423 	wlan_hdd_set_tx_flow_info();
424 	/*
425 	 * Convert and cache internal reason code in adapter. This can be
426 	 * sent to userspace with a vendor event.
427 	 */
428 	adapter->last_disconnect_reason =
429 			osif_cm_mac_to_qca_reason(rsp->req.req.reason_code);
430 
431 	return QDF_STATUS_SUCCESS;
432 }
433 
434 /**
435  * hdd_cm_set_default_wlm_mode - reset the default wlm mode if
436  *				 wlm_latency_reset_on_disconnect is set.
437  *@adapter: adapter pointer
438  *
439  * return: None.
440  */
hdd_cm_set_default_wlm_mode(struct hdd_adapter * adapter)441 static void hdd_cm_set_default_wlm_mode(struct hdd_adapter *adapter)
442 {
443 	QDF_STATUS status;
444 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
445 	bool reset;
446 	uint8_t def_level;
447 	uint32_t client_id_bitmap;
448 
449 	if (!hdd_ctx) {
450 		hdd_err("hdd_ctx is NULL");
451 		return;
452 	}
453 
454 	status = ucfg_mlme_cfg_get_wlm_reset(hdd_ctx->psoc, &reset);
455 	if (QDF_IS_STATUS_ERROR(status)) {
456 		hdd_err("could not get wlm reset flag");
457 		return;
458 	}
459 	if (!reset)
460 		return;
461 
462 	status = ucfg_mlme_cfg_get_wlm_level(hdd_ctx->psoc, &def_level);
463 	if (QDF_IS_STATUS_ERROR(status))
464 		def_level = QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL;
465 
466 	if (hdd_get_multi_client_ll_support(adapter)) {
467 		client_id_bitmap = wlan_hdd_get_client_id_bitmap(adapter);
468 		hdd_debug("client_id_bitmap: 0x%x", client_id_bitmap);
469 		status = wlan_hdd_set_wlm_latency_level(adapter, def_level,
470 							client_id_bitmap, true);
471 		wlan_hdd_deinit_multi_client_info_table(adapter);
472 	} else {
473 		status =
474 			sme_set_wlm_latency_level(hdd_ctx->mac_handle,
475 						  adapter->deflink->vdev_id,
476 						  def_level, 0, false);
477 		if (QDF_IS_STATUS_SUCCESS(status)) {
478 			hdd_debug("reset wlm mode %x on disconnection",
479 				  def_level);
480 			adapter->latency_level = def_level;
481 		} else {
482 			hdd_err("reset wlm mode failed: %d", status);
483 		}
484 	}
485 }
486 
487 /**
488  * hdd_cm_reset_udp_qos_upgrade_config() - Reset the threshold for UDP packet
489  * QoS upgrade.
490  * @adapter: adapter for which this configuration is to be applied
491  *
492  * Return: None
493  */
hdd_cm_reset_udp_qos_upgrade_config(struct hdd_adapter * adapter)494 static void hdd_cm_reset_udp_qos_upgrade_config(struct hdd_adapter *adapter)
495 {
496 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
497 	bool reset;
498 	QDF_STATUS status;
499 
500 	if (!hdd_ctx) {
501 		hdd_err("hdd_ctx is NULL");
502 		return;
503 	}
504 
505 	status = ucfg_mlme_cfg_get_wlm_reset(hdd_ctx->psoc, &reset);
506 	if (QDF_IS_STATUS_ERROR(status)) {
507 		hdd_err("could not get the wlm reset flag");
508 		return;
509 	}
510 
511 	if (reset) {
512 		adapter->upgrade_udp_qos_threshold = QCA_WLAN_AC_BK;
513 		hdd_debug("UDP packets qos upgrade to: %d",
514 			  adapter->upgrade_udp_qos_threshold);
515 	}
516 }
517 
518 #ifdef WLAN_FEATURE_11BE
get_max_bw(void)519 static inline enum eSirMacHTChannelWidth get_max_bw(void)
520 {
521 	uint32_t max_bw = wma_get_orig_eht_ch_width();
522 
523 	if (max_bw == WNI_CFG_EHT_CHANNEL_WIDTH_320MHZ)
524 		return eHT_CHANNEL_WIDTH_320MHZ;
525 	else if (max_bw == WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ)
526 		return eHT_CHANNEL_WIDTH_160MHZ;
527 	else if (max_bw == WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ)
528 		return eHT_CHANNEL_WIDTH_80P80MHZ;
529 	else if (max_bw == WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ)
530 		return eHT_CHANNEL_WIDTH_80MHZ;
531 	else
532 		return eHT_CHANNEL_WIDTH_40MHZ;
533 }
534 
535 static
wlan_hdd_re_enable_320mhz_6g_conection(struct hdd_context * hdd_ctx,enum phy_ch_width assoc_ch_width)536 void wlan_hdd_re_enable_320mhz_6g_conection(struct hdd_context *hdd_ctx,
537 					    enum phy_ch_width assoc_ch_width)
538 {
539 	struct wlan_mlme_psoc_ext_obj *mlme_obj;
540 
541 	mlme_obj = mlme_get_psoc_ext_obj(hdd_ctx->psoc);
542 	if (!mlme_obj)
543 		return;
544 	/*
545 	 * Initial connection was in 320 MHz and if via SET_MAX_BANDWIDTH
546 	 * command, current channel BW (des_chan->ch_width) gets modified
547 	 * to less than 320MHz, driver disables 6 GHz connection by disabling
548 	 * support_320mhz_6ghz EHT capability. So, in order to allow
549 	 * re-connection (after disconnection) in 320 MHz, also re-enable
550 	 * support_320mhz_6ghz EHT capability before disconnect complete.
551 	 */
552 	if (assoc_ch_width == CH_WIDTH_320MHZ)
553 		mlme_obj->cfg.eht_caps.dot11_eht_cap.support_320mhz_6ghz = 1;
554 }
555 #else
get_max_bw(void)556 static inline enum eSirMacHTChannelWidth get_max_bw(void)
557 {
558 	uint32_t max_bw = wma_get_vht_ch_width();
559 
560 	if (max_bw == WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ)
561 		return eHT_CHANNEL_WIDTH_160MHZ;
562 	else if (max_bw == WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ)
563 		return eHT_CHANNEL_WIDTH_80P80MHZ;
564 	else if (max_bw == WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ)
565 		return eHT_CHANNEL_WIDTH_80MHZ;
566 	else
567 		return eHT_CHANNEL_WIDTH_40MHZ;
568 }
569 
570 static
wlan_hdd_re_enable_320mhz_6g_conection(struct hdd_context * hdd_ctx,enum phy_ch_width assoc_ch_width)571 void wlan_hdd_re_enable_320mhz_6g_conection(struct hdd_context *hdd_ctx,
572 					    enum phy_ch_width assoc_ch_width)
573 {
574 }
575 #endif
576 
hdd_cm_restore_ch_width(struct wlan_objmgr_vdev * vdev,struct wlan_hdd_link_info * link_info)577 static void hdd_cm_restore_ch_width(struct wlan_objmgr_vdev *vdev,
578 				    struct wlan_hdd_link_info *link_info)
579 {
580 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(link_info->adapter);
581 	struct mlme_legacy_priv *mlme_priv;
582 	enum eSirMacHTChannelWidth max_bw;
583 	struct wlan_channel *des_chan;
584 	uint8_t link_id = 0xFF;
585 	int ret;
586 	uint8_t vdev_id = wlan_vdev_get_id(vdev);
587 	enum phy_ch_width assoc_ch_width;
588 
589 	mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev);
590 	if (!mlme_priv)
591 		return;
592 
593 	des_chan = wlan_vdev_mlme_get_des_chan(vdev);
594 	if (!des_chan)
595 		return;
596 
597 	assoc_ch_width = mlme_priv->connect_info.assoc_chan_info.assoc_ch_width;
598 	if (!ucfg_mlme_is_chwidth_with_notify_supported(hdd_ctx->psoc) ||
599 	    assoc_ch_width == CH_WIDTH_INVALID)
600 		return;
601 
602 	cm_update_associated_ch_info(vdev, false);
603 
604 	if (des_chan->ch_width != assoc_ch_width)
605 		wlan_hdd_re_enable_320mhz_6g_conection(hdd_ctx, assoc_ch_width);
606 
607 	max_bw = get_max_bw();
608 	ret = hdd_set_mac_chan_width(link_info, max_bw, link_id, true);
609 	if (ret) {
610 		hdd_err("vdev %d : fail to set max ch width", vdev_id);
611 		return;
612 	}
613 
614 	hdd_debug("vdev %d : updated ch width to: %d on disconnection", vdev_id,
615 		  max_bw);
616 }
617 
618 static QDF_STATUS
hdd_cm_disconnect_complete_post_user_update(struct wlan_objmgr_vdev * vdev,struct wlan_cm_discon_rsp * rsp)619 hdd_cm_disconnect_complete_post_user_update(struct wlan_objmgr_vdev *vdev,
620 					    struct wlan_cm_discon_rsp *rsp)
621 {
622 	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
623 	struct hdd_adapter *adapter;
624 	struct wlan_hdd_link_info *link_info;
625 
626 	if (!hdd_ctx) {
627 		hdd_err("hdd_ctx is NULL");
628 		return QDF_STATUS_E_INVAL;
629 	}
630 
631 	link_info = hdd_get_link_info_by_vdev(hdd_ctx, wlan_vdev_get_id(vdev));
632 	if (!link_info) {
633 		hdd_err("adapter is NULL for vdev %d", wlan_vdev_get_id(vdev));
634 		return QDF_STATUS_E_INVAL;
635 	}
636 
637 	adapter = link_info->adapter;
638 	if (adapter->device_mode == QDF_STA_MODE) {
639 	/* Inform FTM TIME SYNC about the disconnection with the AP */
640 		hdd_ftm_time_sync_sta_state_notify(
641 				adapter, FTM_TIME_SYNC_STA_DISCONNECTED);
642 	}
643 
644 	/*
645 	 * via the SET_MAX_BANDWIDTH command, the upper layer can update channel
646 	 * width. The host should update channel bandwidth to the max supported
647 	 * bandwidth on disconnection so that post disconnection DUT can
648 	 * connect in max BW.
649 	 */
650 	hdd_cm_restore_ch_width(vdev, link_info);
651 
652 	/*
653 	 * same WLM configuration is applicable for all links, So no need to
654 	 * restore it while processing disconnection due to link switch.
655 	 */
656 	if (rsp->req.req.source != CM_MLO_LINK_SWITCH_DISCONNECT)
657 		hdd_cm_set_default_wlm_mode(adapter);
658 
659 	__hdd_cm_disconnect_handler_post_user_update(link_info, vdev,
660 						     rsp->req.req.source);
661 	wlan_twt_concurrency_update(hdd_ctx);
662 	hdd_cm_reset_udp_qos_upgrade_config(adapter);
663 	ucfg_mlme_set_ml_link_control_mode(hdd_ctx->psoc,
664 					   vdev->vdev_objmgr.vdev_id, 0);
665 
666 	return QDF_STATUS_SUCCESS;
667 }
668 
669 #ifdef FEATURE_RUNTIME_PM
670 static void
wlan_hdd_runtime_pm_wow_disconnect_handler(struct hdd_context * hdd_ctx)671 wlan_hdd_runtime_pm_wow_disconnect_handler(struct hdd_context *hdd_ctx)
672 {
673 	struct hif_opaque_softc *hif_ctx;
674 
675 	if (!hdd_ctx) {
676 		hdd_err("hdd_ctx is NULL");
677 		return;
678 	}
679 
680 	hif_ctx = cds_get_context(QDF_MODULE_ID_HIF);
681 	if (!hif_ctx) {
682 		hdd_err("hif_ctx is NULL");
683 		return;
684 	}
685 
686 	if (hdd_is_any_sta_connected(hdd_ctx)) {
687 		hdd_debug("active connections: runtime pm prevented: %d",
688 			  hdd_ctx->runtime_pm_prevented);
689 		return;
690 	}
691 
692 	hdd_debug("Runtime allowed : %d", hdd_ctx->runtime_pm_prevented);
693 	qdf_spin_lock_irqsave(&hdd_ctx->pm_qos_lock);
694 	if (hdd_ctx->runtime_pm_prevented) {
695 		qdf_rtpm_put(QDF_RTPM_PUT, QDF_RTPM_ID_PM_QOS_NOTIFY);
696 		hdd_ctx->runtime_pm_prevented = false;
697 	}
698 	qdf_spin_unlock_irqrestore(&hdd_ctx->pm_qos_lock);
699 }
700 #else
701 static void
wlan_hdd_runtime_pm_wow_disconnect_handler(struct hdd_context * hdd_ctx)702 wlan_hdd_runtime_pm_wow_disconnect_handler(struct hdd_context *hdd_ctx)
703 {
704 }
705 #endif
706 
hdd_cm_disconnect_complete(struct wlan_objmgr_vdev * vdev,struct wlan_cm_discon_rsp * rsp,enum osif_cb_type type)707 QDF_STATUS hdd_cm_disconnect_complete(struct wlan_objmgr_vdev *vdev,
708 				      struct wlan_cm_discon_rsp *rsp,
709 				      enum osif_cb_type type)
710 {
711 	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
712 
713 	switch (type) {
714 	case OSIF_PRE_USERSPACE_UPDATE:
715 		return hdd_cm_disconnect_complete_pre_user_update(vdev, rsp);
716 	case OSIF_POST_USERSPACE_UPDATE:
717 		hdd_debug("Wifi disconnected: vdev id %d",
718 			  vdev->vdev_objmgr.vdev_id);
719 		wlan_hdd_runtime_pm_wow_disconnect_handler(hdd_ctx);
720 
721 		return hdd_cm_disconnect_complete_post_user_update(vdev, rsp);
722 	default:
723 		hdd_cm_disconnect_complete_pre_user_update(vdev, rsp);
724 		hdd_cm_disconnect_complete_post_user_update(vdev, rsp);
725 		return QDF_STATUS_SUCCESS;
726 	}
727 }
728 
hdd_cm_netif_queue_control(struct wlan_objmgr_vdev * vdev,enum netif_action_type action,enum netif_reason_type reason)729 QDF_STATUS hdd_cm_netif_queue_control(struct wlan_objmgr_vdev *vdev,
730 				      enum netif_action_type action,
731 				      enum netif_reason_type reason)
732 {
733 	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
734 	struct wlan_hdd_link_info *link_info;
735 
736 	if (!hdd_ctx) {
737 		hdd_err("hdd_ctx is NULL");
738 		return QDF_STATUS_E_INVAL;
739 	}
740 
741 	link_info = hdd_get_link_info_by_vdev(hdd_ctx, wlan_vdev_get_id(vdev));
742 	if (!link_info) {
743 		hdd_err("adapter is NULL for vdev %d", wlan_vdev_get_id(vdev));
744 		return QDF_STATUS_E_INVAL;
745 	}
746 
747 	wlan_hdd_netif_queue_control(link_info->adapter, action, reason);
748 
749 	return QDF_STATUS_SUCCESS;
750 }
751 
hdd_cm_napi_serialize_control(bool action)752 QDF_STATUS hdd_cm_napi_serialize_control(bool action)
753 {
754 	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
755 
756 	if (!hdd_ctx) {
757 		hdd_err("hdd_ctx is NULL");
758 		return QDF_STATUS_E_INVAL;
759 	}
760 
761 	hdd_napi_serialize(action);
762 
763 	/* reinit scan reject params for napi off (roam abort/ho fail) */
764 	if (!action)
765 		hdd_init_scan_reject_params(hdd_ctx);
766 
767 	return QDF_STATUS_SUCCESS;
768 }
769 
770 #ifdef WLAN_BOOST_CPU_FREQ_IN_ROAM
hdd_cm_perfd_set_cpufreq(bool action)771 QDF_STATUS hdd_cm_perfd_set_cpufreq(bool action)
772 {
773 	struct wlan_core_minfreq req;
774 	struct hdd_context *hdd_ctx;
775 
776 	hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
777 	if (unlikely(!hdd_ctx)) {
778 		hdd_err("cannot get hdd_context");
779 		return QDF_STATUS_E_INVAL;
780 	}
781 
782 	if (action) {
783 		req.magic    = WLAN_CORE_MINFREQ_MAGIC;
784 		req.reserved = 0; /* unused */
785 		req.coremask = 0x00ff;/* big and little cluster */
786 		req.freq     = 0xfff;/* set to max freq */
787 	} else {
788 		req.magic    = WLAN_CORE_MINFREQ_MAGIC;
789 		req.reserved = 0; /* unused */
790 		req.coremask = 0; /* not valid */
791 		req.freq     = 0; /* reset */
792 	}
793 
794 	hdd_debug("CPU min freq to 0x%x coremask 0x%x", req.freq, req.coremask);
795 	/* the following service function returns void */
796 	wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index,
797 				    WLAN_SVC_CORE_MINFREQ,
798 				    &req, sizeof(struct wlan_core_minfreq));
799 	return QDF_STATUS_SUCCESS;
800 }
801 #endif
802