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