diff --git a/target_if/mlme/vdev_mgr/src/target_if_vdev_mgr_rx_ops.c b/target_if/mlme/vdev_mgr/src/target_if_vdev_mgr_rx_ops.c index 0e5fc987e4..afa2e8dbcd 100644 --- a/target_if/mlme/vdev_mgr/src/target_if_vdev_mgr_rx_ops.c +++ b/target_if/mlme/vdev_mgr/src/target_if_vdev_mgr_rx_ops.c @@ -775,6 +775,7 @@ static int target_if_pdev_csa_status_event_handler( struct target_psoc_info *tgt_hdl; int i; QDF_STATUS status; + struct wlan_lmac_if_mlme_rx_ops *rx_ops = NULL; if (!scn || !data) { mlme_err("Invalid input"); @@ -787,6 +788,12 @@ static int target_if_pdev_csa_status_event_handler( return -EINVAL; } + rx_ops = target_if_vdev_mgr_get_rx_ops(psoc); + if (!rx_ops || !rx_ops->vdev_mgr_set_max_channel_switch_time) { + mlme_err("No Rx Ops"); + return -EINVAL; + } + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); if (!wmi_handle) { mlme_err("wmi_handle is null"); @@ -807,6 +814,10 @@ static int target_if_pdev_csa_status_event_handler( return -EINVAL; } + if (csa_status.current_switch_count == 1) + rx_ops->vdev_mgr_set_max_channel_switch_time + (psoc, csa_status.vdev_ids, csa_status.num_vdevs); + if (wlan_psoc_nif_fw_ext_cap_get(psoc, WLAN_SOC_CEXT_CSA_TX_OFFLOAD)) { for (i = 0; i < csa_status.num_vdevs; i++) { if (!csa_status.current_switch_count) diff --git a/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h b/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h index 4690fa476e..dbc92cc4ed 100644 --- a/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h +++ b/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h @@ -2020,6 +2020,8 @@ struct wlan_lmac_if_dfs_rx_ops { * @vdev_mgr_multi_vdev_restart_resp: function to handle mvr response * @vdev_mgr_set_mac_addr_response: Callback to get response for set MAC address * command + * @vdev_mgr_set_max_channel_switch_time: Set max channel switch time for the + * given vdev list. */ struct wlan_lmac_if_mlme_rx_ops { QDF_STATUS (*vdev_mgr_start_response)( @@ -2053,6 +2055,9 @@ struct wlan_lmac_if_mlme_rx_ops { #ifdef WLAN_FEATURE_DYNAMIC_MAC_ADDR_UPDATE void (*vdev_mgr_set_mac_addr_response)(uint8_t vdev_id, uint8_t status); #endif + void (*vdev_mgr_set_max_channel_switch_time) + (struct wlan_objmgr_psoc *psoc, + uint32_t *vdev_ids, uint32_t num_vdevs); }; #ifdef WLAN_SUPPORT_GREEN_AP diff --git a/umac/mlme/include/wlan_vdev_mlme.h b/umac/mlme/include/wlan_vdev_mlme.h index 13d246534d..d21db4ebc6 100644 --- a/umac/mlme/include/wlan_vdev_mlme.h +++ b/umac/mlme/include/wlan_vdev_mlme.h @@ -370,11 +370,16 @@ struct wlan_vdev_aid_mgr { * @hidden_ssid: flag to indicate whether it is hidden ssid * @cac_duration_ms: cac duration in millseconds * @aid_mgr: AID bitmap mgr + * @max_chan_switch_time: Max channel switch time in milliseconds. + * @last_bcn_ts_ms: Timestamp (in milliseconds) of the last beacon sent on the + * CSA triggered channel. */ struct vdev_mlme_mgmt_ap { bool hidden_ssid; uint32_t cac_duration_ms; struct wlan_vdev_aid_mgr *aid_mgr; + uint32_t max_chan_switch_time; + unsigned long last_bcn_ts_ms; }; /** diff --git a/umac/mlme/vdev_mgr/core/src/vdev_mgr_ops.c b/umac/mlme/vdev_mgr/core/src/vdev_mgr_ops.c index 3567dc6a57..11dd9a7a10 100644 --- a/umac/mlme/vdev_mgr/core/src/vdev_mgr_ops.c +++ b/umac/mlme/vdev_mgr/core/src/vdev_mgr_ops.c @@ -636,6 +636,12 @@ QDF_STATUS vdev_mgr_up_send(struct vdev_mlme_obj *mlme_obj) if (QDF_IS_STATUS_ERROR(status)) return status; + /* Reset the max channel switch time and last beacon sent time as the + * VDEV UP command sent to FW. + */ + mlme_obj->mgmt.ap.max_chan_switch_time = 0; + mlme_obj->mgmt.ap.last_bcn_ts_ms = 0; + is_6g_sap_fd_enabled = wlan_vdev_mlme_feat_ext_cap_get(vdev, WLAN_VDEV_FEXT_FILS_DISC_6G_SAP); mlme_debug("SAP FD enabled %d", is_6g_sap_fd_enabled); diff --git a/umac/mlme/vdev_mgr/dispatcher/inc/wlan_vdev_mgr_utils_api.h b/umac/mlme/vdev_mgr/dispatcher/inc/wlan_vdev_mgr_utils_api.h index 296fba7b87..ac48310169 100644 --- a/umac/mlme/vdev_mgr/dispatcher/inc/wlan_vdev_mgr_utils_api.h +++ b/umac/mlme/vdev_mgr/dispatcher/inc/wlan_vdev_mgr_utils_api.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -30,6 +31,16 @@ #include #include +/* The total time required to receive CSA event handler from FW with CSA count + * 0, plus, time required to process the CSA event, plus, time required to + * send multi-vdev restart request on the new channel and send updated beacon + * template is approximately 1 second (considered 16 AP vaps). + */ +#define VDEV_RESTART_TIME 1 + +/* Convert seconds to milliseconds */ +#define SECONDS_TO_MS(seconds) ((seconds) * 1000) + /** * wlan_util_vdev_get_cdp_txrx_opmode - get cdp txrx opmode from qdf mode * @vdev: pointer to vdev object @@ -97,4 +108,42 @@ void wlan_util_vdev_get_param(struct wlan_objmgr_vdev *vdev, enum wlan_mlme_cfg_id param_id, uint32_t *param_value); +/** + * wlan_util_vdev_mgr_get_csa_channel_switch_time() - Returns the time required + * to switch the channel after completing the CSA announcement. This does not + * include the CAC duration. + * @vdev: Pointer to vdev object + * @chan_switch_time: Pointer to save the CSA channel switch time. This does not + * include the DFS CAC duration + * + * Return: QDF_STATUS - Success or Failure + */ +QDF_STATUS wlan_util_vdev_mgr_get_csa_channel_switch_time( + struct wlan_objmgr_vdev *vdev, + uint32_t *chan_switch_time); + +/** + * wlan_util_vdev_mgr_compute_max_channel_switch_time() - Compute the max + * channel switch time for the given vdev + * @vdev: pointer to vdev object + * @max_chan_switch_time: Pointer to save the computed max channel switch time + * + * Return: QDF_STATUS - Success or Failure + */ +QDF_STATUS wlan_util_vdev_mgr_compute_max_channel_switch_time( + struct wlan_objmgr_vdev *vdev, uint32_t *max_chan_switch_time); + +/** + * wlan_utils_get_vdev_remaining_channel_switch_time() - Get the remaining + * channel switch time. + * @vdev: Pointer to vdev object + * + * Remaining channel switch time is equal to the time when last beacon sent on + * the CSA triggered vap plus max channel switch time minus current + * time. + * + * Return: Remaining cac time + */ +uint32_t wlan_utils_get_vdev_remaining_channel_switch_time( + struct wlan_objmgr_vdev *vdev); #endif /* __WLAN_VDEV_MGR_UTILS_API_H__ */ diff --git a/umac/mlme/vdev_mgr/dispatcher/src/wlan_vdev_mgr_tgt_if_rx_api.c b/umac/mlme/vdev_mgr/dispatcher/src/wlan_vdev_mgr_tgt_if_rx_api.c index c5c7c34e13..06fa1a704f 100644 --- a/umac/mlme/vdev_mgr/dispatcher/src/wlan_vdev_mgr_tgt_if_rx_api.c +++ b/umac/mlme/vdev_mgr/dispatcher/src/wlan_vdev_mgr_tgt_if_rx_api.c @@ -33,6 +33,7 @@ #include #include #include +#include void tgt_vdev_mgr_reset_response_timer_info(struct wlan_objmgr_psoc *psoc) @@ -278,6 +279,54 @@ static inline void tgt_vdev_mgr_reg_set_mac_address_response( } #endif +static void tgt_vdev_mgr_set_max_channel_switch_time( + struct wlan_objmgr_psoc *psoc, uint32_t *vdev_ids, + uint32_t num_vdevs) +{ + struct wlan_objmgr_vdev *vdev = NULL; + struct vdev_mlme_obj *vdev_mlme = NULL; + unsigned long current_time = qdf_mc_timer_get_system_time(); + uint32_t max_chan_switch_time = 0; + int i = 0; + QDF_STATUS status; + + /* Compute and populate the max channel switch time and time of the last + * beacon sent on the CSA triggered channel for all the vdevs. + */ + for (i = 0; i < num_vdevs; i++) { + vdev = wlan_objmgr_get_vdev_by_id_from_psoc + (psoc, vdev_ids[i], WLAN_VDEV_TARGET_IF_ID); + if (!vdev) { + mlme_err("VDEV is NULL"); + continue; + } + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!vdev_mlme) { + mlme_err("VDEV_%d: PSOC_%d VDEV_MLME is NULL", + vdev_ids[i], + wlan_psoc_get_id(psoc)); + wlan_objmgr_vdev_release_ref(vdev, + WLAN_VDEV_TARGET_IF_ID); + continue; + } + + status = wlan_util_vdev_mgr_compute_max_channel_switch_time + (vdev, &max_chan_switch_time); + if (QDF_IS_STATUS_ERROR(status)) { + mlme_err("Failed to get the max channel switch time value"); + wlan_objmgr_vdev_release_ref(vdev, + WLAN_VDEV_TARGET_IF_ID); + continue; + } + + vdev_mlme->mgmt.ap.last_bcn_ts_ms = current_time; + vdev_mlme->mgmt.ap.max_chan_switch_time = max_chan_switch_time; + + wlan_objmgr_vdev_release_ref(vdev, WLAN_VDEV_TARGET_IF_ID); + } +} + void tgt_vdev_mgr_register_rx_ops(struct wlan_lmac_if_rx_ops *rx_ops) { struct wlan_lmac_if_mlme_rx_ops *mlme_rx_ops = &rx_ops->mops; @@ -298,6 +347,8 @@ void tgt_vdev_mgr_register_rx_ops(struct wlan_lmac_if_rx_ops *rx_ops) tgt_vdev_mgr_get_response_timer_info; mlme_rx_ops->vdev_mgr_multi_vdev_restart_resp = tgt_vdev_mgr_multi_vdev_restart_resp_handler; + mlme_rx_ops->vdev_mgr_set_max_channel_switch_time = + tgt_vdev_mgr_set_max_channel_switch_time; tgt_psoc_reg_wakelock_info_rx_op(&rx_ops->mops); tgt_vdev_mgr_reg_set_mac_address_response(mlme_rx_ops); } diff --git a/umac/mlme/vdev_mgr/dispatcher/src/wlan_vdev_mgr_utils_api.c b/umac/mlme/vdev_mgr/dispatcher/src/wlan_vdev_mgr_utils_api.c index 2541a32f0c..a57d1fd35e 100644 --- a/umac/mlme/vdev_mgr/dispatcher/src/wlan_vdev_mgr_utils_api.c +++ b/umac/mlme/vdev_mgr/dispatcher/src/wlan_vdev_mgr_utils_api.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -28,6 +29,10 @@ #include #include #include +#include +#ifndef MOBILE_DFS_SUPPORT +#include +#endif /* MOBILE_DFS_SUPPORT */ static QDF_STATUS vdev_mgr_config_ratemask_update( struct vdev_mlme_obj *mlme_obj, @@ -674,3 +679,119 @@ void wlan_util_vdev_get_param(struct wlan_objmgr_vdev *vdev, } qdf_export_symbol(wlan_util_vdev_get_param); + +/** + * wlan_util_vdev_mgr_get_cac_timeout_for_vdev() - Get the CAC timeout value for + * a given vdev. + * @vdev: Pointer to vdev object. + * + * Return: CAC timeout value + */ +#ifndef MOBILE_DFS_SUPPORT +static int wlan_util_vdev_mgr_get_cac_timeout_for_vdev( + struct wlan_objmgr_vdev *vdev) +{ + struct wlan_channel *des_chan = NULL; + struct wlan_channel *bss_chan = NULL; + bool continue_current_cac = 0; + + des_chan = wlan_vdev_mlme_get_des_chan(vdev); + if (!des_chan) + return 0; + + bss_chan = wlan_vdev_mlme_get_bss_chan(vdev); + if (!bss_chan) + return 0; + + if (!utils_dfs_is_cac_required(wlan_vdev_get_pdev(vdev), des_chan, + bss_chan, &continue_current_cac)) + return 0; + + return dfs_mlme_get_cac_timeout_for_freq(wlan_vdev_get_pdev(vdev), + des_chan->ch_freq, + des_chan->ch_cfreq2, + des_chan->ch_flags); +} +#else +static int wlan_util_vdev_mgr_get_cac_timeout_for_vdev( + struct wlan_objmgr_vdev *vdev) +{ + return 0; +} +#endif /* MOBILE_DFS_SUPPORT */ + +QDF_STATUS wlan_util_vdev_mgr_get_csa_channel_switch_time( + struct wlan_objmgr_vdev *vdev, + uint32_t *chan_switch_time) +{ + struct vdev_mlme_obj *vdev_mlme = NULL; + + *chan_switch_time = 0; + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!vdev_mlme) { + mlme_err("vdev_mlme is null"); + return QDF_STATUS_E_FAILURE; + } + + /* Time between CSA count 1 and CSA count 0 is one beacon interval. */ + *chan_switch_time = vdev_mlme->proto.generic.beacon_interval; + + /* Vdev restart time */ + *chan_switch_time += SECONDS_TO_MS(VDEV_RESTART_TIME); + + /* Add one beacon interval time required to send beacon on the + * new channel after switching to the new channel. + */ + *chan_switch_time += vdev_mlme->proto.generic.beacon_interval; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_util_vdev_mgr_compute_max_channel_switch_time( + struct wlan_objmgr_vdev *vdev, uint32_t *max_chan_switch_time) +{ + int dfs_cac_timeout = 0; + QDF_STATUS status; + + status = wlan_util_vdev_mgr_get_csa_channel_switch_time( + vdev, max_chan_switch_time); + if (QDF_IS_STATUS_ERROR(status)) { + mlme_err("Failed to get the CSA channel switch time"); + return status; + } + + /* Get the CAC time */ + dfs_cac_timeout = wlan_util_vdev_mgr_get_cac_timeout_for_vdev(vdev); + + /* Seconds to milliseconds */ + *max_chan_switch_time += SECONDS_TO_MS(dfs_cac_timeout); + + return QDF_STATUS_SUCCESS; +} + +uint32_t +wlan_utils_get_vdev_remaining_channel_switch_time(struct wlan_objmgr_vdev *vdev) +{ + struct vdev_mlme_obj *vdev_mlme = NULL; + int32_t remaining_chan_switch_time; + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!vdev_mlme) + return 0; + + if (!vdev_mlme->mgmt.ap.last_bcn_ts_ms) + return 0; + + /* Remaining channel switch time is equal to the time when last beacon + * sent on the CSA triggered vap plus max channel switch time minus + * current time. + */ + remaining_chan_switch_time = + ((vdev_mlme->mgmt.ap.last_bcn_ts_ms + + vdev_mlme->mgmt.ap.max_chan_switch_time) - + qdf_mc_timer_get_system_time()); + + return (remaining_chan_switch_time > 0) ? + remaining_chan_switch_time : 0; +}