| /* |
| * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. |
| * Copyright (c) 2021-2022 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 |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /** |
| * DOC: wlan_hdd_cfg80211.c |
| * |
| * WLAN Host Device Driver cfg80211 APIs implementation |
| * |
| */ |
| |
| #include <linux/version.h> |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/etherdevice.h> |
| #include <linux/wireless.h> |
| #include "osif_sync.h" |
| #include <wlan_hdd_includes.h> |
| #include <net/arp.h> |
| #include <net/cfg80211.h> |
| #include <net/mac80211.h> |
| #include <wlan_hdd_wowl.h> |
| #include <ani_global.h> |
| #include "sir_params.h" |
| #include "dot11f.h" |
| #include "wlan_hdd_assoc.h" |
| #include "wlan_hdd_wext.h" |
| #include "sme_api.h" |
| #include "sme_power_save_api.h" |
| #include "wlan_hdd_p2p.h" |
| #include "wlan_hdd_cfg80211.h" |
| #include "wlan_hdd_hostapd.h" |
| #include "wlan_hdd_softap_tx_rx.h" |
| #include "wlan_hdd_main.h" |
| #include "wlan_hdd_power.h" |
| #include "wlan_hdd_trace.h" |
| #include "wlan_hdd_tx_rx.h" |
| #include "qdf_str.h" |
| #include "qdf_trace.h" |
| #include "qdf_types.h" |
| #include "cds_utils.h" |
| #include "cds_sched.h" |
| #include "wlan_hdd_scan.h" |
| #include <qc_sap_ioctl.h> |
| #include "wlan_hdd_tdls.h" |
| #include "wlan_hdd_wmm.h" |
| #include "wma_types.h" |
| #include "wma.h" |
| #include "wma_twt.h" |
| #include "wlan_hdd_misc.h" |
| #include "wlan_hdd_nan.h" |
| #include "wlan_logging_sock_svc.h" |
| #include "sap_api.h" |
| #include "csr_api.h" |
| #include "pld_common.h" |
| #include "wmi_unified_param.h" |
| |
| #include <cdp_txrx_handle.h> |
| #include <wlan_cfg80211_scan.h> |
| #include <wlan_cfg80211_ftm.h> |
| |
| #include "wlan_hdd_ext_scan.h" |
| |
| #include "wlan_hdd_stats.h" |
| #include "cds_api.h" |
| #include "wlan_policy_mgr_api.h" |
| #include "qwlan_version.h" |
| |
| #include "wlan_hdd_ocb.h" |
| #include "wlan_hdd_tsf.h" |
| |
| #include "wlan_hdd_subnet_detect.h" |
| #include <wlan_hdd_regulatory.h> |
| #include "wlan_hdd_lpass.h" |
| #include "wlan_hdd_nan_datapath.h" |
| #include "wlan_hdd_disa.h" |
| #include "wlan_osif_request_manager.h" |
| #include "wlan_hdd_he.h" |
| #ifdef FEATURE_WLAN_APF |
| #include "wlan_hdd_apf.h" |
| #endif |
| #include "wlan_hdd_fw_state.h" |
| #include "wlan_hdd_mpta_helper.h" |
| |
| #include <cdp_txrx_cmn.h> |
| #include <cdp_txrx_misc.h> |
| #include <cdp_txrx_ctrl.h> |
| #include <qca_vendor.h> |
| #include "wlan_pmo_ucfg_api.h" |
| #include "os_if_wifi_pos.h" |
| #include "wlan_utility.h" |
| #include "wlan_reg_ucfg_api.h" |
| #include "wifi_pos_api.h" |
| #include "wlan_hdd_spectralscan.h" |
| #include "wlan_ipa_ucfg_api.h" |
| #include <wlan_cfg80211_mc_cp_stats.h> |
| #include <wlan_cp_stats_mc_ucfg_api.h> |
| #include "wlan_tdls_cfg_api.h" |
| #include "wlan_tdls_ucfg_api.h" |
| #include <wlan_hdd_bss_transition.h> |
| #include <wlan_hdd_concurrency_matrix.h> |
| #include <wlan_hdd_p2p_listen_offload.h> |
| #include <wlan_hdd_rssi_monitor.h> |
| #include <wlan_hdd_sap_cond_chan_switch.h> |
| #include <wlan_hdd_station_info.h> |
| #include <wlan_hdd_tx_power.h> |
| #include <wlan_hdd_active_tos.h> |
| #include <wlan_hdd_sar_limits.h> |
| #include <wlan_hdd_ota_test.h> |
| #include "wlan_policy_mgr_ucfg.h" |
| #include "wlan_mlme_ucfg_api.h" |
| #include "wlan_mlme_twt_ucfg_api.h" |
| #include "wlan_mlme_public_struct.h" |
| #include "wlan_extscan_ucfg_api.h" |
| #include "wlan_pmo_cfg.h" |
| #include "cfg_ucfg_api.h" |
| |
| #include "wlan_crypto_def_i.h" |
| #include "wlan_crypto_global_api.h" |
| #include "wlan_nl_to_crypto_params.h" |
| #include "wlan_crypto_global_def.h" |
| #include "cdp_txrx_cfg.h" |
| #include "wlan_hdd_object_manager.h" |
| #include "nan_ucfg_api.h" |
| #include "wlan_fwol_ucfg_api.h" |
| #include "wlan_cfg80211_crypto.h" |
| #include "wlan_cfg80211_interop_issues_ap.h" |
| #include "wlan_scan_ucfg_api.h" |
| #include "wlan_hdd_coex_config.h" |
| #include "wlan_hdd_bcn_recv.h" |
| #include "wlan_hdd_connectivity_logging.h" |
| #include "wlan_blm_ucfg_api.h" |
| #include "wlan_hdd_hw_capability.h" |
| #include "wlan_hdd_oemdata.h" |
| #include "os_if_fwol.h" |
| #include "wlan_hdd_sta_info.h" |
| #include "sme_api.h" |
| #include "wlan_hdd_thermal.h" |
| #include <ol_defines.h> |
| #include "wlan_hdd_btc_chain_mode.h" |
| #include "os_if_nan.h" |
| #include "wlan_hdd_apf.h" |
| #include "wlan_hdd_cfr.h" |
| #include "wlan_hdd_ioctl.h" |
| #include "wlan_cm_roam_ucfg_api.h" |
| #include "hif.h" |
| #include "wlan_reg_ucfg_api.h" |
| #include "wlan_hdd_twt.h" |
| #include "wlan_hdd_gpio.h" |
| #include "wlan_hdd_medium_assess.h" |
| #include "wlan_if_mgr_ucfg_api.h" |
| #include "wlan_if_mgr_public_struct.h" |
| #include "wlan_wfa_ucfg_api.h" |
| #include <osif_cm_util.h> |
| #include <osif_cm_req.h> |
| #include "wlan_hdd_bootup_marker.h" |
| #include "wlan_hdd_cm_api.h" |
| #include "wlan_roam_debug.h" |
| #include "wlan_hdd_avoid_freq_ext.h" |
| #include "qdf_util.h" |
| #include "wlan_hdd_mdns_offload.h" |
| #include "wlan_pkt_capture_ucfg_api.h" |
| #include "os_if_pkt_capture.h" |
| |
| #define g_mode_rates_size (12) |
| #define a_mode_rates_size (8) |
| |
| #define WLAN_WAIT_WLM_LATENCY_LEVEL 1000 |
| |
| /** |
| * rtt_is_initiator - Macro to check if the bitmap has any RTT roles set |
| * @bitmap: The bitmap to be checked |
| */ |
| #define rtt_is_enabled(bitmap) \ |
| ((bitmap) & (WMI_FW_STA_RTT_INITR | \ |
| WMI_FW_STA_RTT_RESPR | \ |
| WMI_FW_AP_RTT_INITR | \ |
| WMI_FW_AP_RTT_RESPR)) |
| |
| /* |
| * Android CTS verifier needs atleast this much wait time (in msec) |
| */ |
| #define MAX_REMAIN_ON_CHANNEL_DURATION (2000) |
| |
| #define HDD2GHZCHAN(freq, chan, flag) { \ |
| .band = HDD_NL80211_BAND_2GHZ, \ |
| .center_freq = (freq), \ |
| .hw_value = (chan), \ |
| .flags = (flag), \ |
| .max_antenna_gain = 0, \ |
| .max_power = 0, \ |
| } |
| |
| #define HDD5GHZCHAN(freq, chan, flag) { \ |
| .band = HDD_NL80211_BAND_5GHZ, \ |
| .center_freq = (freq), \ |
| .hw_value = (chan), \ |
| .flags = (flag), \ |
| .max_antenna_gain = 0, \ |
| .max_power = 0, \ |
| } |
| |
| #define HDD_G_MODE_RATETAB(rate, rate_id, flag) \ |
| { \ |
| .bitrate = rate, \ |
| .hw_value = rate_id, \ |
| .flags = flag, \ |
| } |
| |
| #define IS_DFS_MODE_VALID(mode) ((mode >= DFS_MODE_NONE && \ |
| mode <= DFS_MODE_DEPRIORITIZE)) |
| |
| #ifndef WLAN_CIPHER_SUITE_GCMP |
| #define WLAN_CIPHER_SUITE_GCMP 0x000FAC08 |
| #endif |
| #ifndef WLAN_CIPHER_SUITE_GCMP_256 |
| #define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09 |
| #endif |
| |
| static const u32 hdd_gcmp_cipher_suits[] = { |
| WLAN_CIPHER_SUITE_GCMP, |
| WLAN_CIPHER_SUITE_GCMP_256, |
| }; |
| |
| static const u32 hdd_cipher_suites[] = { |
| WLAN_CIPHER_SUITE_WEP40, |
| WLAN_CIPHER_SUITE_WEP104, |
| WLAN_CIPHER_SUITE_TKIP, |
| #ifdef FEATURE_WLAN_ESE |
| #define WLAN_CIPHER_SUITE_BTK 0x004096fe /* use for BTK */ |
| #define WLAN_CIPHER_SUITE_KRK 0x004096ff /* use for KRK */ |
| WLAN_CIPHER_SUITE_BTK, |
| WLAN_CIPHER_SUITE_KRK, |
| WLAN_CIPHER_SUITE_CCMP, |
| #else |
| WLAN_CIPHER_SUITE_CCMP, |
| #endif |
| #ifdef FEATURE_WLAN_WAPI |
| WLAN_CIPHER_SUITE_SMS4, |
| #endif |
| WLAN_CIPHER_SUITE_AES_CMAC, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) |
| WLAN_CIPHER_SUITE_BIP_GMAC_128, |
| WLAN_CIPHER_SUITE_BIP_GMAC_256, |
| #endif |
| }; |
| |
| static const struct ieee80211_channel hdd_channels_2_4_ghz[] = { |
| HDD2GHZCHAN(2412, 1, 0), |
| HDD2GHZCHAN(2417, 2, 0), |
| HDD2GHZCHAN(2422, 3, 0), |
| HDD2GHZCHAN(2427, 4, 0), |
| HDD2GHZCHAN(2432, 5, 0), |
| HDD2GHZCHAN(2437, 6, 0), |
| HDD2GHZCHAN(2442, 7, 0), |
| HDD2GHZCHAN(2447, 8, 0), |
| HDD2GHZCHAN(2452, 9, 0), |
| HDD2GHZCHAN(2457, 10, 0), |
| HDD2GHZCHAN(2462, 11, 0), |
| HDD2GHZCHAN(2467, 12, 0), |
| HDD2GHZCHAN(2472, 13, 0), |
| HDD2GHZCHAN(2484, 14, 0), |
| }; |
| |
| static const struct ieee80211_channel hdd_channels_5_ghz[] = { |
| HDD5GHZCHAN(5180, 36, 0), |
| HDD5GHZCHAN(5200, 40, 0), |
| HDD5GHZCHAN(5220, 44, 0), |
| HDD5GHZCHAN(5240, 48, 0), |
| HDD5GHZCHAN(5260, 52, 0), |
| HDD5GHZCHAN(5280, 56, 0), |
| HDD5GHZCHAN(5300, 60, 0), |
| HDD5GHZCHAN(5320, 64, 0), |
| HDD5GHZCHAN(5500, 100, 0), |
| HDD5GHZCHAN(5520, 104, 0), |
| HDD5GHZCHAN(5540, 108, 0), |
| HDD5GHZCHAN(5560, 112, 0), |
| HDD5GHZCHAN(5580, 116, 0), |
| HDD5GHZCHAN(5600, 120, 0), |
| HDD5GHZCHAN(5620, 124, 0), |
| HDD5GHZCHAN(5640, 128, 0), |
| HDD5GHZCHAN(5660, 132, 0), |
| HDD5GHZCHAN(5680, 136, 0), |
| HDD5GHZCHAN(5700, 140, 0), |
| HDD5GHZCHAN(5720, 144, 0), |
| HDD5GHZCHAN(5745, 149, 0), |
| HDD5GHZCHAN(5765, 153, 0), |
| HDD5GHZCHAN(5785, 157, 0), |
| HDD5GHZCHAN(5805, 161, 0), |
| HDD5GHZCHAN(5825, 165, 0), |
| }; |
| |
| #ifdef WLAN_FEATURE_DSRC |
| static const struct ieee80211_channel hdd_channels_dot11p[] = { |
| HDD5GHZCHAN(5852, 170, 0), |
| HDD5GHZCHAN(5855, 171, 0), |
| HDD5GHZCHAN(5860, 172, 0), |
| HDD5GHZCHAN(5865, 173, 0), |
| HDD5GHZCHAN(5870, 174, 0), |
| HDD5GHZCHAN(5875, 175, 0), |
| HDD5GHZCHAN(5880, 176, 0), |
| HDD5GHZCHAN(5885, 177, 0), |
| HDD5GHZCHAN(5890, 178, 0), |
| HDD5GHZCHAN(5895, 179, 0), |
| HDD5GHZCHAN(5900, 180, 0), |
| HDD5GHZCHAN(5905, 181, 0), |
| HDD5GHZCHAN(5910, 182, 0), |
| HDD5GHZCHAN(5915, 183, 0), |
| HDD5GHZCHAN(5920, 184, 0), |
| }; |
| #else |
| static const struct ieee80211_channel hdd_5dot9_ghz_ch[] = { |
| HDD5GHZCHAN(5845, 169, 0), |
| HDD5GHZCHAN(5865, 173, 0), |
| HDD5GHZCHAN(5885, 177, 0), |
| }; |
| #endif |
| |
| #define band_2_ghz_channels_size sizeof(hdd_channels_2_4_ghz) |
| |
| #ifdef WLAN_FEATURE_DSRC |
| #define band_5_ghz_chanenls_size (sizeof(hdd_channels_5_ghz) + \ |
| sizeof(hdd_channels_dot11p)) |
| #else |
| #define band_5_ghz_chanenls_size (sizeof(hdd_channels_5_ghz) + \ |
| sizeof(hdd_5dot9_ghz_ch)) |
| #endif |
| |
| static struct ieee80211_rate g_mode_rates[] = { |
| HDD_G_MODE_RATETAB(10, 0x1, 0), |
| HDD_G_MODE_RATETAB(20, 0x2, 0), |
| HDD_G_MODE_RATETAB(55, 0x4, 0), |
| HDD_G_MODE_RATETAB(110, 0x8, 0), |
| HDD_G_MODE_RATETAB(60, 0x10, 0), |
| HDD_G_MODE_RATETAB(90, 0x20, 0), |
| HDD_G_MODE_RATETAB(120, 0x40, 0), |
| HDD_G_MODE_RATETAB(180, 0x80, 0), |
| HDD_G_MODE_RATETAB(240, 0x100, 0), |
| HDD_G_MODE_RATETAB(360, 0x200, 0), |
| HDD_G_MODE_RATETAB(480, 0x400, 0), |
| HDD_G_MODE_RATETAB(540, 0x800, 0), |
| }; |
| |
| static struct ieee80211_rate a_mode_rates[] = { |
| HDD_G_MODE_RATETAB(60, 0x10, 0), |
| HDD_G_MODE_RATETAB(90, 0x20, 0), |
| HDD_G_MODE_RATETAB(120, 0x40, 0), |
| HDD_G_MODE_RATETAB(180, 0x80, 0), |
| HDD_G_MODE_RATETAB(240, 0x100, 0), |
| HDD_G_MODE_RATETAB(360, 0x200, 0), |
| HDD_G_MODE_RATETAB(480, 0x400, 0), |
| HDD_G_MODE_RATETAB(540, 0x800, 0), |
| }; |
| |
| static struct ieee80211_supported_band wlan_hdd_band_2_4_ghz = { |
| .channels = NULL, |
| .n_channels = ARRAY_SIZE(hdd_channels_2_4_ghz), |
| .band = HDD_NL80211_BAND_2GHZ, |
| .bitrates = g_mode_rates, |
| .n_bitrates = g_mode_rates_size, |
| .ht_cap.ht_supported = 1, |
| .ht_cap.cap = IEEE80211_HT_CAP_SGI_20 |
| | IEEE80211_HT_CAP_GRN_FLD |
| | IEEE80211_HT_CAP_DSSSCCK40 |
| | IEEE80211_HT_CAP_LSIG_TXOP_PROT |
| | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SUP_WIDTH_20_40, |
| .ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, |
| .ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, |
| .ht_cap.mcs.rx_mask = {0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, |
| .ht_cap.mcs.rx_highest = cpu_to_le16(72), |
| .ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED, |
| }; |
| |
| static struct ieee80211_supported_band wlan_hdd_band_5_ghz = { |
| .channels = NULL, |
| .n_channels = ARRAY_SIZE(hdd_channels_5_ghz), |
| .band = HDD_NL80211_BAND_5GHZ, |
| .bitrates = a_mode_rates, |
| .n_bitrates = a_mode_rates_size, |
| .ht_cap.ht_supported = 1, |
| .ht_cap.cap = IEEE80211_HT_CAP_SGI_20 |
| | IEEE80211_HT_CAP_GRN_FLD |
| | IEEE80211_HT_CAP_DSSSCCK40 |
| | IEEE80211_HT_CAP_LSIG_TXOP_PROT |
| | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SUP_WIDTH_20_40, |
| .ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, |
| .ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, |
| .ht_cap.mcs.rx_mask = {0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, |
| .ht_cap.mcs.rx_highest = cpu_to_le16(72), |
| .ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED, |
| .vht_cap.vht_supported = 1, |
| }; |
| |
| #if defined(CONFIG_BAND_6GHZ) && (defined(CFG80211_6GHZ_BAND_SUPPORTED) || \ |
| (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE)) |
| |
| static struct ieee80211_channel hdd_channels_6_ghz[NUM_6GHZ_CHANNELS]; |
| |
| static struct ieee80211_supported_band wlan_hdd_band_6_ghz = { |
| .channels = NULL, |
| .n_channels = 0, |
| .band = HDD_NL80211_BAND_6GHZ, |
| .bitrates = a_mode_rates, |
| .n_bitrates = a_mode_rates_size, |
| }; |
| |
| #define HDD_SET_6GHZCHAN(ch, freq, chan, flag) { \ |
| (ch).band = HDD_NL80211_BAND_6GHZ; \ |
| (ch).center_freq = (freq); \ |
| (ch).hw_value = (chan); \ |
| (ch).flags = (flag); \ |
| (ch).max_antenna_gain = 0; \ |
| (ch).max_power = 0; \ |
| } |
| |
| static void hdd_init_6ghz(struct hdd_context *hdd_ctx) |
| { |
| uint32_t i; |
| struct wiphy *wiphy = hdd_ctx->wiphy; |
| struct ieee80211_channel *chlist = hdd_channels_6_ghz; |
| uint32_t num = ARRAY_SIZE(hdd_channels_6_ghz); |
| uint16_t base_freq; |
| QDF_STATUS status; |
| uint32_t band_capability; |
| |
| hdd_enter(); |
| |
| status = ucfg_mlme_get_band_capability(hdd_ctx->psoc, &band_capability); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("Failed to get MLME Band Capability"); |
| return; |
| } |
| |
| if (!(band_capability & (BIT(REG_BAND_6G)))) { |
| hdd_debug("6ghz band not enabled"); |
| return; |
| } |
| |
| qdf_mem_zero(chlist, sizeof(*chlist) * num); |
| base_freq = wlan_reg_min_6ghz_chan_freq(); |
| |
| for (i = 0; i < num; i++) |
| HDD_SET_6GHZCHAN(chlist[i], |
| base_freq + i * 20, |
| wlan_reg_freq_to_chan(hdd_ctx->pdev, |
| base_freq + i * 20), |
| IEEE80211_CHAN_DISABLED); |
| wiphy->bands[HDD_NL80211_BAND_6GHZ] = &wlan_hdd_band_6_ghz; |
| wiphy->bands[HDD_NL80211_BAND_6GHZ]->channels = chlist; |
| wiphy->bands[HDD_NL80211_BAND_6GHZ]->n_channels = num; |
| |
| hdd_exit(); |
| } |
| #else |
| static void hdd_init_6ghz(struct hdd_context *hdd_ctx) |
| { |
| } |
| #endif |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)) || \ |
| defined(CFG80211_IFTYPE_AKM_SUITES_SUPPORT) |
| /*akm suits supported by sta*/ |
| static const u32 hdd_sta_akm_suites[] = { |
| WLAN_AKM_SUITE_8021X, |
| WLAN_AKM_SUITE_PSK, |
| WLAN_AKM_SUITE_FT_8021X, |
| WLAN_AKM_SUITE_FT_PSK, |
| WLAN_AKM_SUITE_8021X_SHA256, |
| WLAN_AKM_SUITE_PSK_SHA256, |
| WLAN_AKM_SUITE_TDLS, |
| WLAN_AKM_SUITE_SAE, |
| WLAN_AKM_SUITE_FT_OVER_SAE, |
| WLAN_AKM_SUITE_EAP_SHA256, |
| WLAN_AKM_SUITE_EAP_SHA384, |
| WLAN_AKM_SUITE_FILS_SHA256, |
| WLAN_AKM_SUITE_FILS_SHA384, |
| WLAN_AKM_SUITE_FT_FILS_SHA256, |
| WLAN_AKM_SUITE_FT_FILS_SHA384, |
| WLAN_AKM_SUITE_OWE, |
| WLAN_AKM_SUITE_DPP_RSN, |
| WLAN_AKM_SUITE_FT_EAP_SHA_384, |
| RSN_AUTH_KEY_MGMT_CCKM, |
| RSN_AUTH_KEY_MGMT_OSEN, |
| WAPI_PSK_AKM_SUITE, |
| WAPI_CERT_AKM_SUITE, |
| }; |
| |
| /*akm suits supported by AP*/ |
| static const u32 hdd_ap_akm_suites[] = { |
| WLAN_AKM_SUITE_PSK, |
| WLAN_AKM_SUITE_SAE, |
| WLAN_AKM_SUITE_OWE, |
| }; |
| |
| /* This structure contain information what akm suits are |
| * supported for each mode |
| */ |
| static const struct wiphy_iftype_akm_suites |
| wlan_hdd_akm_suites[] = { |
| { |
| .iftypes_mask = BIT(NL80211_IFTYPE_STATION) | |
| BIT(NL80211_IFTYPE_P2P_CLIENT), |
| .akm_suites = hdd_sta_akm_suites, |
| .n_akm_suites = (sizeof(hdd_sta_akm_suites) / sizeof(u32)), |
| }, |
| { |
| .iftypes_mask = BIT(NL80211_IFTYPE_AP) | |
| BIT(NL80211_IFTYPE_P2P_GO), |
| .akm_suites = hdd_ap_akm_suites, |
| .n_akm_suites = (sizeof(hdd_ap_akm_suites) / sizeof(u32)), |
| }, |
| }; |
| #endif |
| |
| /* This structure contain information what kind of frame are expected in |
| * TX/RX direction for each kind of interface |
| */ |
| static const struct ieee80211_txrx_stypes |
| wlan_hdd_txrx_stypes[NUM_NL80211_IFTYPES] = { |
| [NL80211_IFTYPE_STATION] = { |
| .tx = 0xffff, |
| .rx = BIT(SIR_MAC_MGMT_ACTION) | |
| BIT(SIR_MAC_MGMT_PROBE_REQ) | |
| BIT(SIR_MAC_MGMT_AUTH), |
| }, |
| [NL80211_IFTYPE_AP] = { |
| .tx = 0xffff, |
| .rx = BIT(SIR_MAC_MGMT_ASSOC_REQ) | |
| BIT(SIR_MAC_MGMT_REASSOC_REQ) | |
| BIT(SIR_MAC_MGMT_PROBE_REQ) | |
| BIT(SIR_MAC_MGMT_DISASSOC) | |
| BIT(SIR_MAC_MGMT_AUTH) | |
| BIT(SIR_MAC_MGMT_DEAUTH) | |
| BIT(SIR_MAC_MGMT_ACTION), |
| }, |
| [NL80211_IFTYPE_ADHOC] = { |
| .tx = 0xffff, |
| .rx = BIT(SIR_MAC_MGMT_ASSOC_REQ) | |
| BIT(SIR_MAC_MGMT_REASSOC_REQ) | |
| BIT(SIR_MAC_MGMT_PROBE_REQ) | |
| BIT(SIR_MAC_MGMT_DISASSOC) | |
| BIT(SIR_MAC_MGMT_AUTH) | |
| BIT(SIR_MAC_MGMT_DEAUTH) | |
| BIT(SIR_MAC_MGMT_ACTION), |
| }, |
| [NL80211_IFTYPE_P2P_CLIENT] = { |
| .tx = 0xffff, |
| .rx = BIT(SIR_MAC_MGMT_ACTION) | |
| BIT(SIR_MAC_MGMT_AUTH) | |
| BIT(SIR_MAC_MGMT_PROBE_REQ), |
| }, |
| [NL80211_IFTYPE_P2P_GO] = { |
| /* This is also same as for SoftAP */ |
| .tx = 0xffff, |
| .rx = BIT(SIR_MAC_MGMT_ASSOC_REQ) | |
| BIT(SIR_MAC_MGMT_REASSOC_REQ) | |
| BIT(SIR_MAC_MGMT_PROBE_REQ) | |
| BIT(SIR_MAC_MGMT_DISASSOC) | |
| BIT(SIR_MAC_MGMT_AUTH) | |
| BIT(SIR_MAC_MGMT_DEAUTH) | |
| BIT(SIR_MAC_MGMT_ACTION), |
| }, |
| }; |
| |
| /* Interface limits and combinations registered by the driver */ |
| |
| /* STA ( + STA ) combination */ |
| static const struct ieee80211_iface_limit |
| wlan_hdd_sta_iface_limit[] = { |
| { |
| .max = 3, /* p2p0 is a STA as well */ |
| .types = BIT(NL80211_IFTYPE_STATION), |
| }, |
| }; |
| |
| /* AP ( + AP ) combination */ |
| static const struct ieee80211_iface_limit |
| wlan_hdd_ap_iface_limit[] = { |
| { |
| .max = (QDF_MAX_NO_OF_SAP_MODE + SAP_MAX_OBSS_STA_CNT), |
| .types = BIT(NL80211_IFTYPE_AP), |
| }, |
| }; |
| |
| /* P2P limit */ |
| static const struct ieee80211_iface_limit |
| wlan_hdd_p2p_iface_limit[] = { |
| { |
| .max = 1, |
| .types = BIT(NL80211_IFTYPE_P2P_CLIENT), |
| }, |
| { |
| .max = 1, |
| .types = BIT(NL80211_IFTYPE_P2P_GO), |
| }, |
| }; |
| |
| static const struct ieee80211_iface_limit |
| wlan_hdd_sta_ap_iface_limit[] = { |
| { |
| /* We need 1 extra STA interface for OBSS scan when SAP starts |
| * with HT40 in STA+SAP concurrency mode |
| */ |
| .max = (1 + SAP_MAX_OBSS_STA_CNT), |
| .types = BIT(NL80211_IFTYPE_STATION), |
| }, |
| { |
| .max = QDF_MAX_NO_OF_SAP_MODE, |
| .types = BIT(NL80211_IFTYPE_AP), |
| }, |
| }; |
| |
| /* STA + P2P combination */ |
| static const struct ieee80211_iface_limit |
| wlan_hdd_sta_p2p_iface_limit[] = { |
| { |
| /* One reserved for dedicated P2PDEV usage */ |
| .max = 2, |
| .types = BIT(NL80211_IFTYPE_STATION) |
| }, |
| { |
| /* Support for two identical (GO + GO or CLI + CLI) |
| * or dissimilar (GO + CLI) P2P interfaces |
| */ |
| .max = 2, |
| .types = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT), |
| }, |
| }; |
| |
| /* STA + AP + P2PGO combination */ |
| static const struct ieee80211_iface_limit |
| wlan_hdd_sta_ap_p2pgo_iface_limit[] = { |
| /* Support for AP+P2PGO interfaces */ |
| { |
| .max = 2, |
| .types = BIT(NL80211_IFTYPE_STATION) |
| }, |
| { |
| .max = 1, |
| .types = BIT(NL80211_IFTYPE_P2P_GO) |
| }, |
| { |
| .max = 1, |
| .types = BIT(NL80211_IFTYPE_AP) |
| } |
| }; |
| |
| /* SAP + P2P combination */ |
| static const struct ieee80211_iface_limit |
| wlan_hdd_sap_p2p_iface_limit[] = { |
| { |
| /* 1 dedicated for p2p0 which is a STA type */ |
| .max = 1, |
| .types = BIT(NL80211_IFTYPE_STATION) |
| }, |
| { |
| /* The p2p interface in SAP+P2P can be GO/CLI. |
| * The p2p connection can be formed on p2p0 or p2p-p2p0-x. |
| */ |
| .max = 1, |
| .types = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT) |
| }, |
| { |
| /* SAP+GO to support only one SAP interface */ |
| .max = 1, |
| .types = BIT(NL80211_IFTYPE_AP) |
| } |
| }; |
| |
| /* P2P + P2P combination */ |
| static const struct ieee80211_iface_limit |
| wlan_hdd_p2p_p2p_iface_limit[] = { |
| { |
| /* 1 dedicated for p2p0 which is a STA type */ |
| .max = 1, |
| .types = BIT(NL80211_IFTYPE_STATION) |
| }, |
| { |
| /* The p2p interface in P2P+P2P can be GO/CLI. |
| * For P2P+P2P, the new interfaces are formed on p2p-p2p0-x. |
| */ |
| .max = 2, |
| .types = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT) |
| }, |
| }; |
| |
| static const struct ieee80211_iface_limit |
| wlan_hdd_mon_iface_limit[] = { |
| { |
| .max = 3, /* Monitor interface */ |
| .types = BIT(NL80211_IFTYPE_MONITOR), |
| }, |
| }; |
| |
| static struct ieee80211_iface_combination |
| wlan_hdd_iface_combination[] = { |
| /* STA */ |
| { |
| .limits = wlan_hdd_sta_iface_limit, |
| .num_different_channels = 2, |
| .max_interfaces = 3, |
| .n_limits = ARRAY_SIZE(wlan_hdd_sta_iface_limit), |
| }, |
| /* AP */ |
| { |
| .limits = wlan_hdd_ap_iface_limit, |
| .num_different_channels = 2, |
| .max_interfaces = (SAP_MAX_OBSS_STA_CNT + QDF_MAX_NO_OF_SAP_MODE), |
| .n_limits = ARRAY_SIZE(wlan_hdd_ap_iface_limit), |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) || \ |
| defined(CFG80211_BEACON_INTERVAL_BACKPORT) |
| .beacon_int_min_gcd = 1, |
| #endif |
| }, |
| /* P2P */ |
| { |
| .limits = wlan_hdd_p2p_iface_limit, |
| .num_different_channels = 2, |
| .max_interfaces = 2, |
| .n_limits = ARRAY_SIZE(wlan_hdd_p2p_iface_limit), |
| }, |
| /* STA + AP */ |
| { |
| .limits = wlan_hdd_sta_ap_iface_limit, |
| .num_different_channels = 2, |
| .max_interfaces = (1 + SAP_MAX_OBSS_STA_CNT + QDF_MAX_NO_OF_SAP_MODE), |
| .n_limits = ARRAY_SIZE(wlan_hdd_sta_ap_iface_limit), |
| .beacon_int_infra_match = true, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) || \ |
| defined(CFG80211_BEACON_INTERVAL_BACKPORT) |
| .beacon_int_min_gcd = 1, |
| #endif |
| }, |
| /* STA + P2P */ |
| { |
| .limits = wlan_hdd_sta_p2p_iface_limit, |
| .num_different_channels = 2, |
| /* one interface reserved for P2PDEV dedicated usage */ |
| .max_interfaces = 4, |
| .n_limits = ARRAY_SIZE(wlan_hdd_sta_p2p_iface_limit), |
| .beacon_int_infra_match = true, |
| }, |
| /* STA + P2P GO + SAP */ |
| { |
| .limits = wlan_hdd_sta_ap_p2pgo_iface_limit, |
| /* we can allow 3 channels for three different persona |
| * but due to firmware limitation, allow max 2 concrnt channels. |
| */ |
| .num_different_channels = 2, |
| /* one interface reserved for P2PDEV dedicated usage */ |
| .max_interfaces = 4, |
| .n_limits = ARRAY_SIZE(wlan_hdd_sta_ap_p2pgo_iface_limit), |
| .beacon_int_infra_match = true, |
| }, |
| /* SAP + P2P */ |
| { |
| .limits = wlan_hdd_sap_p2p_iface_limit, |
| .num_different_channels = 2, |
| /* 1-p2p0 + 1-SAP + 1-P2P (on p2p0 or p2p-p2p0-x) */ |
| .max_interfaces = 3, |
| .n_limits = ARRAY_SIZE(wlan_hdd_sap_p2p_iface_limit), |
| .beacon_int_infra_match = true, |
| }, |
| /* P2P + P2P */ |
| { |
| .limits = wlan_hdd_p2p_p2p_iface_limit, |
| .num_different_channels = 2, |
| /* 1-p2p0 + 2-P2P (on p2p-p2p0-x) */ |
| .max_interfaces = 3, |
| .n_limits = ARRAY_SIZE(wlan_hdd_p2p_p2p_iface_limit), |
| .beacon_int_infra_match = true, |
| }, |
| /* Monitor */ |
| { |
| .limits = wlan_hdd_mon_iface_limit, |
| .max_interfaces = 3, |
| .num_different_channels = 2, |
| .n_limits = ARRAY_SIZE(wlan_hdd_mon_iface_limit), |
| }, |
| }; |
| |
| static struct cfg80211_ops wlan_hdd_cfg80211_ops; |
| |
| #ifdef WLAN_NL80211_TESTMODE |
| enum wlan_hdd_tm_attr { |
| WLAN_HDD_TM_ATTR_INVALID = 0, |
| WLAN_HDD_TM_ATTR_CMD = 1, |
| WLAN_HDD_TM_ATTR_DATA = 2, |
| WLAN_HDD_TM_ATTR_STREAM_ID = 3, |
| WLAN_HDD_TM_ATTR_TYPE = 4, |
| /* keep last */ |
| WLAN_HDD_TM_ATTR_AFTER_LAST, |
| WLAN_HDD_TM_ATTR_MAX = WLAN_HDD_TM_ATTR_AFTER_LAST - 1, |
| }; |
| |
| enum wlan_hdd_tm_cmd { |
| WLAN_HDD_TM_CMD_WLAN_FTM = 0, |
| WLAN_HDD_TM_CMD_WLAN_HB = 1, |
| }; |
| |
| #define WLAN_HDD_TM_DATA_MAX_LEN 5000 |
| |
| static const struct nla_policy wlan_hdd_tm_policy[WLAN_HDD_TM_ATTR_MAX + 1] = { |
| [WLAN_HDD_TM_ATTR_CMD] = {.type = NLA_U32}, |
| [WLAN_HDD_TM_ATTR_DATA] = {.type = NLA_BINARY, |
| .len = WLAN_HDD_TM_DATA_MAX_LEN}, |
| }; |
| #endif /* WLAN_NL80211_TESTMODE */ |
| |
| enum wlan_hdd_vendor_ie_access_policy { |
| WLAN_HDD_VENDOR_IE_ACCESS_NONE = 0, |
| WLAN_HDD_VENDOR_IE_ACCESS_ALLOW_IF_LISTED, |
| }; |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) |
| static const struct wiphy_wowlan_support wowlan_support_cfg80211_init = { |
| .flags = WIPHY_WOWLAN_MAGIC_PKT, |
| .n_patterns = WOWL_MAX_PTRNS_ALLOWED, |
| .pattern_min_len = 1, |
| .pattern_max_len = WOWL_PTRN_MAX_SIZE, |
| }; |
| #endif |
| |
| /** |
| * hdd_add_channel_switch_support()- Adds Channel Switch flag if supported |
| * @flags: Pointer to the flags to Add channel switch flag. |
| * |
| * This Function adds Channel Switch support flag, if channel switch is |
| * supported by kernel. |
| * Return: void. |
| */ |
| #ifdef CHANNEL_SWITCH_SUPPORTED |
| static inline void hdd_add_channel_switch_support(uint32_t *flags) |
| { |
| *flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; |
| } |
| #else |
| static inline void hdd_add_channel_switch_support(uint32_t *flags) |
| { |
| } |
| #endif |
| |
| #ifdef FEATURE_WLAN_TDLS |
| |
| /* TDLS capabilities params */ |
| #define PARAM_MAX_TDLS_SESSION \ |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX_CONC_SESSIONS |
| #define PARAM_TDLS_FEATURE_SUPPORT \ |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_FEATURES_SUPPORTED |
| |
| /** |
| * __wlan_hdd_cfg80211_get_tdls_capabilities() - Provide TDLS Capabilities. |
| * @wiphy: WIPHY structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of the data received |
| * |
| * This function provides TDLS capabilities |
| * |
| * Return: 0 on success and errno on failure |
| */ |
| static int __wlan_hdd_cfg80211_get_tdls_capabilities(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int status; |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct sk_buff *skb; |
| uint32_t set = 0; |
| uint32_t max_num_tdls_sta = 0; |
| bool tdls_support; |
| bool tdls_external_control; |
| bool tdls_sleep_sta_enable; |
| bool tdls_buffer_sta; |
| bool tdls_off_channel; |
| |
| hdd_enter_dev(wdev->netdev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (status) |
| return status; |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, (2 * sizeof(u32)) + |
| NLMSG_HDRLEN); |
| if (!skb) { |
| hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| goto fail; |
| } |
| |
| if ((cfg_tdls_get_support_enable(hdd_ctx->psoc, &tdls_support) == |
| QDF_STATUS_SUCCESS) && !tdls_support) { |
| hdd_debug("TDLS feature not Enabled or Not supported in FW"); |
| if (nla_put_u32(skb, PARAM_MAX_TDLS_SESSION, 0) || |
| nla_put_u32(skb, PARAM_TDLS_FEATURE_SUPPORT, 0)) { |
| hdd_err("nla put fail"); |
| goto fail; |
| } |
| } else { |
| cfg_tdls_get_external_control(hdd_ctx->psoc, |
| &tdls_external_control); |
| cfg_tdls_get_sleep_sta_enable(hdd_ctx->psoc, |
| &tdls_sleep_sta_enable); |
| cfg_tdls_get_buffer_sta_enable(hdd_ctx->psoc, |
| &tdls_buffer_sta); |
| cfg_tdls_get_off_channel_enable(hdd_ctx->psoc, |
| &tdls_off_channel); |
| set = set | WIFI_TDLS_SUPPORT; |
| set = set | (tdls_external_control ? |
| WIFI_TDLS_EXTERNAL_CONTROL_SUPPORT : 0); |
| set = set | (tdls_off_channel ? |
| WIIF_TDLS_OFFCHANNEL_SUPPORT : 0); |
| max_num_tdls_sta = cfg_tdls_get_max_peer_count(hdd_ctx->psoc); |
| |
| hdd_debug("TDLS Feature supported value %x", set); |
| if (nla_put_u32(skb, PARAM_MAX_TDLS_SESSION, |
| max_num_tdls_sta) || |
| nla_put_u32(skb, PARAM_TDLS_FEATURE_SUPPORT, set)) { |
| hdd_err("nla put fail"); |
| goto fail; |
| } |
| } |
| return cfg80211_vendor_cmd_reply(skb); |
| fail: |
| if (skb) |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_get_tdls_capabilities() - Provide TDLS Capabilities. |
| * @wiphy: WIPHY structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of the data received |
| * |
| * This function provides TDLS capabilities |
| * |
| * Return: 0 on success and errno on failure |
| */ |
| static int |
| wlan_hdd_cfg80211_get_tdls_capabilities(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_tdls_capabilities(wiphy, wdev, |
| data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| static uint8_t hdd_get_bw_offset(uint32_t ch_width) |
| { |
| uint8_t bw_offset = 0; |
| |
| if (ch_width == CH_WIDTH_40MHZ) |
| bw_offset = 1 << BW_40_OFFSET_BIT; |
| else if (ch_width == CH_WIDTH_20MHZ) |
| bw_offset = 1 << BW_20_OFFSET_BIT; |
| |
| return bw_offset; |
| } |
| |
| #else /* !FEATURE_WLAN_TDLS */ |
| |
| static inline uint8_t hdd_get_bw_offset(uint32_t ch_width) |
| { |
| return 0; |
| } |
| |
| #endif /* FEATURE_WLAN_TDLS */ |
| |
| /** |
| * wlan_vendor_bitmap_to_reg_wifi_band_bitmap() - Convert vendor bitmap to |
| * reg_wifi_band bitmap |
| * @psoc: PSOC pointer |
| * @vendor_bitmap: vendor bitmap value coming via vendor command |
| * |
| * Return: reg_wifi_band bitmap |
| */ |
| static uint32_t |
| wlan_vendor_bitmap_to_reg_wifi_band_bitmap(struct wlan_objmgr_psoc *psoc, |
| uint32_t vendor_bitmap) |
| { |
| uint32_t reg_bitmap = 0; |
| |
| if (vendor_bitmap == QCA_SETBAND_AUTO) |
| reg_bitmap |= REG_BAND_MASK_ALL; |
| if (vendor_bitmap & QCA_SETBAND_2G) |
| reg_bitmap |= BIT(REG_BAND_2G); |
| if (vendor_bitmap & QCA_SETBAND_5G) |
| reg_bitmap |= BIT(REG_BAND_5G); |
| if (vendor_bitmap & QCA_SETBAND_6G) |
| reg_bitmap |= BIT(REG_BAND_6G); |
| |
| if (!wlan_reg_is_6ghz_supported(psoc)) { |
| hdd_debug("Driver doesn't support 6ghz"); |
| reg_bitmap = (reg_bitmap & (~BIT(REG_BAND_6G))); |
| } |
| |
| return reg_bitmap; |
| } |
| |
| int wlan_hdd_merge_avoid_freqs(struct ch_avoid_ind_type *destFreqList, |
| struct ch_avoid_ind_type *srcFreqList) |
| { |
| int i; |
| uint32_t room; |
| struct ch_avoid_freq_type *avoid_range = |
| &destFreqList->avoid_freq_range[destFreqList->ch_avoid_range_cnt]; |
| |
| room = CH_AVOID_MAX_RANGE - destFreqList->ch_avoid_range_cnt; |
| if (srcFreqList->ch_avoid_range_cnt > room) { |
| hdd_err("avoid freq overflow"); |
| return -EINVAL; |
| } |
| destFreqList->ch_avoid_range_cnt += srcFreqList->ch_avoid_range_cnt; |
| |
| for (i = 0; i < srcFreqList->ch_avoid_range_cnt; i++) { |
| avoid_range->start_freq = |
| srcFreqList->avoid_freq_range[i].start_freq; |
| avoid_range->end_freq = |
| srcFreqList->avoid_freq_range[i].end_freq; |
| avoid_range++; |
| } |
| return 0; |
| } |
| /* |
| * FUNCTION: wlan_hdd_send_avoid_freq_event |
| * This is called when wlan driver needs to send vendor specific |
| * avoid frequency range event to userspace |
| */ |
| int wlan_hdd_send_avoid_freq_event(struct hdd_context *hdd_ctx, |
| struct ch_avoid_ind_type *avoid_freq_list) |
| { |
| struct sk_buff *vendor_event; |
| |
| hdd_enter(); |
| |
| if (!hdd_ctx) { |
| hdd_err("HDD context is null"); |
| return -EINVAL; |
| } |
| |
| if (!avoid_freq_list) { |
| hdd_err("avoid_freq_list is null"); |
| return -EINVAL; |
| } |
| |
| vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, |
| NULL, sizeof(struct ch_avoid_ind_type), |
| QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX, |
| GFP_KERNEL); |
| |
| if (!vendor_event) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return -EINVAL; |
| } |
| |
| memcpy(skb_put(vendor_event, sizeof(struct ch_avoid_ind_type)), |
| (void *)avoid_freq_list, sizeof(struct ch_avoid_ind_type)); |
| |
| cfg80211_vendor_event(vendor_event, GFP_KERNEL); |
| |
| hdd_exit(); |
| return 0; |
| } |
| |
| /* |
| * define short names for the global vendor params |
| * used by QCA_NL80211_VENDOR_SUBCMD_HANG |
| */ |
| #define HANG_REASON_INDEX QCA_NL80211_VENDOR_SUBCMD_HANG_REASON_INDEX |
| |
| /** |
| * hdd_convert_hang_reason() - Convert cds recovery reason to vendor specific |
| * hang reason |
| * @reason: cds recovery reason |
| * |
| * Return: Vendor specific reason code |
| */ |
| static enum qca_wlan_vendor_hang_reason |
| hdd_convert_hang_reason(enum qdf_hang_reason reason) |
| { |
| u32 ret_val; |
| |
| switch (reason) { |
| case QDF_RX_HASH_NO_ENTRY_FOUND: |
| ret_val = QCA_WLAN_HANG_RX_HASH_NO_ENTRY_FOUND; |
| break; |
| case QDF_PEER_DELETION_TIMEDOUT: |
| ret_val = QCA_WLAN_HANG_PEER_DELETION_TIMEDOUT; |
| break; |
| case QDF_PEER_UNMAP_TIMEDOUT: |
| ret_val = QCA_WLAN_HANG_PEER_UNMAP_TIMEDOUT; |
| break; |
| case QDF_SCAN_REQ_EXPIRED: |
| ret_val = QCA_WLAN_HANG_SCAN_REQ_EXPIRED; |
| break; |
| case QDF_SCAN_ATTEMPT_FAILURES: |
| ret_val = QCA_WLAN_HANG_SCAN_ATTEMPT_FAILURES; |
| break; |
| case QDF_GET_MSG_BUFF_FAILURE: |
| ret_val = QCA_WLAN_HANG_GET_MSG_BUFF_FAILURE; |
| break; |
| case QDF_ACTIVE_LIST_TIMEOUT: |
| ret_val = QCA_WLAN_HANG_ACTIVE_LIST_TIMEOUT; |
| break; |
| case QDF_SUSPEND_TIMEOUT: |
| ret_val = QCA_WLAN_HANG_SUSPEND_TIMEOUT; |
| break; |
| case QDF_RESUME_TIMEOUT: |
| ret_val = QCA_WLAN_HANG_RESUME_TIMEOUT; |
| break; |
| case QDF_WMI_EXCEED_MAX_PENDING_CMDS: |
| ret_val = QCA_WLAN_HANG_WMI_EXCEED_MAX_PENDING_CMDS; |
| break; |
| case QDF_AP_STA_CONNECT_REQ_TIMEOUT: |
| ret_val = QCA_WLAN_HANG_AP_STA_CONNECT_REQ_TIMEOUT; |
| break; |
| case QDF_STA_AP_CONNECT_REQ_TIMEOUT: |
| ret_val = QCA_WLAN_HANG_STA_AP_CONNECT_REQ_TIMEOUT; |
| break; |
| case QDF_MAC_HW_MODE_CHANGE_TIMEOUT: |
| ret_val = QCA_WLAN_HANG_MAC_HW_MODE_CHANGE_TIMEOUT; |
| break; |
| case QDF_MAC_HW_MODE_CONFIG_TIMEOUT: |
| ret_val = QCA_WLAN_HANG_MAC_HW_MODE_CONFIG_TIMEOUT; |
| break; |
| case QDF_VDEV_START_RESPONSE_TIMED_OUT: |
| ret_val = QCA_WLAN_HANG_VDEV_START_RESPONSE_TIMED_OUT; |
| break; |
| case QDF_VDEV_RESTART_RESPONSE_TIMED_OUT: |
| ret_val = QCA_WLAN_HANG_VDEV_RESTART_RESPONSE_TIMED_OUT; |
| break; |
| case QDF_VDEV_STOP_RESPONSE_TIMED_OUT: |
| ret_val = QCA_WLAN_HANG_VDEV_STOP_RESPONSE_TIMED_OUT; |
| break; |
| case QDF_VDEV_DELETE_RESPONSE_TIMED_OUT: |
| ret_val = QCA_WLAN_HANG_VDEV_DELETE_RESPONSE_TIMED_OUT; |
| break; |
| case QDF_VDEV_PEER_DELETE_ALL_RESPONSE_TIMED_OUT: |
| ret_val = QCA_WLAN_HANG_VDEV_PEER_DELETE_ALL_RESPONSE_TIMED_OUT; |
| break; |
| case QDF_WMI_BUF_SEQUENCE_MISMATCH: |
| ret_val = QCA_WLAN_HANG_VDEV_PEER_DELETE_ALL_RESPONSE_TIMED_OUT; |
| break; |
| case QDF_HAL_REG_WRITE_FAILURE: |
| ret_val = QCA_WLAN_HANG_REG_WRITE_FAILURE; |
| break; |
| case QDF_SUSPEND_NO_CREDIT: |
| ret_val = QCA_WLAN_HANG_SUSPEND_NO_CREDIT; |
| break; |
| case QCA_HANG_BUS_FAILURE: |
| ret_val = QCA_WLAN_HANG_BUS_FAILURE; |
| break; |
| case QDF_TASKLET_CREDIT_LATENCY_DETECT: |
| ret_val = QCA_WLAN_HANG_TASKLET_CREDIT_LATENCY_DETECT; |
| break; |
| case QDF_RX_REG_PKT_ROUTE_ERR: |
| ret_val = QCA_WLAN_HANG_RX_MSDU_BUF_RCVD_IN_ERR_RING; |
| break; |
| case QDF_VDEV_SM_OUT_OF_SYNC: |
| ret_val = QCA_WLAN_HANG_VDEV_SM_OUT_OF_SYNC; |
| break; |
| case QDF_STATS_REQ_TIMEDOUT: |
| ret_val = QCA_WLAN_HANG_STATS_REQ_TIMEOUT; |
| break; |
| case QDF_TX_DESC_LEAK: |
| ret_val = QCA_WLAN_HANG_TX_DESC_LEAK; |
| break; |
| case QDF_SCHED_TIMEOUT: |
| ret_val = QCA_WLAN_HANG_SCHED_TIMEOUT; |
| break; |
| case QDF_SELF_PEER_DEL_FAILED: |
| ret_val = QCA_WLAN_HANG_SELF_PEER_DEL_FAIL; |
| break; |
| case QDF_DEL_SELF_STA_FAILED: |
| ret_val = QCA_WLAN_HANG_DEL_SELF_STA_FAIL; |
| break; |
| case QDF_FLUSH_LOGS: |
| ret_val = QCA_WLAN_HANG_FLUSH_LOGS; |
| break; |
| case QDF_HOST_WAKEUP_REASON_PAGEFAULT: |
| ret_val = QCA_WLAN_HANG_HOST_WAKEUP_REASON_PAGE_FAULT; |
| break; |
| case QDF_REASON_UNSPECIFIED: |
| default: |
| ret_val = QCA_WLAN_HANG_REASON_UNSPECIFIED; |
| break; |
| } |
| return ret_val; |
| } |
| |
| /** |
| * wlan_hdd_send_hang_reason_event() - Send hang reason to the userspace |
| * @hdd_ctx: Pointer to hdd context |
| * @reason: cds recovery reason |
| * @data: Hang Data |
| * |
| * Return: 0 on success or failure reason |
| */ |
| int wlan_hdd_send_hang_reason_event(struct hdd_context *hdd_ctx, |
| enum qdf_hang_reason reason, uint8_t *data, |
| size_t data_len) |
| { |
| struct sk_buff *vendor_event; |
| enum qca_wlan_vendor_hang_reason hang_reason; |
| struct hdd_adapter *sta_adapter; |
| struct wireless_dev *wdev = NULL; |
| |
| hdd_enter(); |
| |
| if (!hdd_ctx) { |
| hdd_err("HDD context is null"); |
| return -EINVAL; |
| } |
| |
| sta_adapter = hdd_get_adapter(hdd_ctx, QDF_STA_MODE); |
| if (sta_adapter) |
| wdev = &(sta_adapter->wdev); |
| |
| vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, |
| wdev, |
| sizeof(uint32_t) + data_len, |
| HANG_REASON_INDEX, |
| GFP_KERNEL); |
| if (!vendor_event) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return -ENOMEM; |
| } |
| |
| hang_reason = hdd_convert_hang_reason(reason); |
| |
| if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_HANG_REASON, |
| (uint32_t)hang_reason) || |
| nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_HANG_REASON_DATA, |
| data_len, data)) { |
| hdd_err("QCA_WLAN_VENDOR_ATTR_HANG_REASON put fail"); |
| kfree_skb(vendor_event); |
| return -EINVAL; |
| } |
| |
| cfg80211_vendor_event(vendor_event, GFP_KERNEL); |
| |
| hdd_exit(); |
| return 0; |
| } |
| |
| #undef HANG_REASON_INDEX |
| |
| /** |
| * wlan_hdd_get_adjacent_chan_freq(): Gets next/previous channel |
| * with respect to the channel passed. |
| * @freq: Channel frequency |
| * @upper: If "true" then next channel is returned or else |
| * previous channel is returned. |
| * |
| * This function returns the next/previous adjacent-channel to |
| * the channel passed. If "upper = true" then next channel is |
| * returned else previous is returned. |
| */ |
| static qdf_freq_t wlan_hdd_get_adjacent_chan_freq(qdf_freq_t freq, bool upper) |
| { |
| enum channel_enum ch_idx = wlan_reg_get_chan_enum_for_freq(freq); |
| |
| if (ch_idx == INVALID_CHANNEL) |
| return -EINVAL; |
| |
| if (upper && (ch_idx < (NUM_CHANNELS - 1))) |
| ch_idx++; |
| else if (!upper && (ch_idx > CHAN_ENUM_2412)) |
| ch_idx--; |
| else |
| return -EINVAL; |
| |
| return WLAN_REG_CH_TO_FREQ(ch_idx); |
| } |
| |
| /** |
| * wlan_hdd_send_avoid_freq_for_dnbs(): Sends list of frequencies to be |
| * avoided when Do_Not_Break_Stream is active. |
| * @hdd_ctx: HDD Context |
| * @op_freq: AP/P2P-GO operating channel frequency |
| * |
| * This function sends list of frequencies to be avoided when |
| * Do_Not_Break_Stream is active. |
| * To clear the avoid_frequency_list in the application, |
| * op_freq = 0 can be passed. |
| * |
| * Return: 0 on success and errno on failure |
| */ |
| int wlan_hdd_send_avoid_freq_for_dnbs(struct hdd_context *hdd_ctx, |
| qdf_freq_t op_freq) |
| { |
| struct ch_avoid_ind_type p2p_avoid_freq_list; |
| qdf_freq_t min_freq, max_freq; |
| int ret; |
| qdf_freq_t freq; |
| |
| hdd_enter(); |
| |
| if (!hdd_ctx) { |
| hdd_err("invalid param"); |
| return -EINVAL; |
| } |
| |
| qdf_mem_zero(&p2p_avoid_freq_list, sizeof(struct ch_avoid_ind_type)); |
| /* |
| * If channel passed is zero, clear the avoid_freq list in application. |
| */ |
| if (!op_freq) { |
| #ifdef FEATURE_WLAN_CH_AVOID |
| mutex_lock(&hdd_ctx->avoid_freq_lock); |
| qdf_mem_zero(&hdd_ctx->dnbs_avoid_freq_list, |
| sizeof(struct ch_avoid_ind_type)); |
| if (hdd_ctx->coex_avoid_freq_list.ch_avoid_range_cnt) |
| memcpy(&p2p_avoid_freq_list, |
| &hdd_ctx->coex_avoid_freq_list, |
| sizeof(struct ch_avoid_ind_type)); |
| mutex_unlock(&hdd_ctx->avoid_freq_lock); |
| #endif |
| ret = wlan_hdd_send_avoid_freq_event(hdd_ctx, |
| &p2p_avoid_freq_list); |
| if (ret) |
| hdd_err("wlan_hdd_send_avoid_freq_event error:%d", |
| ret); |
| |
| return ret; |
| } |
| |
| if (WLAN_REG_IS_24GHZ_CH_FREQ(op_freq)) { |
| min_freq = WLAN_REG_MIN_24GHZ_CHAN_FREQ; |
| max_freq = WLAN_REG_MAX_24GHZ_CHAN_FREQ; |
| } else if (WLAN_REG_IS_5GHZ_CH_FREQ(op_freq)) { |
| min_freq = WLAN_REG_MIN_5GHZ_CHAN_FREQ; |
| max_freq = WLAN_REG_MAX_5GHZ_CHAN_FREQ; |
| } else { |
| hdd_err("invalid channel freq:%d", op_freq); |
| return -EINVAL; |
| } |
| |
| if (op_freq > min_freq && op_freq < max_freq) { |
| p2p_avoid_freq_list.ch_avoid_range_cnt = 2; |
| p2p_avoid_freq_list.avoid_freq_range[0].start_freq = min_freq; |
| |
| /* Get channel before the op_freq */ |
| freq = wlan_hdd_get_adjacent_chan_freq(op_freq, false); |
| if (freq < 0) |
| return -EINVAL; |
| p2p_avoid_freq_list.avoid_freq_range[0].end_freq = freq; |
| |
| /* Get channel next to the op_freq */ |
| freq = wlan_hdd_get_adjacent_chan_freq(op_freq, true); |
| if (freq < 0) |
| return -EINVAL; |
| p2p_avoid_freq_list.avoid_freq_range[1].start_freq = freq; |
| |
| p2p_avoid_freq_list.avoid_freq_range[1].end_freq = max_freq; |
| } else if (op_freq == min_freq) { |
| p2p_avoid_freq_list.ch_avoid_range_cnt = 1; |
| |
| freq = wlan_hdd_get_adjacent_chan_freq(op_freq, true); |
| if (freq < 0) |
| return -EINVAL; |
| p2p_avoid_freq_list.avoid_freq_range[0].start_freq = freq; |
| |
| p2p_avoid_freq_list.avoid_freq_range[0].end_freq = max_freq; |
| } else { |
| p2p_avoid_freq_list.ch_avoid_range_cnt = 1; |
| p2p_avoid_freq_list.avoid_freq_range[0].start_freq = min_freq; |
| |
| freq = wlan_hdd_get_adjacent_chan_freq(op_freq, false); |
| if (freq < 0) |
| return -EINVAL; |
| p2p_avoid_freq_list.avoid_freq_range[0].end_freq = freq; |
| } |
| #ifdef FEATURE_WLAN_CH_AVOID |
| mutex_lock(&hdd_ctx->avoid_freq_lock); |
| hdd_ctx->dnbs_avoid_freq_list = p2p_avoid_freq_list; |
| if (hdd_ctx->coex_avoid_freq_list.ch_avoid_range_cnt) { |
| ret = wlan_hdd_merge_avoid_freqs(&p2p_avoid_freq_list, |
| &hdd_ctx->coex_avoid_freq_list); |
| if (ret) { |
| mutex_unlock(&hdd_ctx->avoid_freq_lock); |
| hdd_err("avoid freq merge failed"); |
| return ret; |
| } |
| } |
| mutex_unlock(&hdd_ctx->avoid_freq_lock); |
| #endif |
| ret = wlan_hdd_send_avoid_freq_event(hdd_ctx, &p2p_avoid_freq_list); |
| if (ret) |
| hdd_err("wlan_hdd_send_avoid_freq_event error:%d", ret); |
| |
| return ret; |
| } |
| |
| /* vendor specific events */ |
| static const struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] = { |
| [QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY |
| }, |
| |
| [QCA_NL80211_VENDOR_SUBCMD_NAN_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_NAN |
| }, |
| |
| #ifdef WLAN_FEATURE_STATS_EXT |
| [QCA_NL80211_VENDOR_SUBCMD_STATS_EXT_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_STATS_EXT |
| }, |
| #endif /* WLAN_FEATURE_STATS_EXT */ |
| #ifdef FEATURE_WLAN_EXTSCAN |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_START_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_START |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_STOP_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_STOP |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CAPABILITIES_INDEX] = { |
| . |
| vendor_id |
| = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CAPABILITIES |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CACHED_RESULTS_INDEX] = { |
| . |
| vendor_id |
| = |
| QCA_NL80211_VENDOR_ID, |
| . |
| subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CACHED_RESULTS |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_RESULTS_AVAILABLE_INDEX] = { |
| . |
| vendor_id |
| = |
| QCA_NL80211_VENDOR_ID, |
| . |
| subcmd |
| = |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_RESULTS_AVAILABLE |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_FULL_SCAN_RESULT_INDEX] = { |
| . |
| vendor_id |
| = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_FULL_SCAN_RESULT |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_EVENT_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_EVENT |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_FOUND_INDEX] = { |
| . |
| vendor_id |
| = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_FOUND |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_BSSID_HOTLIST_INDEX] = { |
| . |
| vendor_id |
| = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_BSSID_HOTLIST |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_BSSID_HOTLIST_INDEX] = { |
| . |
| vendor_id |
| = |
| QCA_NL80211_VENDOR_ID, |
| . |
| subcmd |
| = |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_BSSID_HOTLIST |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SIGNIFICANT_CHANGE_INDEX] = { |
| . |
| vendor_id |
| = |
| QCA_NL80211_VENDOR_ID, |
| . |
| subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SIGNIFICANT_CHANGE |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_SIGNIFICANT_CHANGE_INDEX] = { |
| . |
| vendor_id |
| = |
| QCA_NL80211_VENDOR_ID, |
| . |
| subcmd |
| = |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_SIGNIFICANT_CHANGE |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_SIGNIFICANT_CHANGE_INDEX] = { |
| . |
| vendor_id |
| = |
| QCA_NL80211_VENDOR_ID, |
| . |
| subcmd |
| = |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_SIGNIFICANT_CHANGE |
| }, |
| #endif /* FEATURE_WLAN_EXTSCAN */ |
| |
| #ifdef WLAN_FEATURE_LINK_LAYER_STATS |
| [QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_LL_RADIO_STATS_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_LL_STATS_RADIO_RESULTS |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_LL_IFACE_STATS_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_LL_STATS_IFACE_RESULTS |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_LL_PEER_INFO_STATS_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_LL_STATS_PEERS_RESULTS |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT |
| }, |
| #endif /* WLAN_FEATURE_LINK_LAYER_STATS */ |
| [QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE_CHANGE_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_DO_ACS_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_DO_ACS |
| }, |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| [QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH |
| }, |
| #endif |
| [QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED_INDEX] = { |
| .vendor_id = |
| QCA_NL80211_VENDOR_ID, |
| .subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED |
| }, |
| #ifdef FEATURE_WLAN_EXTSCAN |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_NETWORK_FOUND_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_NETWORK_FOUND |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_PASSPOINT_NETWORK_FOUND_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_PASSPOINT_NETWORK_FOUND |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_LOST_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_LOST |
| }, |
| #endif /* FEATURE_WLAN_EXTSCAN */ |
| |
| FEATURE_RSSI_MONITOR_VENDOR_EVENTS |
| |
| #ifdef WLAN_FEATURE_TSF |
| [QCA_NL80211_VENDOR_SUBCMD_TSF_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_TSF |
| }, |
| #endif |
| [QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_SCAN_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN |
| }, |
| /* OCB events */ |
| [QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT |
| }, |
| #ifdef FEATURE_LFR_SUBNET_DETECTION |
| [QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG |
| }, |
| #endif /*FEATURE_LFR_SUBNET_DETECTION */ |
| |
| FEATURE_INTEROP_ISSUES_AP_VENDOR_COMMANDS_INDEX |
| |
| [QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_NDP |
| }, |
| |
| [QCA_NL80211_VENDOR_SUBCMD_P2P_LO_EVENT_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_UPDATE_EXTERNAL_ACS_CONFIG] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_PWR_SAVE_FAIL_DETECTED_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET, |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_HANG_REASON_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_HANG, |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO, |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT, |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_NAN_EXT_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_NAN_EXT |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES, |
| }, |
| |
| BCN_RECV_FEATURE_VENDOR_EVENTS |
| FEATURE_MEDIUM_ASSESS_VENDOR_EVENTS |
| [QCA_NL80211_VENDOR_SUBCMD_ROAM_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_ROAM, |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_OEM_DATA_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_OEM_DATA, |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_REQUEST_SAR_LIMITS_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS_EVENT, |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO, |
| }, |
| FEATURE_THERMAL_VENDOR_EVENTS |
| #ifdef WLAN_SUPPORT_TWT |
| FEATURE_TWT_VENDOR_EVENTS |
| #endif |
| FEATURE_CFR_DATA_VENDOR_EVENTS |
| #ifdef WLAN_FEATURE_CONNECTIVITY_LOGGING |
| FEATURE_CONNECTIVITY_LOGGING_EVENT |
| #endif |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| [QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS, |
| }, |
| #endif |
| }; |
| |
| /** |
| * __is_driver_dfs_capable() - get driver DFS capability |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the data to be passed via vendor interface |
| * @data_len:Length of the data to be passed |
| * |
| * This function is called by userspace to indicate whether or not |
| * the driver supports DFS offload. |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int __is_driver_dfs_capable(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| u32 dfs_capability = 0; |
| struct sk_buff *temp_skbuff; |
| int ret_val; |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| |
| hdd_enter_dev(wdev->netdev); |
| |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| return ret_val; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) || \ |
| defined(CFG80211_DFS_OFFLOAD_BACKPORT) |
| dfs_capability = |
| wiphy_ext_feature_isset(wiphy, |
| NL80211_EXT_FEATURE_DFS_OFFLOAD); |
| #else |
| dfs_capability = !!(wiphy->flags & WIPHY_FLAG_DFS_OFFLOAD); |
| #endif |
| |
| temp_skbuff = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) + |
| NLMSG_HDRLEN); |
| |
| if (temp_skbuff) { |
| ret_val = nla_put_u32(temp_skbuff, QCA_WLAN_VENDOR_ATTR_DFS, |
| dfs_capability); |
| if (ret_val) { |
| hdd_err("QCA_WLAN_VENDOR_ATTR_DFS put fail"); |
| kfree_skb(temp_skbuff); |
| |
| return ret_val; |
| } |
| |
| return cfg80211_vendor_cmd_reply(temp_skbuff); |
| } |
| |
| hdd_err("dfs capability: buffer alloc fail"); |
| return -ENOMEM; |
| } |
| |
| /** |
| * is_driver_dfs_capable() - get driver DFS capability |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the data to be passed via vendor interface |
| * @data_len:Length of the data to be passed |
| * |
| * This function is called by userspace to indicate whether or not |
| * the driver supports DFS offload. This is an SSR-protected |
| * wrapper function. |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int is_driver_dfs_capable(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __is_driver_dfs_capable(wiphy, wdev, data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * wlan_hdd_sap_cfg_dfs_override() - DFS MCC restriction check |
| * |
| * @adapter: SAP adapter pointer |
| * |
| * DFS in MCC is not supported for Multi bssid SAP mode due to single physical |
| * radio. So in case of DFS MCC scenario override current SAP given config |
| * to follow concurrent SAP DFS config |
| * |
| * Return: 0 - No DFS issue, 1 - Override done and negative error codes |
| */ |
| int wlan_hdd_sap_cfg_dfs_override(struct hdd_adapter *adapter) |
| { |
| struct hdd_adapter *con_sap_adapter; |
| struct sap_config *sap_config, *con_sap_config; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint32_t con_ch_freq; |
| |
| if (!hdd_ctx) { |
| hdd_err("hdd context is NULL"); |
| return 0; |
| } |
| |
| /* |
| * Check if AP+AP case, once primary AP chooses a DFS |
| * channel secondary AP should always follow primary APs channel |
| */ |
| if (!policy_mgr_concurrent_beaconing_sessions_running( |
| hdd_ctx->psoc)) |
| return 0; |
| |
| con_sap_adapter = hdd_get_con_sap_adapter(adapter, true); |
| if (!con_sap_adapter) |
| return 0; |
| |
| sap_config = &adapter->session.ap.sap_config; |
| con_sap_config = &con_sap_adapter->session.ap.sap_config; |
| con_ch_freq = con_sap_adapter->session.ap.operating_chan_freq; |
| |
| if (!wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, con_ch_freq)) |
| return 0; |
| |
| hdd_debug("Only SCC AP-AP DFS Permitted (ch_freq=%d, con_ch_freq=%d)", |
| sap_config->chan_freq, con_ch_freq); |
| hdd_debug("Overriding guest AP's channel"); |
| sap_config->chan_freq = con_ch_freq; |
| |
| if (con_sap_config->acs_cfg.acs_mode == true) { |
| if (con_ch_freq != con_sap_config->acs_cfg.pri_ch_freq && |
| con_ch_freq != con_sap_config->acs_cfg.ht_sec_ch_freq) { |
| hdd_err("Primary AP channel config error"); |
| hdd_err("Operating ch: %d ACS ch freq: %d Sec Freq %d", |
| con_ch_freq, |
| con_sap_config->acs_cfg.pri_ch_freq, |
| con_sap_config->acs_cfg.ht_sec_ch_freq); |
| return -EINVAL; |
| } |
| /* Sec AP ACS info is overwritten with Pri AP due to DFS |
| * MCC restriction. So free ch list allocated in do_acs |
| * func for Sec AP and realloc for Pri AP ch list size |
| */ |
| if (sap_config->acs_cfg.freq_list) { |
| qdf_mem_free(sap_config->acs_cfg.freq_list); |
| sap_config->acs_cfg.freq_list = NULL; |
| } |
| if (sap_config->acs_cfg.master_freq_list) { |
| qdf_mem_free(sap_config->acs_cfg.master_freq_list); |
| sap_config->acs_cfg.master_freq_list = NULL; |
| } |
| |
| qdf_mem_copy(&sap_config->acs_cfg, |
| &con_sap_config->acs_cfg, |
| sizeof(struct sap_acs_cfg)); |
| |
| sap_config->acs_cfg.freq_list = |
| qdf_mem_malloc(sizeof(uint32_t) * |
| con_sap_config->acs_cfg.ch_list_count); |
| if (!sap_config->acs_cfg.freq_list) { |
| sap_config->acs_cfg.ch_list_count = 0; |
| return -ENOMEM; |
| } |
| qdf_mem_copy(sap_config->acs_cfg.freq_list, |
| con_sap_config->acs_cfg.freq_list, |
| con_sap_config->acs_cfg.ch_list_count * |
| sizeof(uint32_t)); |
| |
| sap_config->acs_cfg.master_freq_list = |
| qdf_mem_malloc(sizeof(uint32_t) * |
| con_sap_config->acs_cfg.master_ch_list_count); |
| if (!sap_config->acs_cfg.master_freq_list) { |
| sap_config->acs_cfg.master_ch_list_count = 0; |
| qdf_mem_free(sap_config->acs_cfg.freq_list); |
| sap_config->acs_cfg.freq_list = NULL; |
| return -ENOMEM; |
| } |
| qdf_mem_copy(sap_config->acs_cfg.master_freq_list, |
| con_sap_config->acs_cfg.master_freq_list, |
| con_sap_config->acs_cfg.master_ch_list_count * |
| sizeof(uint32_t)); |
| } else { |
| sap_config->acs_cfg.pri_ch_freq = con_ch_freq; |
| if (sap_config->acs_cfg.ch_width > eHT_CHANNEL_WIDTH_20MHZ) |
| sap_config->acs_cfg.ht_sec_ch_freq = |
| con_sap_config->sec_ch_freq; |
| } |
| |
| return con_ch_freq; |
| } |
| |
| /** |
| * wlan_hdd_set_acs_ch_range : Populate ACS hw mode and channel range values |
| * @sap_cfg: pointer to SAP config struct |
| * @hw_mode: hw mode retrieved from vendor command buffer |
| * @ht_enabled: whether HT phy mode is enabled |
| * @vht_enabled: whether VHT phy mode is enabled |
| * |
| * This function populates the ACS hw mode based on the configuration retrieved |
| * from the vendor command buffer; and sets ACS start and end channel for the |
| * given band. |
| * |
| * Return: 0 if success; -EINVAL if ACS channel list is NULL |
| */ |
| static int wlan_hdd_set_acs_ch_range( |
| struct sap_config *sap_cfg, enum qca_wlan_vendor_acs_hw_mode hw_mode, |
| bool ht_enabled, bool vht_enabled) |
| { |
| int i; |
| |
| if (hw_mode == QCA_ACS_MODE_IEEE80211B) { |
| sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11b; |
| sap_cfg->acs_cfg.start_ch_freq = |
| wlan_reg_ch_to_freq(CHAN_ENUM_2412); |
| sap_cfg->acs_cfg.end_ch_freq = |
| wlan_reg_ch_to_freq(CHAN_ENUM_2484); |
| } else if (hw_mode == QCA_ACS_MODE_IEEE80211G) { |
| sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11g; |
| sap_cfg->acs_cfg.start_ch_freq = |
| wlan_reg_ch_to_freq(CHAN_ENUM_2412); |
| sap_cfg->acs_cfg.end_ch_freq = |
| wlan_reg_ch_to_freq(CHAN_ENUM_2472); |
| } else if (hw_mode == QCA_ACS_MODE_IEEE80211A) { |
| sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11a; |
| sap_cfg->acs_cfg.start_ch_freq = |
| wlan_reg_ch_to_freq(CHAN_ENUM_5180); |
| sap_cfg->acs_cfg.end_ch_freq = |
| wlan_reg_ch_to_freq(CHAN_ENUM_5885); |
| } else if (hw_mode == QCA_ACS_MODE_IEEE80211ANY) { |
| sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_abg; |
| sap_cfg->acs_cfg.start_ch_freq = |
| wlan_reg_ch_to_freq(CHAN_ENUM_2412); |
| sap_cfg->acs_cfg.end_ch_freq = |
| wlan_reg_ch_to_freq(CHAN_ENUM_5885); |
| } |
| |
| if (ht_enabled) |
| sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11n; |
| |
| if (vht_enabled) |
| sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11ac; |
| |
| /* Parse ACS Chan list from hostapd */ |
| if (!sap_cfg->acs_cfg.freq_list) |
| return -EINVAL; |
| |
| sap_cfg->acs_cfg.start_ch_freq = sap_cfg->acs_cfg.freq_list[0]; |
| sap_cfg->acs_cfg.end_ch_freq = |
| sap_cfg->acs_cfg.freq_list[sap_cfg->acs_cfg.ch_list_count - 1]; |
| for (i = 0; i < sap_cfg->acs_cfg.ch_list_count; i++) { |
| /* avoid channel as start channel */ |
| if (sap_cfg->acs_cfg.start_ch_freq > |
| sap_cfg->acs_cfg.freq_list[i] && |
| sap_cfg->acs_cfg.freq_list[i] != 0) |
| sap_cfg->acs_cfg.start_ch_freq = |
| sap_cfg->acs_cfg.freq_list[i]; |
| if (sap_cfg->acs_cfg.end_ch_freq < |
| sap_cfg->acs_cfg.freq_list[i]) |
| sap_cfg->acs_cfg.end_ch_freq = |
| sap_cfg->acs_cfg.freq_list[i]; |
| } |
| |
| return 0; |
| } |
| |
| static void hdd_update_acs_channel_list(struct sap_config *sap_config, |
| enum band_info band) |
| { |
| int i, temp_count = 0; |
| int acs_list_count = sap_config->acs_cfg.ch_list_count; |
| |
| for (i = 0; i < acs_list_count; i++) { |
| if (BAND_2G == band) { |
| if (WLAN_REG_IS_24GHZ_CH_FREQ( |
| sap_config->acs_cfg.freq_list[i])) { |
| sap_config->acs_cfg.freq_list[temp_count] = |
| sap_config->acs_cfg.freq_list[i]; |
| temp_count++; |
| } |
| } else if (BAND_5G == band) { |
| if (WLAN_REG_IS_5GHZ_CH_FREQ( |
| sap_config->acs_cfg.freq_list[i]) || |
| WLAN_REG_IS_6GHZ_CHAN_FREQ( |
| sap_config->acs_cfg.freq_list[i])) { |
| sap_config->acs_cfg.freq_list[temp_count] = |
| sap_config->acs_cfg.freq_list[i]; |
| temp_count++; |
| } |
| } |
| } |
| sap_config->acs_cfg.ch_list_count = temp_count; |
| } |
| |
| |
| /** |
| * wlan_hdd_cfg80211_start_acs : Start ACS Procedure for SAP |
| * @adapter: pointer to SAP adapter struct |
| * |
| * This function starts the ACS procedure if there are no |
| * constraints like MBSSID DFS restrictions. |
| * |
| * Return: Status of ACS Start procedure |
| */ |
| int wlan_hdd_cfg80211_start_acs(struct hdd_adapter *adapter) |
| { |
| struct hdd_context *hdd_ctx; |
| struct sap_config *sap_config; |
| sap_event_cb acs_event_callback; |
| uint8_t mcc_to_scc_switch = 0; |
| int status; |
| QDF_STATUS qdf_status; |
| |
| if (!adapter) { |
| hdd_err("adapter is NULL"); |
| return -EINVAL; |
| } |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return status; |
| |
| sap_config = &adapter->session.ap.sap_config; |
| if (!sap_config) { |
| hdd_err("SAP config is NULL"); |
| return -EINVAL; |
| } |
| if (hdd_ctx->acs_policy.acs_chan_freq) |
| sap_config->chan_freq = hdd_ctx->acs_policy.acs_chan_freq; |
| else |
| sap_config->chan_freq = AUTO_CHANNEL_SELECT; |
| ucfg_policy_mgr_get_mcc_scc_switch(hdd_ctx->psoc, |
| &mcc_to_scc_switch); |
| /* |
| * No DFS SCC is allowed in Auto use case. Hence not |
| * calling DFS override |
| */ |
| if (QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION != |
| mcc_to_scc_switch && |
| !(policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc) && |
| WLAN_REG_IS_24GHZ_CH_FREQ(sap_config->acs_cfg.end_ch_freq)) && |
| !wlansap_dcs_is_wlan_interference_mitigation_enabled( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter))) { |
| status = wlan_hdd_sap_cfg_dfs_override(adapter); |
| if (status < 0) |
| return status; |
| |
| if (status > 0) { |
| /*notify hostapd about channel override */ |
| wlan_hdd_cfg80211_acs_ch_select_evt(adapter); |
| wlansap_dcs_set_wlan_interference_mitigation_on_band( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| sap_config); |
| return 0; |
| } |
| } |
| /* When first 2 connections are on the same frequency band, |
| * then PCL would include only channels from the other |
| * frequency band on which no connections are active |
| */ |
| if ((policy_mgr_get_connection_count(hdd_ctx->psoc) == 2) && |
| (sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211ANY)) { |
| struct policy_mgr_conc_connection_info *conc_connection_info; |
| uint32_t i; |
| |
| conc_connection_info = policy_mgr_get_conn_info(&i); |
| if (policy_mgr_are_2_freq_on_same_mac(hdd_ctx->psoc, |
| conc_connection_info[0].freq, |
| conc_connection_info[1].freq)) { |
| if (!WLAN_REG_IS_24GHZ_CH_FREQ( |
| sap_config->acs_cfg.pcl_chan_freq[0])) { |
| sap_config->acs_cfg.band = |
| QCA_ACS_MODE_IEEE80211A; |
| hdd_update_acs_channel_list(sap_config, |
| BAND_5G); |
| } else { |
| sap_config->acs_cfg.band = |
| QCA_ACS_MODE_IEEE80211G; |
| hdd_update_acs_channel_list(sap_config, |
| BAND_2G); |
| } |
| } |
| } |
| status = wlan_hdd_config_acs(hdd_ctx, adapter); |
| if (status) { |
| hdd_err("ACS config failed"); |
| return -EINVAL; |
| } |
| |
| acs_event_callback = hdd_hostapd_sap_event_cb; |
| |
| qdf_mem_copy(sap_config->self_macaddr.bytes, |
| adapter->mac_addr.bytes, sizeof(struct qdf_mac_addr)); |
| |
| qdf_status = wlansap_acs_chselect(WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| acs_event_callback, |
| sap_config, adapter->dev); |
| |
| if (QDF_IS_STATUS_ERROR(qdf_status)) { |
| hdd_err("ACS channel select failed"); |
| return -EINVAL; |
| } |
| if (sap_is_auto_channel_select(WLAN_HDD_GET_SAP_CTX_PTR(adapter))) |
| sap_config->acs_cfg.acs_mode = true; |
| |
| qdf_atomic_set(&adapter->session.ap.acs_in_progress, 1); |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_update_vendor_pcl_list() - This API will return unsorted pcl list |
| * @hdd_ctx: hdd context |
| * @acs_chan_params: external acs channel params |
| * @sap_config: SAP config |
| * |
| * This API provides unsorted pcl list. |
| * this list is a subset of the valid channel list given by hostapd. |
| * if channel is not present in pcl, weightage will be given as zero |
| * |
| * Return: Zero on success, non-zero on failure |
| */ |
| static void hdd_update_vendor_pcl_list(struct hdd_context *hdd_ctx, |
| struct hdd_vendor_acs_chan_params *acs_chan_params, |
| struct sap_config *sap_config) |
| { |
| int i, j; |
| /* |
| * PCL shall contain only the preferred channels from the |
| * application. If those channels are not present in the |
| * driver PCL, then set the weight to zero |
| */ |
| for (i = 0; i < sap_config->acs_cfg.ch_list_count; i++) { |
| acs_chan_params->vendor_pcl_list[i] = |
| sap_config->acs_cfg.freq_list[i]; |
| acs_chan_params->vendor_weight_list[i] = 0; |
| for (j = 0; j < sap_config->acs_cfg.pcl_ch_count; j++) { |
| if (sap_config->acs_cfg.freq_list[i] == |
| sap_config->acs_cfg.pcl_chan_freq[j]) { |
| acs_chan_params->vendor_weight_list[i] = |
| sap_config->acs_cfg.pcl_channels_weight_list[j]; |
| break; |
| } |
| } |
| } |
| acs_chan_params->pcl_count = sap_config->acs_cfg.ch_list_count; |
| } |
| |
| /** |
| * hdd_update_reg_chan_info : This API contructs channel info |
| * for all the given channel |
| * @adapter: pointer to SAP adapter struct |
| * @channel_count: channel count |
| * @freq_list: channel frequency (MHz) list |
| * |
| * Return: Status of of channel information updation |
| */ |
| static int |
| hdd_update_reg_chan_info(struct hdd_adapter *adapter, |
| uint32_t channel_count, uint32_t *freq_list) |
| { |
| int i; |
| struct hdd_channel_info *icv; |
| struct ch_params ch_params = {0}; |
| uint8_t bw_offset = 0, chan = 0; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| struct sap_config *sap_config = &adapter->session.ap.sap_config; |
| mac_handle_t mac_handle; |
| uint8_t sub_20_chan_width = 0; |
| QDF_STATUS status; |
| |
| mac_handle = hdd_ctx->mac_handle; |
| sap_config->channel_info_count = channel_count; |
| |
| status = ucfg_mlme_get_sub_20_chan_width(hdd_ctx->psoc, |
| &sub_20_chan_width); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("Failed to get sub_20_chan_width config"); |
| |
| for (i = 0; i < channel_count; i++) { |
| icv = &sap_config->channel_info[i]; |
| chan = wlan_reg_freq_to_chan(hdd_ctx->pdev, |
| freq_list[i]); |
| if (chan == 0) |
| continue; |
| |
| icv->freq = freq_list[i]; |
| icv->ieee_chan_number = chan; |
| icv->max_reg_power = wlan_reg_get_channel_reg_power_for_freq( |
| hdd_ctx->pdev, freq_list[i]); |
| |
| /* filling demo values */ |
| icv->max_radio_power = HDD_MAX_TX_POWER; |
| icv->min_radio_power = HDD_MIN_TX_POWER; |
| /* not supported in current driver */ |
| icv->max_antenna_gain = 0; |
| |
| bw_offset = hdd_get_bw_offset(sap_config->acs_cfg.ch_width); |
| icv->reg_class_id = |
| wlan_hdd_find_opclass(mac_handle, chan, bw_offset); |
| |
| if (WLAN_REG_IS_5GHZ_CH_FREQ(freq_list[i])) { |
| ch_params.ch_width = sap_config->acs_cfg.ch_width; |
| wlan_reg_set_channel_params_for_freq(hdd_ctx->pdev, |
| icv->freq, |
| 0, &ch_params); |
| icv->vht_center_freq_seg0 = ch_params.center_freq_seg0; |
| icv->vht_center_freq_seg1 = ch_params.center_freq_seg1; |
| } |
| |
| icv->flags = 0; |
| icv->flags = cds_get_vendor_reg_flags(hdd_ctx->pdev, |
| icv->freq, |
| sap_config->acs_cfg.ch_width, |
| sap_config->acs_cfg.is_ht_enabled, |
| sap_config->acs_cfg.is_vht_enabled, |
| sub_20_chan_width); |
| if (icv->flags & IEEE80211_CHAN_PASSIVE) |
| icv->flagext |= IEEE80211_CHAN_DFS; |
| |
| hdd_debug("freq %d flags %d flagext %d ieee %d maxreg %d maxpw %d minpw %d regClass %d antenna %d seg0 %d seg1 %d", |
| icv->freq, icv->flags, |
| icv->flagext, icv->ieee_chan_number, |
| icv->max_reg_power, icv->max_radio_power, |
| icv->min_radio_power, icv->reg_class_id, |
| icv->max_antenna_gain, icv->vht_center_freq_seg0, |
| icv->vht_center_freq_seg1); |
| } |
| return 0; |
| } |
| |
| /* Short name for QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO event */ |
| #define CHAN_INFO_ATTR_FLAGS \ |
| QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS |
| #define CHAN_INFO_ATTR_FLAG_EXT \ |
| QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT |
| #define CHAN_INFO_ATTR_FREQ \ |
| QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ |
| #define CHAN_INFO_ATTR_MAX_REG_POWER \ |
| QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_REG_POWER |
| #define CHAN_INFO_ATTR_MAX_POWER \ |
| QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_POWER |
| #define CHAN_INFO_ATTR_MIN_POWER \ |
| QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MIN_POWER |
| #define CHAN_INFO_ATTR_REG_CLASS_ID \ |
| QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_REG_CLASS_ID |
| #define CHAN_INFO_ATTR_ANTENNA_GAIN \ |
| QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_ANTENNA_GAIN |
| #define CHAN_INFO_ATTR_VHT_SEG_0 \ |
| QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0 |
| #define CHAN_INFO_ATTR_VHT_SEG_1 \ |
| QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1 |
| |
| #define CHAN_INFO_ATTR_FREQ_VHT_SEG_0 \ |
| QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 |
| #define CHAN_INFO_ATTR_FREQ_VHT_SEG_1 \ |
| QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 |
| |
| /** |
| * hdd_cfg80211_update_channel_info() - add channel info attributes |
| * @hdd_ctx: pointer to hdd context |
| * @skb: pointer to sk buff |
| * @hdd_ctx: pointer to hdd station context |
| * @idx: attribute index |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int32_t |
| hdd_cfg80211_update_channel_info(struct hdd_context *hdd_ctx, |
| struct sk_buff *skb, |
| struct sap_config *sap_config, int idx) |
| { |
| struct nlattr *nla_attr, *channel; |
| struct hdd_channel_info *icv; |
| int i; |
| uint32_t freq_seg_0 = 0, freq_seg_1 = 0; |
| enum reg_wifi_band band; |
| uint8_t band_mask; |
| |
| nla_attr = nla_nest_start(skb, idx); |
| if (!nla_attr) |
| goto fail; |
| |
| for (i = 0; i < sap_config->channel_info_count; i++) { |
| channel = nla_nest_start(skb, i); |
| if (!channel) |
| goto fail; |
| |
| icv = &sap_config->channel_info[i]; |
| if (!icv) { |
| hdd_err("channel info not found"); |
| goto fail; |
| } |
| |
| band = wlan_reg_freq_to_band(icv->freq); |
| band_mask = 1 << band; |
| |
| if (icv->vht_center_freq_seg0) |
| freq_seg_0 = wlan_reg_chan_band_to_freq(hdd_ctx->pdev, |
| icv->vht_center_freq_seg0, |
| band_mask); |
| if (icv->vht_center_freq_seg1) |
| freq_seg_1 = wlan_reg_chan_band_to_freq(hdd_ctx->pdev, |
| icv->vht_center_freq_seg1, |
| band_mask); |
| |
| if (nla_put_u16(skb, CHAN_INFO_ATTR_FREQ, |
| icv->freq) || |
| nla_put_u32(skb, CHAN_INFO_ATTR_FLAGS, |
| icv->flags) || |
| nla_put_u32(skb, CHAN_INFO_ATTR_FLAG_EXT, |
| icv->flagext) || |
| nla_put_u8(skb, CHAN_INFO_ATTR_MAX_REG_POWER, |
| icv->max_reg_power) || |
| nla_put_u8(skb, CHAN_INFO_ATTR_MAX_POWER, |
| icv->max_radio_power) || |
| nla_put_u8(skb, CHAN_INFO_ATTR_MIN_POWER, |
| icv->min_radio_power) || |
| nla_put_u8(skb, CHAN_INFO_ATTR_REG_CLASS_ID, |
| icv->reg_class_id) || |
| nla_put_u8(skb, CHAN_INFO_ATTR_ANTENNA_GAIN, |
| icv->max_antenna_gain) || |
| nla_put_u8(skb, CHAN_INFO_ATTR_VHT_SEG_0, |
| icv->vht_center_freq_seg0) || |
| nla_put_u8(skb, CHAN_INFO_ATTR_VHT_SEG_1, |
| icv->vht_center_freq_seg1) || |
| nla_put_u32(skb, CHAN_INFO_ATTR_FREQ_VHT_SEG_0, |
| freq_seg_0) || |
| nla_put_u32(skb, CHAN_INFO_ATTR_FREQ_VHT_SEG_1, |
| freq_seg_1)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| nla_nest_end(skb, channel); |
| } |
| nla_nest_end(skb, nla_attr); |
| return 0; |
| fail: |
| hdd_err("nl channel update failed"); |
| return -EINVAL; |
| } |
| #undef CHAN_INFO_ATTR_FLAGS |
| #undef CHAN_INFO_ATTR_FLAG_EXT |
| #undef CHAN_INFO_ATTR_FREQ |
| #undef CHAN_INFO_ATTR_MAX_REG_POWER |
| #undef CHAN_INFO_ATTR_MAX_POWER |
| #undef CHAN_INFO_ATTR_MIN_POWER |
| #undef CHAN_INFO_ATTR_REG_CLASS_ID |
| #undef CHAN_INFO_ATTR_ANTENNA_GAIN |
| #undef CHAN_INFO_ATTR_VHT_SEG_0 |
| #undef CHAN_INFO_ATTR_VHT_SEG_1 |
| |
| #undef CHAN_INFO_ATTR_FREQ_VHT_SEG_0 |
| #undef CHAN_INFO_ATTR_FREQ_VHT_SEG_1 |
| |
| /** |
| * hdd_cfg80211_update_pcl() - add pcl info attributes |
| * @hdd_ctx: pointer to hdd context |
| * @skb: pointer to sk buff |
| * @hdd_ctx: pointer to hdd station context |
| * @idx: attribute index |
| * @vendor_pcl_list: PCL list |
| * @vendor_weight_list: PCL weights |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int32_t |
| hdd_cfg80211_update_pcl(struct hdd_context *hdd_ctx, |
| struct sk_buff *skb, |
| uint8_t ch_list_count, int idx, |
| uint32_t *vendor_pcl_list, uint8_t *vendor_weight_list) |
| { |
| struct nlattr *nla_attr, *channel; |
| int i; |
| uint8_t chan; |
| |
| nla_attr = nla_nest_start(skb, idx); |
| if (!nla_attr) |
| goto fail; |
| |
| for (i = 0; i < ch_list_count; i++) { |
| channel = nla_nest_start(skb, i); |
| if (!channel) |
| goto fail; |
| |
| chan = (uint8_t)wlan_reg_freq_to_chan(hdd_ctx->pdev, |
| vendor_pcl_list[i]); |
| |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_PCL_CHANNEL, chan) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_PCL_FREQ, |
| vendor_pcl_list[i]) || |
| nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT, |
| vendor_weight_list[i])) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| nla_nest_end(skb, channel); |
| } |
| nla_nest_end(skb, nla_attr); |
| |
| return 0; |
| fail: |
| hdd_err("updating pcl list failed"); |
| return -EINVAL; |
| } |
| |
| static void hdd_get_scan_band(struct hdd_context *hdd_ctx, |
| struct sap_config *sap_config, |
| enum band_info *band) |
| { |
| /* Get scan band */ |
| if ((sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211B) || |
| (sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211G)) { |
| *band = BAND_2G; |
| } else if (sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211A) { |
| *band = BAND_5G; |
| } else if (sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211ANY) { |
| *band = BAND_ALL; |
| } |
| } |
| |
| /** |
| * wlan_hdd_sap_get_valid_channellist() - Get SAPs valid channel list |
| * @ap_adapter: adapter |
| * @channel_count: valid channel count |
| * @freq_list: valid channel frequency (MHz) list |
| * @band: frequency band |
| * |
| * This API returns valid channel list for SAP after removing nol and |
| * channel which lies outside of configuration. |
| * |
| * Return: Zero on success, non-zero on failure |
| */ |
| static int wlan_hdd_sap_get_valid_channellist(struct hdd_adapter *adapter, |
| uint32_t *channel_count, |
| uint32_t *freq_list, |
| enum band_info band) |
| { |
| struct sap_config *sap_config; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint32_t pcl_freqs[NUM_CHANNELS] = {0}; |
| uint32_t chan_count; |
| uint32_t i; |
| QDF_STATUS status; |
| struct wlan_objmgr_pdev *pdev = hdd_ctx->pdev; |
| |
| sap_config = &adapter->session.ap.sap_config; |
| |
| status = policy_mgr_get_valid_chans(hdd_ctx->psoc, |
| pcl_freqs, |
| &chan_count); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("Failed to get channel list"); |
| return -EINVAL; |
| } |
| |
| *channel_count = 0; |
| for (i = 0; i < chan_count; i++) { |
| if (*channel_count >= NUM_CHANNELS) |
| break; |
| |
| if (band == BAND_2G && |
| WLAN_REG_IS_24GHZ_CH_FREQ(pcl_freqs[i]) && |
| !wlan_reg_is_disable_for_freq(pdev, pcl_freqs[i])) { |
| freq_list[*channel_count] = pcl_freqs[i]; |
| *channel_count += 1; |
| } else if (band == BAND_5G && |
| (WLAN_REG_IS_5GHZ_CH_FREQ(pcl_freqs[i]) || |
| WLAN_REG_IS_6GHZ_CHAN_FREQ(pcl_freqs[i])) && |
| !wlan_reg_is_disable_for_freq(pdev, pcl_freqs[i])) { |
| freq_list[*channel_count] = pcl_freqs[i]; |
| *channel_count += 1; |
| } |
| } |
| |
| if (*channel_count == 0) { |
| hdd_err("no valid channel found"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_get_external_acs_event_len() - Get event buffer length for external ACS |
| * @channel_count: number of channels for ACS operation |
| * |
| * Return: External ACS event (SUBCMD_UPDATE_EXTERNAL_ACS_CONFIG) buffer length. |
| */ |
| static int hdd_get_external_acs_event_len(uint32_t channel_count) |
| { |
| uint32_t len = NLMSG_HDRLEN; |
| uint32_t i; |
| |
| /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED */ |
| len += nla_total_size(sizeof(u32)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT */ |
| len += nla_total_size(sizeof(u32)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE */ |
| len += nla_total_size(sizeof(u32)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_FREQ_LIST */ |
| len += nla_total_size(channel_count * sizeof(u32)); |
| |
| for (i = 0; i < channel_count; i++) { |
| /* QCA_WLAN_VENDOR_ATTR_PCL_CHANNEL */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_PCL_FREQ */ |
| len += nla_total_size(sizeof(u32)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT */ |
| len += nla_total_size(sizeof(u8)); |
| } |
| |
| for (i = 0; i < channel_count; i++) { |
| /* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ */ |
| len += nla_total_size(sizeof(u16)); |
| |
| /* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS */ |
| len += nla_total_size(sizeof(u32)); |
| |
| /* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT */ |
| len += nla_total_size(sizeof(u32)); |
| |
| /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_REG_POWER */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_POWER */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MIN_POWER */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_REG_CLASS_ID */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_ANTENNA_GAIN */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0 */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1 */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 */ |
| len += nla_total_size(sizeof(u32)); |
| |
| /* VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 */ |
| len += nla_total_size(sizeof(u32)); |
| } |
| |
| /* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY */ |
| len += nla_total_size(sizeof(u32)); |
| |
| return len; |
| } |
| |
| int hdd_cfg80211_update_acs_config(struct hdd_adapter *adapter, |
| uint8_t reason) |
| { |
| struct sk_buff *skb = NULL; |
| struct sap_config *sap_config; |
| uint32_t channel_count = 0, status = -EINVAL; |
| uint32_t *freq_list; |
| uint32_t vendor_pcl_list[NUM_CHANNELS] = {0}; |
| uint8_t vendor_weight_list[NUM_CHANNELS] = {0}; |
| struct hdd_vendor_acs_chan_params acs_chan_params; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| enum band_info band = BAND_2G; |
| eCsrPhyMode phy_mode; |
| enum qca_wlan_vendor_attr_external_acs_policy acs_policy; |
| uint32_t i, id; |
| QDF_STATUS qdf_status; |
| bool is_external_acs_policy = cfg_default(CFG_EXTERNAL_ACS_POLICY); |
| uint32_t len; |
| |
| if (!hdd_ctx) { |
| hdd_err("HDD context is NULL"); |
| return -EINVAL; |
| } |
| |
| hdd_enter(); |
| sap_config = &adapter->session.ap.sap_config; |
| /* When first 2 connections are on the same frequency band, |
| * then PCL would include only channels from the other |
| * frequency band on which no connections are active |
| */ |
| if ((policy_mgr_get_connection_count(hdd_ctx->psoc) == 2) && |
| (sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211ANY)) { |
| struct policy_mgr_conc_connection_info *conc_connection_info; |
| |
| conc_connection_info = policy_mgr_get_conn_info(&i); |
| if (policy_mgr_are_2_freq_on_same_mac(hdd_ctx->psoc, |
| conc_connection_info[0].freq, |
| conc_connection_info[1].freq)) { |
| if (!WLAN_REG_IS_24GHZ_CH_FREQ( |
| sap_config->acs_cfg.pcl_chan_freq[0])) { |
| sap_config->acs_cfg.band = |
| QCA_ACS_MODE_IEEE80211A; |
| hdd_update_acs_channel_list(sap_config, |
| BAND_5G); |
| } else { |
| sap_config->acs_cfg.band = |
| QCA_ACS_MODE_IEEE80211G; |
| hdd_update_acs_channel_list(sap_config, |
| BAND_2G); |
| } |
| } |
| } |
| |
| hdd_get_scan_band(hdd_ctx, &adapter->session.ap.sap_config, &band); |
| |
| freq_list = qdf_mem_malloc(sizeof(uint32_t) * NUM_CHANNELS); |
| if (!freq_list) |
| return -ENOMEM; |
| |
| if (sap_config->acs_cfg.freq_list) { |
| /* Copy INI or hostapd provided ACS channel range*/ |
| for (i = 0; i < sap_config->acs_cfg.ch_list_count; i++) |
| freq_list[i] = sap_config->acs_cfg.freq_list[i]; |
| channel_count = sap_config->acs_cfg.ch_list_count; |
| } else { |
| /* No channel list provided, copy all valid channels */ |
| wlan_hdd_sap_get_valid_channellist(adapter, |
| &channel_count, |
| freq_list, |
| band); |
| } |
| |
| sap_config->channel_info = qdf_mem_malloc( |
| sizeof(struct hdd_channel_info) * |
| channel_count); |
| if (!sap_config->channel_info) { |
| status = -ENOMEM; |
| goto fail; |
| } |
| |
| hdd_update_reg_chan_info(adapter, channel_count, freq_list); |
| |
| /* Get phymode */ |
| phy_mode = adapter->session.ap.sap_config.acs_cfg.hw_mode; |
| |
| len = hdd_get_external_acs_event_len(channel_count); |
| id = QCA_NL80211_VENDOR_SUBCMD_UPDATE_EXTERNAL_ACS_CONFIG; |
| skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, &adapter->wdev, |
| len, id, GFP_KERNEL); |
| if (!skb) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| status = -ENOMEM; |
| goto fail; |
| } |
| /* |
| * Application expects pcl to be a subset of channel list |
| * Remove all channels which are not in channel list from pcl |
| * and add weight as zero |
| */ |
| acs_chan_params.vendor_pcl_list = vendor_pcl_list; |
| acs_chan_params.vendor_weight_list = vendor_weight_list; |
| |
| hdd_update_vendor_pcl_list(hdd_ctx, &acs_chan_params, |
| sap_config); |
| |
| if (acs_chan_params.pcl_count) { |
| hdd_debug("ACS PCL list: len: %d", |
| acs_chan_params.pcl_count); |
| for (i = 0; i < acs_chan_params.pcl_count; i++) |
| hdd_debug("channel_frequency: %u, weight: %u", |
| acs_chan_params. |
| vendor_pcl_list[i], |
| acs_chan_params. |
| vendor_weight_list[i]); |
| } |
| |
| qdf_status = ucfg_mlme_get_external_acs_policy(hdd_ctx->psoc, |
| &is_external_acs_policy); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) |
| hdd_err("get_external_acs_policy failed, set default"); |
| |
| if (is_external_acs_policy) { |
| acs_policy = |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY; |
| } else { |
| acs_policy = |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED; |
| } |
| /* Update values in NL buffer */ |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON, |
| reason) || |
| nla_put_flag(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED) || |
| nla_put_flag(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT) |
| || |
| nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH, |
| sap_config->acs_cfg.ch_width) || |
| nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND, |
| band) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE, |
| phy_mode) || |
| nla_put(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_FREQ_LIST, |
| channel_count * sizeof(uint32_t), freq_list)) { |
| hdd_err("nla put fail"); |
| goto fail; |
| } |
| status = |
| hdd_cfg80211_update_pcl(hdd_ctx, skb, |
| acs_chan_params.pcl_count, |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL, |
| vendor_pcl_list, |
| vendor_weight_list); |
| |
| if (status != 0) |
| goto fail; |
| |
| id = QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO; |
| |
| status = hdd_cfg80211_update_channel_info(hdd_ctx, skb, sap_config, id); |
| if (status != 0) |
| goto fail; |
| |
| status = nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY, |
| acs_policy); |
| |
| if (status != 0) |
| goto fail; |
| |
| cfg80211_vendor_event(skb, GFP_KERNEL); |
| qdf_mem_free(freq_list); |
| qdf_mem_free(sap_config->channel_info); |
| |
| return 0; |
| fail: |
| qdf_mem_free(freq_list); |
| if (sap_config->channel_info) |
| qdf_mem_free(sap_config->channel_info); |
| if (skb) |
| kfree_skb(skb); |
| return status; |
| } |
| |
| /** |
| * hdd_create_acs_timer(): Initialize vendor ACS timer |
| * @adapter: pointer to SAP adapter struct |
| * |
| * This function initializes the vendor ACS timer. |
| * |
| * Return: Status of create vendor ACS timer |
| */ |
| static int hdd_create_acs_timer(struct hdd_adapter *adapter) |
| { |
| struct hdd_external_acs_timer_context *timer_context; |
| QDF_STATUS status; |
| |
| if (adapter->session.ap.vendor_acs_timer_initialized) |
| return 0; |
| |
| hdd_debug("Starting vendor app based ACS"); |
| timer_context = qdf_mem_malloc(sizeof(*timer_context)); |
| if (!timer_context) |
| return -ENOMEM; |
| |
| timer_context->adapter = adapter; |
| |
| set_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags); |
| status = qdf_mc_timer_init(&adapter->session.ap.vendor_acs_timer, |
| QDF_TIMER_TYPE_SW, |
| hdd_acs_response_timeout_handler, timer_context); |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_err("Failed to initialize acs response timeout timer"); |
| return -EFAULT; |
| } |
| adapter->session.ap.vendor_acs_timer_initialized = true; |
| return 0; |
| } |
| |
| static const struct nla_policy |
| wlan_hdd_cfg80211_do_acs_policy[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE] = { .type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED] = { .type = NLA_FLAG }, |
| [QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED] = { .type = NLA_FLAG }, |
| [QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED] = { .type = NLA_FLAG }, |
| [QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH] = { .type = NLA_U16 }, |
| [QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST] = { .type = NLA_BINARY, |
| .len = sizeof(NLA_U8) * NUM_CHANNELS }, |
| [QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST] = { .type = NLA_BINARY, |
| .len = sizeof(NLA_U32) * NUM_CHANNELS }, |
| [QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED] = { .type = NLA_FLAG }, |
| [QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP] = { .type = NLA_U16 }, |
| [QCA_WLAN_VENDOR_ATTR_ACS_EDMG_ENABLED] = { .type = NLA_FLAG }, |
| [QCA_WLAN_VENDOR_ATTR_ACS_EDMG_CHANNEL] = { .type = NLA_U8 }, |
| }; |
| |
| int hdd_start_vendor_acs(struct hdd_adapter *adapter) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| int status; |
| QDF_STATUS qdf_status; |
| bool is_acs_support_for_dfs_ltecoex = cfg_default(CFG_USER_ACS_DFS_LTE); |
| |
| status = hdd_create_acs_timer(adapter); |
| if (status != 0) { |
| hdd_err("failed to create acs timer"); |
| return status; |
| } |
| status = hdd_update_acs_timer_reason(adapter, |
| QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT); |
| if (status != 0) { |
| hdd_err("failed to update acs timer reason"); |
| return status; |
| } |
| qdf_status = ucfg_mlme_get_acs_support_for_dfs_ltecoex( |
| hdd_ctx->psoc, |
| &is_acs_support_for_dfs_ltecoex); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) |
| hdd_err("get_acs_support_for_dfs_ltecoex failed, set def"); |
| |
| if (is_acs_support_for_dfs_ltecoex) |
| status = qdf_status_to_os_return(wlan_sap_set_vendor_acs( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| true)); |
| else |
| status = qdf_status_to_os_return(wlan_sap_set_vendor_acs( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| false)); |
| |
| return status; |
| } |
| |
| /** |
| * hdd_avoid_acs_channels() - Avoid acs channels |
| * @hdd_ctx: Pointer to the hdd context |
| * @sap_config: Sap config structure pointer |
| * |
| * This function avoids channels from the acs corresponding to |
| * the frequencies configured in the ini sap_avoid_acs_freq_list |
| * |
| * Return: None |
| */ |
| |
| #ifdef SAP_AVOID_ACS_FREQ_LIST |
| static void hdd_avoid_acs_channels(struct hdd_context *hdd_ctx, |
| struct sap_config *sap_config) |
| { |
| int i, j, ch_cnt = 0; |
| uint16_t avoid_acs_freq_list[CFG_VALID_CHANNEL_LIST_LEN]; |
| uint8_t avoid_acs_freq_list_num; |
| |
| ucfg_mlme_get_acs_avoid_freq_list(hdd_ctx->psoc, |
| avoid_acs_freq_list, |
| &avoid_acs_freq_list_num); |
| |
| for (i = 0; i < sap_config->acs_cfg.ch_list_count; i++) { |
| for (j = 0; j < avoid_acs_freq_list_num; j++) { |
| if (sap_config->acs_cfg.freq_list[i] == |
| avoid_acs_freq_list[j]) { |
| hdd_debug("skip freq %d", |
| sap_config->acs_cfg.freq_list[i]); |
| break; |
| } |
| } |
| if (j == avoid_acs_freq_list_num) |
| sap_config->acs_cfg.freq_list[ch_cnt++] = |
| sap_config->acs_cfg.freq_list[i]; |
| } |
| sap_config->acs_cfg.ch_list_count = ch_cnt; |
| } |
| #else |
| static void hdd_avoid_acs_channels(struct hdd_context *hdd_ctx, |
| struct sap_config *sap_config) |
| { |
| } |
| #endif |
| |
| /** |
| * wlan_hdd_trim_acs_channel_list() - Trims ACS channel list with |
| * intersection of PCL |
| * @pcl: preferred channel list |
| * @pcl_count: Preferred channel list count |
| * @org_ch_list: ACS channel list from user space |
| * @org_ch_list_count: ACS channel count from user space |
| * |
| * Return: None |
| */ |
| static void wlan_hdd_trim_acs_channel_list(uint32_t *pcl, uint8_t pcl_count, |
| uint32_t *org_freq_list, |
| uint8_t *org_ch_list_count) |
| { |
| uint16_t i, j, ch_list_count = 0; |
| |
| if (*org_ch_list_count >= NUM_CHANNELS) { |
| hdd_err("org_ch_list_count too big %d", |
| *org_ch_list_count); |
| return; |
| } |
| |
| if (pcl_count >= NUM_CHANNELS) { |
| hdd_err("pcl_count is too big %d", pcl_count); |
| return; |
| } |
| |
| hdd_debug("Update ACS chan freq with PCL"); |
| for (j = 0; j < *org_ch_list_count; j++) |
| for (i = 0; i < pcl_count; i++) |
| if (pcl[i] == org_freq_list[j]) { |
| org_freq_list[ch_list_count++] = pcl[i]; |
| break; |
| } |
| |
| *org_ch_list_count = ch_list_count; |
| } |
| |
| /* wlan_hdd_dump_freq_list() - Dump the ACS master frequency list |
| * |
| * @freq_list: Frequency list |
| * @num_freq: num of frequencies in list |
| * |
| * Dump the ACS master frequency list. |
| */ |
| static inline |
| void wlan_hdd_dump_freq_list(uint32_t *freq_list, uint8_t num_freq) |
| { |
| uint32_t buf_len = 0; |
| uint32_t i = 0, j = 0; |
| uint8_t *master_chlist; |
| |
| if (num_freq >= NUM_CHANNELS) |
| return; |
| |
| buf_len = NUM_CHANNELS * 4; |
| master_chlist = qdf_mem_malloc(buf_len); |
| |
| if (!master_chlist) |
| return; |
| |
| for (i = 0; i < num_freq && j < buf_len; i++) { |
| j += qdf_scnprintf(master_chlist + j, buf_len - j, |
| "%d ", freq_list[i]); |
| } |
| |
| hdd_debug("Master channel list: %s", master_chlist); |
| qdf_mem_free(master_chlist); |
| } |
| |
| /** |
| * wlan_hdd_handle_zero_acs_list() - Handle worst case of acs channel |
| * trimmed to zero |
| * @hdd_ctx: struct hdd_context |
| * @org_ch_list: ACS channel list from user space |
| * @org_ch_list_count: ACS channel count from user space |
| * |
| * When all chan in ACS freq list is filtered out |
| * by wlan_hdd_trim_acs_channel_list, the hostapd start will fail. |
| * This happens when PCL is PM_24G_SCC_CH_SBS_CH, and SAP acs range includes |
| * 5G channel list. One example is STA active on 6Ghz chan. Hostapd |
| * start SAP on 5G ACS range. The intersection of PCL and ACS range is zero. |
| * Instead of ACS failure, this API selects one channel from ACS range |
| * and report to Hostapd. When hostapd do start_ap, the driver will |
| * force SCC to 6G or move SAP to 2G based on SAP's configuration. |
| * |
| * Return: None |
| */ |
| static void wlan_hdd_handle_zero_acs_list(struct hdd_context *hdd_ctx, |
| uint32_t *acs_freq_list, |
| uint8_t *acs_ch_list_count, |
| uint32_t *org_freq_list, |
| uint8_t org_ch_list_count) |
| { |
| uint16_t i, sta_count; |
| uint32_t acs_chan_default = 0; |
| bool force_sap_allowed = false; |
| |
| if (!acs_ch_list_count || *acs_ch_list_count > 0 || |
| !acs_freq_list) { |
| return; |
| } |
| if (!org_ch_list_count || !org_freq_list) |
| return; |
| |
| if (!policy_mgr_is_force_scc(hdd_ctx->psoc)) |
| return; |
| sta_count = policy_mgr_mode_specific_connection_count |
| (hdd_ctx->psoc, PM_STA_MODE, NULL); |
| sta_count += policy_mgr_mode_specific_connection_count |
| (hdd_ctx->psoc, PM_P2P_CLIENT_MODE, NULL); |
| |
| ucfg_mlme_get_force_sap_enabled(hdd_ctx->psoc, &force_sap_allowed); |
| if (!sta_count && !force_sap_allowed) |
| return; |
| |
| wlan_hdd_dump_freq_list(org_freq_list, org_ch_list_count); |
| |
| for (i = 0; i < org_ch_list_count; i++) { |
| if (wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, |
| org_freq_list[i])) |
| continue; |
| |
| if (wlan_reg_is_6ghz_chan_freq(org_freq_list[i]) && |
| !wlan_reg_is_6ghz_psc_chan_freq(org_freq_list[i])) |
| continue; |
| |
| if (!policy_mgr_is_safe_channel(hdd_ctx->psoc, |
| org_freq_list[i])) |
| continue; |
| acs_chan_default = org_freq_list[i]; |
| break; |
| } |
| if (!acs_chan_default) |
| acs_chan_default = org_freq_list[0]; |
| |
| acs_freq_list[0] = acs_chan_default; |
| *acs_ch_list_count = 1; |
| hdd_debug("retore acs chan list to single freq %d", acs_chan_default); |
| } |
| |
| /** |
| * wlan_hdd_handle_single_ch_in_acs_list() - Handle acs list with single channel |
| * @hdd_ctx: hdd context |
| * @adapter: adapter |
| * @sap_config: sap acs config context |
| * |
| * If only one acs channel is left after filter, driver will return the channel |
| * to hostapd without ACS scan. |
| * |
| * Return: None |
| */ |
| static void |
| wlan_hdd_handle_single_ch_in_acs_list(struct hdd_context *hdd_ctx, |
| struct hdd_adapter *adapter, |
| struct sap_config *sap_config) |
| { |
| uint32_t channel_bonding_mode_2g; |
| |
| ucfg_mlme_get_channel_bonding_24ghz(hdd_ctx->psoc, |
| &channel_bonding_mode_2g); |
| sap_config->acs_cfg.start_ch_freq = |
| sap_config->acs_cfg.freq_list[0]; |
| sap_config->acs_cfg.end_ch_freq = |
| sap_config->acs_cfg.freq_list[0]; |
| sap_config->acs_cfg.pri_ch_freq = |
| sap_config->acs_cfg.freq_list[0]; |
| if (sap_config->acs_cfg.pri_ch_freq <= |
| WLAN_REG_CH_TO_FREQ(CHAN_ENUM_2484) && |
| sap_config->acs_cfg.ch_width >= |
| CH_WIDTH_40MHZ && |
| !channel_bonding_mode_2g) { |
| sap_config->acs_cfg.ch_width = CH_WIDTH_20MHZ; |
| hdd_debug("2.4ghz channel resetting BW to %d 2.4 cbmode %d", |
| sap_config->acs_cfg.ch_width, |
| channel_bonding_mode_2g); |
| } |
| |
| wlan_sap_set_sap_ctx_acs_cfg( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter), sap_config); |
| sap_config_acs_result(hdd_ctx->mac_handle, |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| sap_config->acs_cfg.ht_sec_ch_freq); |
| sap_config->ch_params.ch_width = |
| sap_config->acs_cfg.ch_width; |
| sap_config->ch_params.sec_ch_offset = |
| wlan_reg_freq_to_chan( |
| hdd_ctx->pdev, |
| sap_config->acs_cfg.ht_sec_ch_freq); |
| sap_config->ch_params.center_freq_seg0 = |
| wlan_reg_freq_to_chan( |
| hdd_ctx->pdev, |
| sap_config->acs_cfg.vht_seg0_center_ch_freq); |
| sap_config->ch_params.center_freq_seg1 = |
| wlan_reg_freq_to_chan( |
| hdd_ctx->pdev, |
| sap_config->acs_cfg.vht_seg1_center_ch_freq); |
| sap_config->ch_params.mhz_freq_seg0 = |
| sap_config->acs_cfg.vht_seg0_center_ch_freq; |
| sap_config->ch_params.mhz_freq_seg1 = |
| sap_config->acs_cfg.vht_seg1_center_ch_freq; |
| /*notify hostapd about channel override */ |
| wlan_hdd_cfg80211_acs_ch_select_evt(adapter); |
| wlansap_dcs_set_wlan_interference_mitigation_on_band( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| sap_config); |
| } |
| |
| #if defined(WLAN_FEATURE_11BE) && defined(CFG80211_11BE_BASIC) |
| static void wlan_hdd_set_sap_acs_ch_width_320(struct sap_config *sap_config) |
| { |
| sap_config->acs_cfg.ch_width = CH_WIDTH_320MHZ; |
| } |
| |
| static bool wlan_hdd_is_sap_acs_ch_width_320(struct sap_config *sap_config) |
| { |
| return sap_config->acs_cfg.ch_width == CH_WIDTH_320MHZ; |
| } |
| |
| static void wlan_hdd_set_chandef(struct wlan_objmgr_vdev *vdev, |
| struct cfg80211_chan_def *chandef) |
| { |
| if (vdev->vdev_mlme.des_chan->ch_width != CH_WIDTH_320MHZ) |
| return; |
| |
| chandef->width = NL80211_CHAN_WIDTH_320; |
| /* Set center_freq1 to center frequency of complete 320MHz */ |
| chandef->center_freq1 = vdev->vdev_mlme.des_chan->ch_cfreq2; |
| } |
| #else /* !WLAN_FEATURE_11BE */ |
| static inline |
| void wlan_hdd_set_sap_acs_ch_width_320(struct sap_config *sap_config) |
| { |
| } |
| |
| static inline |
| bool wlan_hdd_is_sap_acs_ch_width_320(struct sap_config *sap_config) |
| { |
| return false; |
| } |
| |
| static inline void wlan_hdd_set_chandef(struct wlan_objmgr_vdev *vdev, |
| struct cfg80211_chan_def *chandef) |
| { |
| } |
| #endif /* WLAN_FEATURE_11BE */ |
| |
| #ifdef WLAN_FEATURE_11BE |
| /** |
| * wlan_hdd_acs_set_eht_enabled() - set is_eht_enabled of acs config |
| * @sap_config: pointer to sap_config |
| * @eht_enabled: eht is enabled |
| * |
| * Return: void |
| */ |
| static void wlan_hdd_acs_set_eht_enabled(struct sap_config *sap_config, |
| bool eht_enabled) |
| { |
| if (!sap_config) { |
| hdd_err("Invalid sap_config"); |
| return; |
| } |
| |
| sap_config->acs_cfg.is_eht_enabled = eht_enabled; |
| } |
| #else |
| static void wlan_hdd_acs_set_eht_enabled(struct sap_config *sap_config, |
| bool eht_enabled) |
| { |
| } |
| #endif /* WLAN_FEATURE_11BE */ |
| |
| static uint16_t wlan_hdd_update_bw_from_mlme(struct hdd_context *hdd_ctx, |
| struct sap_config *sap_config) |
| { |
| uint16_t ch_width, temp_ch_width = 0; |
| QDF_STATUS status; |
| uint8_t hw_mode = HW_MODE_DBS; |
| struct wma_caps_per_phy caps_per_phy = {0}; |
| |
| ch_width = sap_config->acs_cfg.ch_width; |
| |
| if (ch_width > CH_WIDTH_80P80MHZ) |
| return ch_width; |
| |
| /* 2.4ghz is already handled for acs */ |
| if (sap_config->acs_cfg.end_ch_freq <= |
| WLAN_REG_CH_TO_FREQ(CHAN_ENUM_2484)) |
| return ch_width; |
| |
| if (!policy_mgr_is_dbs_enable(hdd_ctx->psoc)) |
| hw_mode = HW_MODE_DBS_NONE; |
| |
| status = wma_get_caps_for_phyidx_hwmode(&caps_per_phy, hw_mode, |
| CDS_BAND_5GHZ); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| return ch_width; |
| |
| switch (ch_width) { |
| case CH_WIDTH_80P80MHZ: |
| if (!(caps_per_phy.vht_5g & WMI_VHT_CAP_CH_WIDTH_80P80_160MHZ)) |
| { |
| if (caps_per_phy.vht_5g & WMI_VHT_CAP_CH_WIDTH_160MHZ) |
| temp_ch_width = CH_WIDTH_160MHZ; |
| else |
| temp_ch_width = CH_WIDTH_80MHZ; |
| } |
| break; |
| case CH_WIDTH_160MHZ: |
| if (!((caps_per_phy.vht_5g & WMI_VHT_CAP_CH_WIDTH_80P80_160MHZ) |
| || (caps_per_phy.vht_5g & WMI_VHT_CAP_CH_WIDTH_160MHZ))) |
| temp_ch_width = CH_WIDTH_80MHZ; |
| break; |
| default: |
| break; |
| } |
| |
| if (!temp_ch_width) |
| return ch_width; |
| |
| hdd_debug("ch_width updated from %d to %d vht_5g: %x", ch_width, |
| temp_ch_width, caps_per_phy.vht_5g); |
| return temp_ch_width; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_do_acs(): CFG80211 handler function for DO_ACS Vendor CMD |
| * @wiphy: Linux wiphy struct pointer |
| * @wdev: Linux wireless device struct pointer |
| * @data: ACS information from hostapd |
| * @data_len: ACS information length |
| * |
| * This function handle DO_ACS Vendor command from hostapd, parses ACS config |
| * and starts ACS procedure. |
| * |
| * Return: ACS procedure start status |
| */ |
| static int __wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct net_device *ndev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct sap_config *sap_config; |
| struct sk_buff *temp_skbuff; |
| int ret, i; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1]; |
| bool ht_enabled, ht40_enabled, vht_enabled, eht_enabled; |
| uint16_t ch_width; |
| enum qca_wlan_vendor_acs_hw_mode hw_mode; |
| enum policy_mgr_con_mode pm_mode; |
| QDF_STATUS qdf_status; |
| bool is_vendor_acs_support = false; |
| bool is_external_acs_policy = false; |
| bool is_vendor_unsafe_ch_present = false; |
| bool sap_force_11n_for_11ac = 0; |
| bool go_force_11n_for_11ac = 0; |
| bool go_11ac_override = 0; |
| bool sap_11ac_override = 0; |
| uint8_t vht_ch_width; |
| uint32_t channel_bonding_mode_2g; |
| |
| /* ***Note*** Donot set SME config related to ACS operation here because |
| * ACS operation is not synchronouse and ACS for Second AP may come when |
| * ACS operation for first AP is going on. So only do_acs is split to |
| * separate start_acs routine. Also SME-PMAC struct that is used to |
| * pass paremeters from HDD to SAP is global. Thus All ACS related SME |
| * config shall be set only from start_acs. |
| */ |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| ucfg_mlme_get_sap_force_11n_for_11ac(hdd_ctx->psoc, |
| &sap_force_11n_for_11ac); |
| ucfg_mlme_get_go_force_11n_for_11ac(hdd_ctx->psoc, |
| &go_force_11n_for_11ac); |
| ucfg_mlme_get_channel_bonding_24ghz(hdd_ctx->psoc, |
| &channel_bonding_mode_2g); |
| |
| if (!((adapter->device_mode == QDF_SAP_MODE) || |
| (adapter->device_mode == QDF_P2P_GO_MODE))) { |
| hdd_err("Invalid device mode %d", adapter->device_mode); |
| return -EINVAL; |
| } |
| |
| if (cds_is_sub_20_mhz_enabled()) { |
| hdd_err("ACS not supported in sub 20 MHz ch wd."); |
| return -EINVAL; |
| } |
| |
| if (qdf_atomic_read(&adapter->session.ap.acs_in_progress) > 0) { |
| hdd_err("ACS rejected as previous req already in progress"); |
| return -EINVAL; |
| } else { |
| qdf_atomic_set(&adapter->session.ap.acs_in_progress, 1); |
| qdf_event_reset(&adapter->acs_complete_event); |
| } |
| |
| hdd_reg_wait_for_country_change(hdd_ctx); |
| |
| ret = wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX, data, |
| data_len, |
| wlan_hdd_cfg80211_do_acs_policy); |
| if (ret) { |
| hdd_err("Invalid ATTR"); |
| goto out; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) { |
| hdd_err("Attr hw_mode failed"); |
| ret = -EINVAL; |
| goto out; |
| } |
| hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]); |
| |
| hdd_nofl_info("ACS request vid %d hw mode %d", adapter->vdev_id, |
| hw_mode); |
| ht_enabled = nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED]); |
| ht40_enabled = nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED]); |
| vht_enabled = nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED]); |
| eht_enabled = nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED]); |
| |
| if (((adapter->device_mode == QDF_SAP_MODE) && |
| sap_force_11n_for_11ac) || |
| ((adapter->device_mode == QDF_P2P_GO_MODE) && |
| go_force_11n_for_11ac)) { |
| vht_enabled = 0; |
| hdd_info("VHT is Disabled in ACS"); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]) { |
| ch_width = nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]); |
| } else { |
| if (ht_enabled && ht40_enabled) |
| ch_width = 40; |
| else |
| ch_width = 20; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED]) |
| eht_enabled = |
| nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED]); |
| else |
| eht_enabled = 0; |
| |
| if (ch_width == 320 && !eht_enabled) |
| ch_width = 160; |
| |
| /* this may be possible, when sap_force_11n_for_11ac or |
| * go_force_11n_for_11ac is set |
| */ |
| if ((ch_width == 80 || ch_width == 160) && !vht_enabled) { |
| if (ht_enabled && ht40_enabled) |
| ch_width = 40; |
| else |
| ch_width = 20; |
| } |
| |
| sap_config = &adapter->session.ap.sap_config; |
| |
| /* Check and free if memory is already allocated for acs channel list */ |
| wlan_hdd_undo_acs(adapter); |
| |
| qdf_mem_zero(&sap_config->acs_cfg, sizeof(struct sap_acs_cfg)); |
| |
| if (ch_width == 320) |
| wlan_hdd_set_sap_acs_ch_width_320(sap_config); |
| else if (ch_width == 160) |
| sap_config->acs_cfg.ch_width = CH_WIDTH_160MHZ; |
| else if (ch_width == 80) |
| sap_config->acs_cfg.ch_width = CH_WIDTH_80MHZ; |
| else if (ch_width == 40) |
| sap_config->acs_cfg.ch_width = CH_WIDTH_40MHZ; |
| else |
| sap_config->acs_cfg.ch_width = CH_WIDTH_20MHZ; |
| |
| /* Firstly try to get channel frequencies */ |
| if (tb[QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST]) { |
| uint32_t *freq = |
| nla_data(tb[QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST]); |
| sap_config->acs_cfg.ch_list_count = nla_len( |
| tb[QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST]) / |
| sizeof(uint32_t); |
| if (sap_config->acs_cfg.ch_list_count) { |
| sap_config->acs_cfg.freq_list = qdf_mem_malloc( |
| sap_config->acs_cfg.ch_list_count * |
| sizeof(uint32_t)); |
| sap_config->acs_cfg.master_freq_list = qdf_mem_malloc( |
| sap_config->acs_cfg.ch_list_count * |
| sizeof(uint32_t)); |
| if (!sap_config->acs_cfg.freq_list || |
| !sap_config->acs_cfg.master_freq_list) { |
| ret = -ENOMEM; |
| goto out; |
| } |
| |
| for (i = 0; i < sap_config->acs_cfg.ch_list_count; |
| i++) { |
| sap_config->acs_cfg.master_freq_list[i] = |
| freq[i]; |
| sap_config->acs_cfg.freq_list[i] = freq[i]; |
| } |
| sap_config->acs_cfg.master_ch_list_count = |
| sap_config->acs_cfg.ch_list_count; |
| } |
| } else if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST]) { |
| uint8_t *tmp = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST]); |
| |
| sap_config->acs_cfg.ch_list_count = nla_len( |
| tb[QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST]); |
| if (sap_config->acs_cfg.ch_list_count) { |
| sap_config->acs_cfg.freq_list = qdf_mem_malloc( |
| sap_config->acs_cfg.ch_list_count * |
| sizeof(uint32_t)); |
| sap_config->acs_cfg.master_freq_list = qdf_mem_malloc( |
| sap_config->acs_cfg.ch_list_count * |
| sizeof(uint32_t)); |
| if (!sap_config->acs_cfg.freq_list || |
| !sap_config->acs_cfg.master_freq_list) { |
| ret = -ENOMEM; |
| goto out; |
| } |
| |
| /* convert channel to frequency */ |
| for (i = 0; i < sap_config->acs_cfg.ch_list_count; |
| i++) { |
| sap_config->acs_cfg.freq_list[i] = |
| wlan_reg_legacy_chan_to_freq( |
| hdd_ctx->pdev, |
| tmp[i]); |
| sap_config->acs_cfg.master_freq_list[i] = |
| sap_config->acs_cfg.freq_list[i]; |
| } |
| sap_config->acs_cfg.master_ch_list_count = |
| sap_config->acs_cfg.ch_list_count; |
| } |
| } |
| |
| if (!sap_config->acs_cfg.ch_list_count) { |
| hdd_err("acs config chan count 0"); |
| ret = -EINVAL; |
| goto out; |
| } |
| |
| hdd_avoid_acs_channels(hdd_ctx, sap_config); |
| |
| pm_mode = |
| policy_mgr_convert_device_mode_to_qdf_type(adapter->device_mode); |
| /* consult policy manager to get PCL */ |
| qdf_status = policy_mgr_get_pcl(hdd_ctx->psoc, pm_mode, |
| sap_config->acs_cfg.pcl_chan_freq, |
| &sap_config->acs_cfg.pcl_ch_count, |
| sap_config->acs_cfg. |
| pcl_channels_weight_list, |
| NUM_CHANNELS); |
| |
| sap_config->acs_cfg.band = hw_mode; |
| |
| qdf_status = ucfg_mlme_get_external_acs_policy(hdd_ctx->psoc, |
| &is_external_acs_policy); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) |
| hdd_err("get_external_acs_policy failed"); |
| |
| sap_config->acs_cfg.acs_mode = true; |
| |
| if (is_external_acs_policy && |
| policy_mgr_is_force_scc(hdd_ctx->psoc) && |
| policy_mgr_get_connection_count(hdd_ctx->psoc)) { |
| if (adapter->device_mode == QDF_SAP_MODE) |
| is_vendor_unsafe_ch_present = |
| wlansap_filter_vendor_unsafe_ch_freq( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| sap_config); |
| wlan_hdd_trim_acs_channel_list( |
| sap_config->acs_cfg.pcl_chan_freq, |
| sap_config->acs_cfg.pcl_ch_count, |
| sap_config->acs_cfg.freq_list, |
| &sap_config->acs_cfg.ch_list_count); |
| if (!sap_config->acs_cfg.ch_list_count && |
| sap_config->acs_cfg.master_ch_list_count && |
| !is_vendor_unsafe_ch_present) |
| wlan_hdd_handle_zero_acs_list( |
| hdd_ctx, |
| sap_config->acs_cfg.freq_list, |
| &sap_config->acs_cfg.ch_list_count, |
| sap_config->acs_cfg.master_freq_list, |
| sap_config->acs_cfg.master_ch_list_count); |
| /* if it is only one channel, send ACS event to upper layer */ |
| if (sap_config->acs_cfg.ch_list_count == 1) { |
| wlan_hdd_handle_single_ch_in_acs_list( |
| hdd_ctx, adapter, sap_config); |
| ret = 0; |
| goto out; |
| } else if (!sap_config->acs_cfg.ch_list_count) { |
| hdd_err("channel list count 0"); |
| ret = -EINVAL; |
| goto out; |
| } |
| } else if (adapter->device_mode == QDF_SAP_MODE) { |
| wlansap_filter_vendor_unsafe_ch_freq( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| sap_config); |
| if (sap_config->acs_cfg.ch_list_count == 1) { |
| wlan_hdd_handle_single_ch_in_acs_list( |
| hdd_ctx, adapter, sap_config); |
| ret = 0; |
| goto out; |
| } else if (!sap_config->acs_cfg.ch_list_count) { |
| hdd_err("channel count 0 after vendor unsafe filter"); |
| ret = -EINVAL; |
| goto out; |
| } |
| } |
| |
| ret = wlan_hdd_set_acs_ch_range(sap_config, hw_mode, |
| ht_enabled, vht_enabled); |
| if (ret) { |
| hdd_err("set acs channel range failed"); |
| goto out; |
| } |
| |
| ucfg_mlme_is_go_11ac_override(hdd_ctx->psoc, &go_11ac_override); |
| ucfg_mlme_is_sap_11ac_override(hdd_ctx->psoc, &sap_11ac_override); |
| /* ACS override for android */ |
| if (ht_enabled && |
| sap_config->acs_cfg.end_ch_freq >= |
| WLAN_REG_CH_TO_FREQ(CHAN_ENUM_5180) && |
| ((adapter->device_mode == QDF_SAP_MODE && |
| !sap_force_11n_for_11ac && |
| sap_11ac_override) || |
| (adapter->device_mode == QDF_P2P_GO_MODE && |
| !go_force_11n_for_11ac && |
| go_11ac_override))) { |
| vht_enabled = 1; |
| sap_config->acs_cfg.hw_mode = eCSR_DOT11_MODE_11ac; |
| qdf_status = |
| ucfg_mlme_get_vht_channel_width(hdd_ctx->psoc, |
| &vht_ch_width); |
| ch_width = vht_ch_width; |
| sap_config->acs_cfg.ch_width = ch_width; |
| } |
| |
| /* Check 2.4ghz cbmode and update BW if only 2.4 channels are present */ |
| if (sap_config->acs_cfg.end_ch_freq <= |
| WLAN_REG_CH_TO_FREQ(CHAN_ENUM_2484) && |
| sap_config->acs_cfg.ch_width >= eHT_CHANNEL_WIDTH_40MHZ) { |
| |
| sap_config->acs_cfg.ch_width = channel_bonding_mode_2g ? |
| eHT_CHANNEL_WIDTH_40MHZ : eHT_CHANNEL_WIDTH_20MHZ; |
| |
| hdd_debug("Only 2.4ghz channels, resetting BW to %d 2.4 cbmode %d", |
| sap_config->acs_cfg.ch_width, |
| channel_bonding_mode_2g); |
| } |
| |
| sap_config->acs_cfg.ch_width = wlan_hdd_update_bw_from_mlme(hdd_ctx, |
| sap_config); |
| |
| hdd_nofl_debug("ACS Config country %s ch_width %d hw_mode %d ACS_BW: %d HT: %d VHT: %d EHT: %d START_CH: %d END_CH: %d band %d", |
| hdd_ctx->reg.alpha2, ch_width, |
| sap_config->acs_cfg.hw_mode, sap_config->acs_cfg.ch_width, |
| ht_enabled, vht_enabled, eht_enabled, |
| sap_config->acs_cfg.start_ch_freq, |
| sap_config->acs_cfg.end_ch_freq, |
| sap_config->acs_cfg.band); |
| host_log_acs_req_event(adapter->dev->name, |
| csr_phy_mode_str(sap_config->acs_cfg.hw_mode), |
| ch_width, ht_enabled, vht_enabled, |
| sap_config->acs_cfg.start_ch_freq, |
| sap_config->acs_cfg.end_ch_freq); |
| |
| sap_config->acs_cfg.is_ht_enabled = ht_enabled; |
| sap_config->acs_cfg.is_vht_enabled = vht_enabled; |
| wlan_hdd_acs_set_eht_enabled(sap_config, eht_enabled); |
| sap_dump_acs_channel(&sap_config->acs_cfg); |
| |
| qdf_status = ucfg_mlme_get_vendor_acs_support(hdd_ctx->psoc, |
| &is_vendor_acs_support); |
| if (QDF_IS_STATUS_ERROR(qdf_status)) |
| hdd_err("get_vendor_acs_support failed, set default"); |
| |
| /* Check if vendor specific acs is enabled */ |
| if (is_vendor_acs_support) |
| ret = hdd_start_vendor_acs(adapter); |
| else |
| ret = wlan_hdd_cfg80211_start_acs(adapter); |
| |
| out: |
| if (ret == 0) { |
| temp_skbuff = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, |
| NLMSG_HDRLEN); |
| if (temp_skbuff) |
| return cfg80211_vendor_cmd_reply(temp_skbuff); |
| } |
| qdf_atomic_set(&adapter->session.ap.acs_in_progress, 0); |
| |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_do_acs : CFG80211 handler function for DO_ACS Vendor CMD |
| * @wiphy: Linux wiphy struct pointer |
| * @wdev: Linux wireless device struct pointer |
| * @data: ACS information from hostapd |
| * @data_len: ACS information len |
| * |
| * This function handle DO_ACS Vendor command from hostapd, parses ACS config |
| * and starts ACS procedure. |
| * |
| * Return: ACS procedure start status |
| */ |
| |
| static int wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_do_acs(wiphy, wdev, data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * wlan_hdd_undo_acs : Do cleanup of DO_ACS |
| * @adapter: Pointer to adapter struct |
| * |
| * This function handle cleanup of what was done in DO_ACS, including free |
| * memory. |
| * |
| * Return: void |
| */ |
| void wlan_hdd_undo_acs(struct hdd_adapter *adapter) |
| { |
| sap_undo_acs(WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| &adapter->session.ap.sap_config); |
| } |
| |
| /** |
| * hdd_fill_acs_chan_freq() - Populate channel frequencies (MHz) selected in ACS |
| * @hdd_ctx: pointer to hdd context |
| * @sap_cfg: sap acs configuration |
| * @vendor_event: output pointer to populate channel frequencies (MHz) |
| * |
| * Return: If populated successfully return 0 else negative value. |
| */ |
| static int hdd_fill_acs_chan_freq(struct hdd_context *hdd_ctx, |
| struct sap_config *sap_cfg, |
| struct sk_buff *vendor_event) |
| { |
| uint32_t id; |
| int errno; |
| |
| id = QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY; |
| errno = nla_put_u32(vendor_event, id, sap_cfg->acs_cfg.pri_ch_freq); |
| if (errno) { |
| hdd_err("VENDOR_ATTR_ACS_PRIMARY_FREQUENCY put fail"); |
| return errno; |
| } |
| |
| id = QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY; |
| errno = nla_put_u32(vendor_event, id, sap_cfg->acs_cfg.ht_sec_ch_freq); |
| if (errno) { |
| hdd_err("VENDOR_ATTR_ACS_SECONDARY_FREQUENCY put fail"); |
| return errno; |
| } |
| |
| id = QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY; |
| errno = nla_put_u32(vendor_event, id, |
| sap_cfg->acs_cfg.vht_seg0_center_ch_freq); |
| if (errno) { |
| hdd_err("VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY put fail"); |
| return errno; |
| } |
| |
| id = QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY; |
| errno = nla_put_u32(vendor_event, id, |
| sap_cfg->acs_cfg.vht_seg1_center_ch_freq); |
| if (errno) { |
| hdd_err("VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY put fail"); |
| return errno; |
| } |
| |
| return 0; |
| } |
| |
| static int hdd_get_acs_evt_data_len(struct sap_config *sap_cfg) |
| { |
| uint32_t len = NLMSG_HDRLEN; |
| |
| /* QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY */ |
| len += nla_total_size(sizeof(u32)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY */ |
| len += nla_total_size(sizeof(u32)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY */ |
| len += nla_total_size(sizeof(u32)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY */ |
| len += nla_total_size(sizeof(u32)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH */ |
| len += nla_total_size(sizeof(u16)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE */ |
| len += nla_total_size(sizeof(u8)); |
| |
| /* QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP */ |
| if (sap_acs_is_puncture_applicable(&sap_cfg->acs_cfg)) |
| len += nla_total_size(sizeof(u16)); |
| |
| return len; |
| } |
| |
| #ifdef WLAN_FEATURE_11BE |
| /** |
| * wlan_hdd_acs_get_puncture_bitmap() - get puncture_bitmap for acs result |
| * @acs_cfg: pointer to struct sap_acs_cfg |
| * |
| * Return: acs puncture bitmap |
| */ |
| static uint16_t wlan_hdd_acs_get_puncture_bitmap(struct sap_acs_cfg *acs_cfg) |
| { |
| if (sap_acs_is_puncture_applicable(acs_cfg)) |
| return acs_cfg->acs_puncture_bitmap; |
| |
| return 0; |
| } |
| #else |
| static uint16_t wlan_hdd_acs_get_puncture_bitmap(struct sap_acs_cfg *acs_cfg) |
| { |
| return 0; |
| } |
| #endif /* WLAN_FEATURE_11BE */ |
| |
| /** |
| * wlan_hdd_cfg80211_acs_ch_select_evt: Callback function for ACS evt |
| * @adapter: Pointer to SAP adapter struct |
| * @pri_channel: SAP ACS procedure selected Primary channel |
| * @sec_channel: SAP ACS procedure selected secondary channel |
| * |
| * This is a callback function on ACS procedure is completed. |
| * This function send the ACS selected channel information to hostapd |
| * |
| * Return: None |
| */ |
| void wlan_hdd_cfg80211_acs_ch_select_evt(struct hdd_adapter *adapter) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| struct sap_config *sap_cfg = |
| &(WLAN_HDD_GET_AP_CTX_PTR(adapter))->sap_config; |
| struct sk_buff *vendor_event; |
| int ret_val; |
| uint16_t ch_width; |
| uint8_t pri_channel; |
| uint8_t ht_sec_channel; |
| uint8_t vht_seg0_center_ch, vht_seg1_center_ch; |
| uint32_t id = QCA_NL80211_VENDOR_SUBCMD_DO_ACS_INDEX; |
| uint32_t len = hdd_get_acs_evt_data_len(sap_cfg); |
| uint16_t puncture_bitmap; |
| |
| qdf_atomic_set(&adapter->session.ap.acs_in_progress, 0); |
| qdf_event_set(&adapter->acs_complete_event); |
| |
| vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, |
| &adapter->wdev, len, id, |
| GFP_KERNEL); |
| |
| if (!vendor_event) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return; |
| } |
| |
| ret_val = hdd_fill_acs_chan_freq(hdd_ctx, sap_cfg, vendor_event); |
| if (ret_val) { |
| hdd_err("failed to put frequencies"); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| pri_channel = wlan_reg_freq_to_chan(hdd_ctx->pdev, |
| sap_cfg->acs_cfg.pri_ch_freq); |
| |
| ht_sec_channel = wlan_reg_freq_to_chan(hdd_ctx->pdev, |
| sap_cfg->acs_cfg.ht_sec_ch_freq); |
| |
| ret_val = nla_put_u8(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL, |
| pri_channel); |
| if (ret_val) { |
| hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL put fail"); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| ret_val = nla_put_u8(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL, |
| ht_sec_channel); |
| if (ret_val) { |
| hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL put fail"); |
| kfree_skb(vendor_event); |
| return; |
| } |
| vht_seg0_center_ch = wlan_reg_freq_to_chan( |
| hdd_ctx->pdev, |
| sap_cfg->acs_cfg.vht_seg0_center_ch_freq); |
| ret_val = nla_put_u8(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL, |
| vht_seg0_center_ch); |
| if (ret_val) { |
| hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL put fail"); |
| kfree_skb(vendor_event); |
| return; |
| } |
| vht_seg1_center_ch = wlan_reg_freq_to_chan( |
| hdd_ctx->pdev, |
| sap_cfg->acs_cfg.vht_seg1_center_ch_freq); |
| ret_val = nla_put_u8(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL, |
| vht_seg1_center_ch); |
| if (ret_val) { |
| hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL put fail"); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| if (wlan_hdd_is_sap_acs_ch_width_320(sap_cfg)) |
| ch_width = 320; |
| else if (sap_cfg->acs_cfg.ch_width == CH_WIDTH_160MHZ) |
| ch_width = 160; |
| else if (sap_cfg->acs_cfg.ch_width == CH_WIDTH_80MHZ) |
| ch_width = 80; |
| else if (sap_cfg->acs_cfg.ch_width == CH_WIDTH_40MHZ) |
| ch_width = 40; |
| else |
| ch_width = 20; |
| |
| ret_val = nla_put_u16(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH, |
| ch_width); |
| if (ret_val) { |
| hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH put fail"); |
| kfree_skb(vendor_event); |
| return; |
| } |
| if (WLAN_REG_IS_24GHZ_CH_FREQ(sap_cfg->acs_cfg.pri_ch_freq)) |
| ret_val = nla_put_u8(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, |
| QCA_ACS_MODE_IEEE80211G); |
| else |
| ret_val = nla_put_u8(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, |
| QCA_ACS_MODE_IEEE80211A); |
| |
| if (ret_val) { |
| hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE put fail"); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| puncture_bitmap = wlan_hdd_acs_get_puncture_bitmap(&sap_cfg->acs_cfg); |
| if (sap_acs_is_puncture_applicable(&sap_cfg->acs_cfg)) { |
| ret_val = nla_put_u16(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP, |
| puncture_bitmap); |
| if (ret_val) { |
| hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP put fail"); |
| kfree_skb(vendor_event); |
| return; |
| } |
| } |
| |
| hdd_debug("ACS result for %s: PRI_CH_FREQ: %d SEC_CH_FREQ: %d VHT_SEG0: %d VHT_SEG1: %d ACS_BW: %d punc support: %d punc bitmap: %d", |
| adapter->dev->name, sap_cfg->acs_cfg.pri_ch_freq, |
| sap_cfg->acs_cfg.ht_sec_ch_freq, |
| sap_cfg->acs_cfg.vht_seg0_center_ch_freq, |
| sap_cfg->acs_cfg.vht_seg1_center_ch_freq, ch_width, |
| sap_acs_is_puncture_applicable(&sap_cfg->acs_cfg), |
| puncture_bitmap); |
| |
| cfg80211_vendor_event(vendor_event, GFP_KERNEL); |
| } |
| |
| /** |
| * hdd_is_wlm_latency_manager_supported - Checks if WLM Latency manager is |
| * supported |
| * @hdd_ctx: The HDD context |
| * |
| * Return: True if supported, false otherwise |
| */ |
| static inline |
| bool hdd_is_wlm_latency_manager_supported(struct hdd_context *hdd_ctx) |
| { |
| bool latency_enable; |
| |
| if (QDF_IS_STATUS_ERROR(ucfg_mlme_get_latency_enable |
| (hdd_ctx->psoc, &latency_enable))) |
| return false; |
| |
| if (latency_enable && |
| sme_is_feature_supported_by_fw(VDEV_LATENCY_CONFIG)) |
| return true; |
| else |
| return false; |
| } |
| |
| static int |
| __wlan_hdd_cfg80211_get_supported_features(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct sk_buff *skb = NULL; |
| uint32_t fset = 0; |
| int ret; |
| #ifdef FEATURE_WLAN_TDLS |
| bool bvalue; |
| #endif |
| uint32_t fine_time_meas_cap; |
| |
| /* ENTER_DEV() intentionally not used in a frequently invoked API */ |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| if (wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) { |
| hdd_debug("Infra Station mode is supported by driver"); |
| fset |= WIFI_FEATURE_INFRA; |
| } |
| if (true == hdd_is_5g_supported(hdd_ctx)) { |
| hdd_debug("INFRA_5G is supported by firmware"); |
| fset |= WIFI_FEATURE_INFRA_5G; |
| } |
| #ifdef WLAN_FEATURE_P2P |
| if ((wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) && |
| (wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_GO))) { |
| hdd_debug("WiFi-Direct is supported by driver"); |
| fset |= WIFI_FEATURE_P2P; |
| } |
| #endif |
| fset |= WIFI_FEATURE_SOFT_AP; |
| |
| /* HOTSPOT is a supplicant feature, enable it by default */ |
| fset |= WIFI_FEATURE_HOTSPOT; |
| |
| if (ucfg_extscan_get_enable(hdd_ctx->psoc) && |
| sme_is_feature_supported_by_fw(EXTENDED_SCAN)) { |
| hdd_debug("EXTScan is supported by firmware"); |
| fset |= WIFI_FEATURE_EXTSCAN | WIFI_FEATURE_HAL_EPNO; |
| } |
| if (wlan_hdd_nan_is_supported(hdd_ctx)) { |
| hdd_debug("NAN is supported by firmware"); |
| fset |= WIFI_FEATURE_NAN; |
| } |
| |
| ucfg_mlme_get_fine_time_meas_cap(hdd_ctx->psoc, &fine_time_meas_cap); |
| |
| if (sme_is_feature_supported_by_fw(RTT) && |
| rtt_is_enabled(fine_time_meas_cap)) { |
| hdd_debug("RTT is supported by firmware and driver: %x", |
| fine_time_meas_cap); |
| fset |= WIFI_FEATURE_D2D_RTT; |
| fset |= WIFI_FEATURE_D2AP_RTT; |
| } |
| #ifdef FEATURE_WLAN_SCAN_PNO |
| if (ucfg_scan_get_pno_scan_support(hdd_ctx->psoc) && |
| sme_is_feature_supported_by_fw(PNO)) { |
| hdd_debug("PNO is supported by firmware"); |
| fset |= WIFI_FEATURE_PNO; |
| } |
| #endif |
| fset |= WIFI_FEATURE_ADDITIONAL_STA; |
| #ifdef FEATURE_WLAN_TDLS |
| cfg_tdls_get_support_enable(hdd_ctx->psoc, &bvalue); |
| if ((bvalue) && sme_is_feature_supported_by_fw(TDLS)) { |
| hdd_debug("TDLS is supported by firmware"); |
| fset |= WIFI_FEATURE_TDLS; |
| } |
| |
| cfg_tdls_get_off_channel_enable(hdd_ctx->psoc, &bvalue); |
| if (sme_is_feature_supported_by_fw(TDLS) && |
| bvalue && sme_is_feature_supported_by_fw(TDLS_OFF_CHANNEL)) { |
| hdd_debug("TDLS off-channel is supported by firmware"); |
| fset |= WIFI_FEATURE_TDLS_OFFCHANNEL; |
| } |
| #endif |
| fset |= WIFI_FEATURE_AP_STA; |
| fset |= WIFI_FEATURE_RSSI_MONITOR; |
| fset |= WIFI_FEATURE_TX_TRANSMIT_POWER; |
| fset |= WIFI_FEATURE_SET_TX_POWER_LIMIT; |
| fset |= WIFI_FEATURE_CONFIG_NDO; |
| |
| if (hdd_link_layer_stats_supported()) |
| fset |= WIFI_FEATURE_LINK_LAYER_STATS; |
| |
| if (hdd_roaming_supported(hdd_ctx)) |
| fset |= WIFI_FEATURE_CONTROL_ROAMING; |
| |
| if (hdd_scan_random_mac_addr_supported()) |
| fset |= WIFI_FEATURE_SCAN_RAND; |
| |
| if (hdd_is_wlm_latency_manager_supported(hdd_ctx)) |
| fset |= WIFI_FEATURE_SET_LATENCY_MODE; |
| |
| if (hdd_dynamic_mac_addr_supported(hdd_ctx)) |
| fset |= WIFI_FEATURE_DYNAMIC_SET_MAC; |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(fset) + |
| NLMSG_HDRLEN); |
| if (!skb) { |
| hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| return -EINVAL; |
| } |
| hdd_debug("Supported Features : 0x%x", fset); |
| if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_FEATURE_SET, fset)) { |
| hdd_err("nla put fail"); |
| goto nla_put_failure; |
| } |
| ret = cfg80211_vendor_cmd_reply(skb); |
| return ret; |
| nla_put_failure: |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_get_supported_features() - get supported features |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the data to be passed via vendor interface |
| * @data_len:Length of the data to be passed |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int |
| wlan_hdd_cfg80211_get_supported_features(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_supported_features(wiphy, wdev, |
| data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_set_scanning_mac_oui() - set scan MAC |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the data to be passed via vendor interface |
| * @data_len:Length of the data to be passed |
| * |
| * Set the MAC address that is to be used for scanning. |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int |
| __wlan_hdd_cfg80211_set_scanning_mac_oui(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct scan_mac_oui scan_mac_oui = { {0} }; |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI_MAX + 1]; |
| QDF_STATUS status; |
| int ret, len; |
| struct net_device *ndev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); |
| mac_handle_t mac_handle; |
| bool mac_spoofing_enabled; |
| |
| hdd_enter_dev(wdev->netdev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| mac_spoofing_enabled = ucfg_scan_is_mac_spoofing_enabled(hdd_ctx->psoc); |
| if (!mac_spoofing_enabled) { |
| hdd_debug("MAC address spoofing is not enabled"); |
| return -ENOTSUPP; |
| } |
| |
| /* |
| * audit note: it is ok to pass a NULL policy here since only |
| * one attribute is parsed and it is explicitly validated |
| */ |
| if (wlan_cfg80211_nla_parse(tb, |
| QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI_MAX, |
| data, data_len, NULL)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI]) { |
| hdd_err("attr mac oui failed"); |
| return -EINVAL; |
| } |
| |
| len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI]); |
| if (len != sizeof(scan_mac_oui.oui)) { |
| hdd_err("attr mac oui invalid size %d expected %zu", |
| len, sizeof(scan_mac_oui.oui)); |
| return -EINVAL; |
| } |
| |
| nla_memcpy(scan_mac_oui.oui, |
| tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI], |
| sizeof(scan_mac_oui.oui)); |
| |
| /* populate rest of scan_mac_oui for mac addr randomization */ |
| scan_mac_oui.vdev_id = adapter->vdev_id; |
| scan_mac_oui.enb_probe_req_sno_randomization = true; |
| |
| hdd_debug("Oui (%02x:%02x:%02x), vdev_id = %d", |
| scan_mac_oui.oui[0], scan_mac_oui.oui[1], |
| scan_mac_oui.oui[2], scan_mac_oui.vdev_id); |
| |
| hdd_update_ie_whitelist_attr(&scan_mac_oui.ie_whitelist, hdd_ctx); |
| |
| mac_handle = hdd_ctx->mac_handle; |
| status = sme_set_scanning_mac_oui(mac_handle, &scan_mac_oui); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("sme_set_scanning_mac_oui failed(err=%d)", status); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_scanning_mac_oui() - set scan MAC |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the data to be passed via vendor interface |
| * @data_len:Length of the data to be passed |
| * |
| * Set the MAC address that is to be used for scanning. This is an |
| * SSR-protecting wrapper function. |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int |
| wlan_hdd_cfg80211_set_scanning_mac_oui(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_scanning_mac_oui(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_feature() - Set the bitmask for supported features |
| * @feature_flags: pointer to the byte array of features. |
| * @feature: Feature to be turned ON in the byte array. |
| * |
| * Return: None |
| * |
| * This is called to turn ON or SET the feature flag for the requested feature. |
| **/ |
| #define NUM_BITS_IN_BYTE 8 |
| static void wlan_hdd_cfg80211_set_feature(uint8_t *feature_flags, |
| uint8_t feature) |
| { |
| uint32_t index; |
| uint8_t bit_mask; |
| |
| index = feature / NUM_BITS_IN_BYTE; |
| bit_mask = 1 << (feature % NUM_BITS_IN_BYTE); |
| feature_flags[index] |= bit_mask; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_get_features() - Get the Driver Supported features |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the data to be passed via vendor interface |
| * @data_len:Length of the data to be passed |
| * |
| * This is called when wlan driver needs to send supported feature set to |
| * supplicant upon a request/query from the supplicant. |
| * |
| * Return: Return the Success or Failure code. |
| **/ |
| #define MAX_CONCURRENT_CHAN_ON_24G 2 |
| #define MAX_CONCURRENT_CHAN_ON_5G 2 |
| static int |
| __wlan_hdd_cfg80211_get_features(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct sk_buff *skb = NULL; |
| uint32_t dbs_capability = 0; |
| bool one_by_one_dbs, two_by_two_dbs; |
| bool value, twt_req, twt_res; |
| QDF_STATUS ret = QDF_STATUS_E_FAILURE; |
| QDF_STATUS status; |
| int ret_val; |
| |
| uint8_t feature_flags[(NUM_QCA_WLAN_VENDOR_FEATURES + 7) / 8] = {0}; |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| |
| hdd_enter_dev(wdev->netdev); |
| |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| return ret_val; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| if (roaming_offload_enabled(hdd_ctx)) { |
| hdd_debug("Key Mgmt Offload is supported"); |
| wlan_hdd_cfg80211_set_feature(feature_flags, |
| QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD); |
| } |
| |
| wlan_hdd_cfg80211_set_feature(feature_flags, |
| QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY); |
| if (policy_mgr_is_scan_simultaneous_capable(hdd_ctx->psoc)) |
| wlan_hdd_cfg80211_set_feature(feature_flags, |
| QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS); |
| |
| if (policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc)) |
| wlan_hdd_cfg80211_set_feature(feature_flags, |
| QCA_WLAN_VENDOR_FEATURE_CONCURRENT_BAND_SESSIONS); |
| |
| if (wma_is_p2p_lo_capable()) |
| wlan_hdd_cfg80211_set_feature(feature_flags, |
| QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD); |
| |
| value = false; |
| status = ucfg_mlme_get_oce_sta_enabled_info(hdd_ctx->psoc, &value); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("could not get OCE STA enable info"); |
| if (value) |
| wlan_hdd_cfg80211_set_feature(feature_flags, |
| QCA_WLAN_VENDOR_FEATURE_OCE_STA); |
| |
| value = false; |
| status = ucfg_mlme_get_oce_sap_enabled_info(hdd_ctx->psoc, &value); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("could not get OCE SAP enable info"); |
| if (value) |
| wlan_hdd_cfg80211_set_feature(feature_flags, |
| QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON); |
| |
| value = false; |
| status = ucfg_mlme_get_adaptive11r_enabled(hdd_ctx->psoc, &value); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("could not get FT-Adaptive 11R info"); |
| if (value) { |
| hdd_debug("FT-Adaptive 11R is Enabled"); |
| wlan_hdd_cfg80211_set_feature(feature_flags, |
| QCA_WLAN_VENDOR_FEATURE_ADAPTIVE_11R); |
| } |
| |
| hdd_get_twt_requestor(hdd_ctx->psoc, &twt_req); |
| hdd_get_twt_responder(hdd_ctx->psoc, &twt_res); |
| hdd_debug("twt_req:%d twt_res:%d", twt_req, twt_res); |
| |
| if (twt_req || twt_res) { |
| wlan_hdd_cfg80211_set_feature(feature_flags, |
| QCA_WLAN_VENDOR_FEATURE_TWT); |
| |
| wlan_hdd_cfg80211_set_feature( |
| feature_flags, |
| QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT); |
| } |
| |
| /* Check the kernel version for upstream commit aced43ce780dc5 that |
| * has support for processing user cell_base hints when wiphy is |
| * self managed or check the backport flag for the same. |
| */ |
| #if defined CFG80211_USER_HINT_CELL_BASE_SELF_MANAGED || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) |
| wlan_hdd_cfg80211_set_feature(feature_flags, |
| QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY); |
| #endif |
| |
| if (wlan_hdd_thermal_config_support()) |
| wlan_hdd_cfg80211_set_feature(feature_flags, |
| QCA_WLAN_VENDOR_FEATURE_THERMAL_CONFIG); |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(feature_flags) + |
| NLMSG_HDRLEN); |
| |
| if (!skb) { |
| hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| return -ENOMEM; |
| } |
| |
| if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS, |
| sizeof(feature_flags), feature_flags)) |
| goto nla_put_failure; |
| |
| ret = policy_mgr_get_dbs_hw_modes(hdd_ctx->psoc, |
| &one_by_one_dbs, &two_by_two_dbs); |
| if (QDF_STATUS_SUCCESS == ret) { |
| if (one_by_one_dbs) |
| dbs_capability = DRV_DBS_CAPABILITY_1X1; |
| |
| if (two_by_two_dbs) |
| dbs_capability = DRV_DBS_CAPABILITY_2X2; |
| |
| if (!one_by_one_dbs && !two_by_two_dbs) |
| dbs_capability = DRV_DBS_CAPABILITY_DISABLED; |
| } else { |
| hdd_err("wma_get_dbs_hw_mode failed"); |
| dbs_capability = DRV_DBS_CAPABILITY_DISABLED; |
| } |
| |
| hdd_debug("dbs_capability is %d", dbs_capability); |
| |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA, |
| dbs_capability)) |
| goto nla_put_failure; |
| |
| |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND, |
| MAX_CONCURRENT_CHAN_ON_24G)) |
| goto nla_put_failure; |
| |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND, |
| MAX_CONCURRENT_CHAN_ON_5G)) |
| goto nla_put_failure; |
| |
| return cfg80211_vendor_cmd_reply(skb); |
| |
| nla_put_failure: |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_get_features() - Get the Driver Supported features |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the data to be passed via vendor interface |
| * @data_len:Length of the data to be passed |
| * |
| * This is called when wlan driver needs to send supported feature set to |
| * supplicant upon a request/query from the supplicant. |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int |
| wlan_hdd_cfg80211_get_features(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_features(wiphy, wdev, data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| #define PARAM_NUM_NW \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS |
| #define PARAM_SET_BSSID \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID |
| #define PARAM_SET_BSSID_HINT \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT |
| #define PARAM_SSID_LIST QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST |
| #define PARAM_LIST_SSID QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID |
| #define MAX_ROAMING_PARAM \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX |
| #define PARAM_NUM_BSSID \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID |
| #define PARAM_BSSID_PREFS \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS |
| #define PARAM_ROAM_BSSID \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID |
| #define PARAM_RSSI_MODIFIER \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER |
| #define PARAMS_NUM_BSSID \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID |
| #define PARAM_BSSID_PARAMS \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS |
| #define PARAM_A_BAND_BOOST_THLD \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD |
| #define PARAM_A_BAND_PELT_THLD \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD |
| #define PARAM_A_BAND_BOOST_FACTOR \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR |
| #define PARAM_A_BAND_PELT_FACTOR \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR |
| #define PARAM_A_BAND_MAX_BOOST \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST |
| #define PARAM_ROAM_HISTERESYS \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS |
| #define PARAM_RSSI_TRIGGER \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER |
| #define PARAM_ROAM_ENABLE \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE |
| #define PARAM_ROAM_CONTROL_CONFIG \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL |
| #define PARAM_FREQ_LIST_SCHEME \ |
| QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME |
| #define PARAM_FREQ_LIST_SCHEME_MAX \ |
| QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_SCHEME_MAX |
| #define PARAM_SCAN_FREQ_LIST \ |
| QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST |
| #define PARAM_SCAN_FREQ_LIST_TYPE \ |
| QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_TYPE |
| #define PARAM_CAND_SEL_CRITERIA_MAX \ |
| QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE_MAX |
| #define PARAM_CAND_SEL_SCORE_RSSI \ |
| QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_RSSI |
| |
| const struct nla_policy wlan_hdd_set_roam_param_policy[ |
| MAX_ROAMING_PARAM + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID] = {.type = NLA_U32}, |
| [PARAM_NUM_NW] = {.type = NLA_U32}, |
| [PARAM_SSID_LIST] = { .type = NLA_NESTED }, |
| [PARAM_LIST_SSID] = { .type = NLA_BINARY }, |
| [PARAM_A_BAND_BOOST_FACTOR] = {.type = NLA_U32}, |
| [PARAM_A_BAND_PELT_FACTOR] = {.type = NLA_U32}, |
| [PARAM_A_BAND_MAX_BOOST] = {.type = NLA_U32}, |
| [PARAM_ROAM_HISTERESYS] = {.type = NLA_S32}, |
| [PARAM_A_BAND_BOOST_THLD] = {.type = NLA_S32}, |
| [PARAM_A_BAND_PELT_THLD] = {.type = NLA_S32}, |
| [PARAM_RSSI_TRIGGER] = {.type = NLA_U32}, |
| [PARAM_ROAM_ENABLE] = { .type = NLA_S32}, |
| [PARAM_BSSID_PREFS] = { .type = NLA_NESTED }, |
| [PARAM_NUM_BSSID] = {.type = NLA_U32}, |
| [PARAM_RSSI_MODIFIER] = {.type = NLA_U32}, |
| [PARAM_BSSID_PARAMS] = {.type = NLA_NESTED}, |
| [PARAMS_NUM_BSSID] = {.type = NLA_U32}, |
| [PARAM_ROAM_BSSID] = VENDOR_NLA_POLICY_MAC_ADDR, |
| [PARAM_SET_BSSID] = VENDOR_NLA_POLICY_MAC_ADDR, |
| [PARAM_SET_BSSID_HINT] = {.type = NLA_FLAG}, |
| [PARAM_ROAM_CONTROL_CONFIG] = {.type = NLA_NESTED}, |
| }; |
| |
| /** |
| * hdd_set_white_list() - parse white list |
| * @hdd_ctx: HDD context |
| * @rso_config: rso config |
| * @tb: list of attributes |
| * @vdev_id: vdev id |
| * |
| * Return: 0 on success; error number on failure |
| */ |
| static int hdd_set_white_list(struct hdd_context *hdd_ctx, |
| struct rso_config_params *rso_config, |
| struct nlattr **tb, uint8_t vdev_id) |
| { |
| int rem, i; |
| uint32_t buf_len = 0, count; |
| struct nlattr *tb2[MAX_ROAMING_PARAM + 1]; |
| struct nlattr *curr_attr = NULL; |
| mac_handle_t mac_handle; |
| |
| i = 0; |
| if (tb[PARAM_NUM_NW]) { |
| count = nla_get_u32(tb[PARAM_NUM_NW]); |
| } else { |
| hdd_err("Number of networks is not provided"); |
| goto fail; |
| } |
| |
| if (count && tb[PARAM_SSID_LIST]) { |
| nla_for_each_nested(curr_attr, |
| tb[PARAM_SSID_LIST], rem) { |
| if (i == MAX_SSID_ALLOWED_LIST) { |
| hdd_err("Excess MAX_SSID_ALLOWED_LIST"); |
| goto fail; |
| } |
| if (wlan_cfg80211_nla_parse(tb2, |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX, |
| nla_data(curr_attr), |
| nla_len(curr_attr), |
| wlan_hdd_set_roam_param_policy)) { |
| hdd_err("nla_parse failed"); |
| goto fail; |
| } |
| /* Parse and Fetch allowed SSID list*/ |
| if (!tb2[PARAM_LIST_SSID]) { |
| hdd_err("attr allowed ssid failed"); |
| goto fail; |
| } |
| buf_len = nla_len(tb2[PARAM_LIST_SSID]); |
| /* |
| * Upper Layers include a null termination |
| * character. Check for the actual permissible |
| * length of SSID and also ensure not to copy |
| * the NULL termination character to the driver |
| * buffer. |
| */ |
| if (buf_len > 1 && |
| ((buf_len - 1) <= WLAN_SSID_MAX_LEN)) { |
| nla_memcpy(rso_config->ssid_allowed_list[i].ssid, |
| tb2[PARAM_LIST_SSID], buf_len - 1); |
| rso_config->ssid_allowed_list[i].length = buf_len - 1; |
| hdd_debug("SSID[%d]: %.*s,length = %d", |
| i, |
| rso_config->ssid_allowed_list[i].length, |
| rso_config->ssid_allowed_list[i].ssid, |
| rso_config->ssid_allowed_list[i].length); |
| i++; |
| } else { |
| hdd_err("Invalid buffer length"); |
| } |
| } |
| } |
| |
| if (i != count) { |
| hdd_err("Invalid number of SSIDs i = %d, count = %d", i, count); |
| goto fail; |
| } |
| |
| rso_config->num_ssid_allowed_list = i; |
| hdd_debug("Num of Allowed SSID %d", rso_config->num_ssid_allowed_list); |
| mac_handle = hdd_ctx->mac_handle; |
| sme_update_roam_params(mac_handle, vdev_id, rso_config, |
| REASON_ROAM_SET_SSID_ALLOWED); |
| return 0; |
| |
| fail: |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_set_bssid_prefs() - parse set bssid prefs |
| * @hdd_ctx: HDD context |
| * @rso_config: rso config |
| * @tb: list of attributes |
| * @vdev_id: vdev id |
| * |
| * Return: 0 on success; error number on failure |
| */ |
| static int hdd_set_bssid_prefs(struct hdd_context *hdd_ctx, |
| struct rso_config_params *rso_config, |
| struct nlattr **tb, uint8_t vdev_id) |
| { |
| int rem, i; |
| uint32_t count; |
| struct nlattr *tb2[MAX_ROAMING_PARAM + 1]; |
| struct nlattr *curr_attr = NULL; |
| mac_handle_t mac_handle; |
| |
| /* Parse and fetch number of preferred BSSID */ |
| if (!tb[PARAM_NUM_BSSID]) { |
| hdd_err("attr num of preferred bssid failed"); |
| goto fail; |
| } |
| count = nla_get_u32(tb[PARAM_NUM_BSSID]); |
| if (count > MAX_BSSID_FAVORED) { |
| hdd_err("Preferred BSSID count %u exceeds max %u", |
| count, MAX_BSSID_FAVORED); |
| goto fail; |
| } |
| hdd_debug("Num of Preferred BSSID (%d)", count); |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS]) { |
| hdd_err("attr Preferred BSSID failed"); |
| goto fail; |
| } |
| |
| i = 0; |
| nla_for_each_nested(curr_attr, |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS], |
| rem) { |
| if (i == count) { |
| hdd_warn("Ignoring excess Preferred BSSID"); |
| break; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb2, |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX, |
| nla_data(curr_attr), |
| nla_len(curr_attr), |
| wlan_hdd_set_roam_param_policy)) { |
| hdd_err("nla_parse failed"); |
| goto fail; |
| } |
| /* Parse and fetch MAC address */ |
| if (!tb2[PARAM_ROAM_BSSID]) { |
| hdd_err("attr mac address failed"); |
| goto fail; |
| } |
| nla_memcpy(rso_config->bssid_favored[i].bytes, |
| tb2[PARAM_ROAM_BSSID], |
| QDF_MAC_ADDR_SIZE); |
| hdd_debug(QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(rso_config->bssid_favored[i].bytes)); |
| /* Parse and fetch preference factor*/ |
| if (!tb2[PARAM_RSSI_MODIFIER]) { |
| hdd_err("BSSID Preference score failed"); |
| goto fail; |
| } |
| rso_config->bssid_favored_factor[i] = nla_get_u32( |
| tb2[PARAM_RSSI_MODIFIER]); |
| hdd_debug("BSSID Preference score (%d)", |
| rso_config->bssid_favored_factor[i]); |
| i++; |
| } |
| if (i < count) |
| hdd_warn("Num Preferred BSSID %u less than expected %u", |
| i, count); |
| |
| rso_config->num_bssid_favored = i; |
| mac_handle = hdd_ctx->mac_handle; |
| sme_update_roam_params(mac_handle, vdev_id, |
| rso_config, REASON_ROAM_SET_FAVORED_BSSID); |
| |
| return 0; |
| |
| fail: |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_set_blacklist_bssid() - parse set blacklist bssid |
| * @hdd_ctx: HDD context |
| * @rso_config: roam params |
| * @tb: list of attributes |
| * @vdev_id: vdev id |
| * |
| * Return: 0 on success; error number on failure |
| */ |
| static int hdd_set_blacklist_bssid(struct hdd_context *hdd_ctx, |
| struct rso_config_params *rso_config, |
| struct nlattr **tb, |
| uint8_t vdev_id) |
| { |
| int rem, i; |
| uint32_t count; |
| uint8_t j = 0; |
| struct nlattr *tb2[MAX_ROAMING_PARAM + 1]; |
| struct nlattr *curr_attr = NULL; |
| struct qdf_mac_addr *black_list_bssid; |
| mac_handle_t mac_handle; |
| |
| /* Parse and fetch number of blacklist BSSID */ |
| if (!tb[PARAMS_NUM_BSSID]) { |
| hdd_err("attr num of blacklist bssid failed"); |
| goto fail; |
| } |
| count = nla_get_u32(tb[PARAMS_NUM_BSSID]); |
| if (count > MAX_BSSID_AVOID_LIST) { |
| hdd_err("Blacklist BSSID count %u exceeds max %u", |
| count, MAX_BSSID_AVOID_LIST); |
| goto fail; |
| } |
| hdd_debug("Num of blacklist BSSID (%d)", count); |
| black_list_bssid = qdf_mem_malloc(sizeof(*black_list_bssid) * |
| MAX_BSSID_AVOID_LIST); |
| if (!black_list_bssid) |
| goto fail; |
| |
| i = 0; |
| if (count && tb[PARAM_BSSID_PARAMS]) { |
| nla_for_each_nested(curr_attr, |
| tb[PARAM_BSSID_PARAMS], |
| rem) { |
| if (i == count) { |
| hdd_warn("Ignoring excess Blacklist BSSID"); |
| break; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb2, |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX, |
| nla_data(curr_attr), |
| nla_len(curr_attr), |
| wlan_hdd_set_roam_param_policy)) { |
| hdd_err("nla_parse failed"); |
| qdf_mem_free(black_list_bssid); |
| goto fail; |
| } |
| /* Parse and fetch MAC address */ |
| if (!tb2[PARAM_SET_BSSID]) { |
| hdd_err("attr blacklist addr failed"); |
| qdf_mem_free(black_list_bssid); |
| goto fail; |
| } |
| if (tb2[PARAM_SET_BSSID_HINT]) { |
| struct reject_ap_info ap_info; |
| |
| qdf_mem_zero(&ap_info, |
| sizeof(struct reject_ap_info)); |
| nla_memcpy(ap_info.bssid.bytes, |
| tb2[PARAM_SET_BSSID], |
| QDF_MAC_ADDR_SIZE); |
| ap_info.reject_ap_type = USERSPACE_AVOID_TYPE; |
| ap_info.reject_reason = |
| REASON_USERSPACE_AVOID_LIST; |
| ap_info.source = ADDED_BY_DRIVER; |
| |
| /* This BSSID is avoided and not blacklisted */ |
| ucfg_blm_add_bssid_to_reject_list(hdd_ctx->pdev, |
| &ap_info); |
| i++; |
| continue; |
| } |
| nla_memcpy(black_list_bssid[j].bytes, |
| tb2[PARAM_SET_BSSID], QDF_MAC_ADDR_SIZE); |
| hdd_debug(QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(black_list_bssid[j].bytes)); |
| i++; |
| j++; |
| } |
| } |
| |
| if (i < count) |
| hdd_warn("Num Blacklist BSSID %u less than expected %u", |
| i, count); |
| |
| /* Send the blacklist to the blacklist mgr component */ |
| ucfg_blm_add_userspace_black_list(hdd_ctx->pdev, black_list_bssid, j); |
| qdf_mem_free(black_list_bssid); |
| mac_handle = hdd_ctx->mac_handle; |
| sme_update_roam_params(mac_handle, vdev_id, |
| rso_config, REASON_ROAM_SET_BLACKLIST_BSSID); |
| |
| return 0; |
| fail: |
| return -EINVAL; |
| } |
| |
| static const struct nla_policy |
| roam_scan_freq_list_scheme_policy[PARAM_FREQ_LIST_SCHEME_MAX + 1] = { |
| [PARAM_SCAN_FREQ_LIST_TYPE] = {.type = NLA_U32}, |
| [PARAM_SCAN_FREQ_LIST] = {.type = NLA_NESTED}, |
| }; |
| |
| /** |
| * hdd_send_roam_scan_channel_freq_list_to_sme() - Send control roam scan freqs |
| * @hdd_ctx: HDD context |
| * @vdev_id: vdev id |
| * @tb: Nested attribute carrying frequency list scheme |
| * |
| * Extracts the frequency list and frequency list type from the frequency |
| * list scheme and send the frequencies to SME. |
| * |
| * Return: QDF_STATUS |
| */ |
| static QDF_STATUS |
| hdd_send_roam_scan_channel_freq_list_to_sme(struct hdd_context *hdd_ctx, |
| uint8_t vdev_id, struct nlattr *tb) |
| { |
| QDF_STATUS status; |
| struct nlattr *tb2[PARAM_FREQ_LIST_SCHEME_MAX + 1], *curr_attr; |
| uint8_t num_chan = 0; |
| uint32_t freq_list[SIR_MAX_SUPPORTED_CHANNEL_LIST] = {0}; |
| uint32_t list_type; |
| mac_handle_t mac_handle = hdd_ctx->mac_handle; |
| int rem; |
| |
| if (wlan_cfg80211_nla_parse_nested(tb2, PARAM_FREQ_LIST_SCHEME_MAX, |
| tb, |
| roam_scan_freq_list_scheme_policy)) { |
| hdd_err("nla_parse failed"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (!tb2[PARAM_SCAN_FREQ_LIST] || !tb2[PARAM_SCAN_FREQ_LIST_TYPE]) { |
| hdd_err("ROAM_CONTROL_SCAN_FREQ_LIST or type are not present"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| list_type = nla_get_u32(tb2[PARAM_SCAN_FREQ_LIST_TYPE]); |
| if (list_type != QCA_PREFERRED_SCAN_FREQ_LIST && |
| list_type != QCA_SPECIFIC_SCAN_FREQ_LIST) { |
| hdd_err("Invalid freq list type received: %u", list_type); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| nla_for_each_nested(curr_attr, tb2[PARAM_SCAN_FREQ_LIST], rem) { |
| if (num_chan >= SIR_MAX_SUPPORTED_CHANNEL_LIST) { |
| hdd_err("number of channels (%d) supported exceeded max (%d)", |
| num_chan, SIR_MAX_SUPPORTED_CHANNEL_LIST); |
| return QDF_STATUS_E_INVAL; |
| } |
| num_chan++; |
| } |
| num_chan = 0; |
| |
| nla_for_each_nested(curr_attr, tb2[PARAM_SCAN_FREQ_LIST], rem) { |
| if (nla_len(curr_attr) != sizeof(uint32_t)) { |
| hdd_err("len is not correct for frequency %d", |
| num_chan); |
| return QDF_STATUS_E_INVAL; |
| } |
| freq_list[num_chan++] = nla_get_u32(curr_attr); |
| } |
| |
| status = sme_update_roam_scan_freq_list(mac_handle, vdev_id, freq_list, |
| num_chan, list_type); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("Failed to update channel list information"); |
| |
| return status; |
| } |
| |
| static const struct nla_policy |
| roam_control_policy[QCA_ATTR_ROAM_CONTROL_MAX + 1] = { |
| [QCA_ATTR_ROAM_CONTROL_ENABLE] = {.type = NLA_U8}, |
| [QCA_ATTR_ROAM_CONTROL_STATUS] = {.type = NLA_U8}, |
| [PARAM_FREQ_LIST_SCHEME] = {.type = NLA_NESTED}, |
| [QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD] = {.type = NLA_U32}, |
| [QCA_ATTR_ROAM_CONTROL_CLEAR_ALL] = {.type = NLA_FLAG}, |
| [QCA_ATTR_ROAM_CONTROL_TRIGGERS] = {.type = NLA_U32}, |
| [QCA_ATTR_ROAM_CONTROL_SELECTION_CRITERIA] = {.type = NLA_NESTED}, |
| [QCA_ATTR_ROAM_CONTROL_SCAN_PERIOD] = {.type = NLA_U32}, |
| [QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME] = {.type = NLA_U32}, |
| [QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD] = {.type = NLA_U32}, |
| [QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD] = {.type = NLA_U32}, |
| [QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_2P4GHZ] = { |
| .type = NLA_U32}, |
| [QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_5GHZ] = { |
| .type = NLA_U32}, |
| [QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_6GHZ] = { |
| .type = NLA_U32}, |
| [QCA_ATTR_ROAM_CONTROL_USER_REASON] = {.type = NLA_U32}, |
| [QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS] = {.type = NLA_U32}, |
| [QCA_ATTR_ROAM_CONTROL_BAND_MASK] = {.type = NLA_U32}, |
| }; |
| |
| /** |
| * hdd_send_roam_full_scan_period_to_sme() - Send full roam scan period to SME |
| * @hdd_ctx: HDD context |
| * @vdev_id: vdev id |
| * @full_roam_scan_period: Idle period in seconds between two successive |
| * full channel roam scans |
| * @check_and_update: If this is true/set, update the value only if the current |
| * configured value is not same as global value read from |
| * ini param. This is to give priority to the user configured |
| * values and retain the value, if updated already. |
| * If this is not set, update the value without any check. |
| * |
| * Validate the full roam scan period and send it to firmware |
| * |
| * Return: QDF_STATUS |
| */ |
| static QDF_STATUS |
| hdd_send_roam_full_scan_period_to_sme(struct hdd_context *hdd_ctx, |
| uint8_t vdev_id, |
| uint32_t full_roam_scan_period, |
| bool check_and_update) |
| { |
| QDF_STATUS status; |
| uint32_t full_roam_scan_period_current, full_roam_scan_period_global; |
| |
| if (!ucfg_mlme_validate_full_roam_scan_period(full_roam_scan_period)) |
| return QDF_STATUS_E_INVAL; |
| |
| hdd_debug("Received Command to Set full roam scan period = %u", |
| full_roam_scan_period); |
| |
| status = sme_get_full_roam_scan_period(hdd_ctx->mac_handle, vdev_id, |
| &full_roam_scan_period_current); |
| if (QDF_IS_STATUS_ERROR(status)) |
| return status; |
| |
| full_roam_scan_period_global = |
| sme_get_full_roam_scan_period_global(hdd_ctx->mac_handle); |
| if (check_and_update && |
| full_roam_scan_period_current != full_roam_scan_period_global) { |
| hdd_debug("Full roam scan period is already updated, value: %u", |
| full_roam_scan_period_current); |
| return QDF_STATUS_SUCCESS; |
| } |
| status = sme_update_full_roam_scan_period(hdd_ctx->mac_handle, vdev_id, |
| full_roam_scan_period); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("Failed to set full scan period"); |
| |
| return status; |
| } |
| |
| /** |
| * wlan_hdd_convert_control_roam_trigger_reason_bitmap - Convert the |
| * vendor specific reason code to internal reason code. |
| * @trigger_reason_bitmap: Vendor specific roam trigger bitmap |
| * |
| * Return: Internal roam trigger bitmap |
| */ |
| static uint32_t |
| wlan_hdd_convert_control_roam_trigger_bitmap(uint32_t trigger_reason_bitmap) |
| { |
| uint32_t drv_trigger_bitmap = 0, all_bitmap; |
| |
| /* Enable the complete trigger bitmap when all bits are set in |
| * the control config bitmap |
| */ |
| all_bitmap = (QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN << 1) - 1; |
| if (trigger_reason_bitmap == all_bitmap) |
| return BIT(ROAM_TRIGGER_REASON_MAX) - 1; |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_PER) |
| drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_PER); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_BEACON_MISS) |
| drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_BMISS); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_POOR_RSSI) |
| drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_LOW_RSSI); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_BETTER_RSSI) |
| drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_HIGH_RSSI); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_PERIODIC) |
| drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_PERIODIC); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_DENSE) |
| drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_DENSE); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_BTM) |
| drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_BTM); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_BSS_LOAD) |
| drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_BSS_LOAD); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_USER_TRIGGER) |
| drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_FORCED); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_DEAUTH) |
| drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_DEAUTH); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_IDLE) |
| drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_IDLE); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_TX_FAILURES) |
| drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_STA_KICKOUT); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN) |
| drv_trigger_bitmap |= BIT(ROAM_TRIGGER_REASON_BACKGROUND); |
| |
| return drv_trigger_bitmap; |
| } |
| |
| /** |
| * wlan_hdd_convert_control_roam_scan_scheme_bitmap() - Convert the |
| * vendor specific roam scan scheme for roam triggers to internal roam trigger |
| * bitmap for partial scan. |
| * @trigger_reason_bitmap: Vendor specific roam trigger bitmap |
| * |
| * Return: Internal roam scan scheme bitmap |
| */ |
| static uint32_t |
| wlan_hdd_convert_control_roam_scan_scheme_bitmap(uint32_t trigger_reason_bitmap) |
| { |
| uint32_t drv_scan_scheme_bitmap = 0; |
| |
| /* |
| * Partial scan scheme override over default scan scheme only for |
| * the PER, BMISS, Low RSSI, BTM, BSS_LOAD Triggers |
| */ |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_PER) |
| drv_scan_scheme_bitmap |= BIT(ROAM_TRIGGER_REASON_PER); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_BEACON_MISS) |
| drv_scan_scheme_bitmap |= BIT(ROAM_TRIGGER_REASON_BMISS); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_POOR_RSSI) |
| drv_scan_scheme_bitmap |= BIT(ROAM_TRIGGER_REASON_LOW_RSSI); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_BTM) |
| drv_scan_scheme_bitmap |= BIT(ROAM_TRIGGER_REASON_BTM); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_BSS_LOAD) |
| drv_scan_scheme_bitmap |= BIT(ROAM_TRIGGER_REASON_BSS_LOAD); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_USER_TRIGGER) |
| drv_scan_scheme_bitmap |= BIT(ROAM_TRIGGER_REASON_FORCED); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_DEAUTH) |
| drv_scan_scheme_bitmap |= BIT(ROAM_TRIGGER_REASON_DEAUTH); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_IDLE) |
| drv_scan_scheme_bitmap |= BIT(ROAM_TRIGGER_REASON_IDLE); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_TX_FAILURES) |
| drv_scan_scheme_bitmap |= BIT(ROAM_TRIGGER_REASON_STA_KICKOUT); |
| |
| if (trigger_reason_bitmap & QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN) |
| drv_scan_scheme_bitmap |= BIT(ROAM_TRIGGER_REASON_BACKGROUND); |
| |
| return drv_scan_scheme_bitmap; |
| } |
| |
| /** |
| * hdd_send_roam_triggers_to_sme() - Send roam trigger bitmap to SME |
| * @hdd_ctx: HDD context |
| * @vdev_id: vdev id |
| * @roam_trigger_bitmap: Vendor configured roam trigger bitmap to be configured |
| * to firmware |
| * |
| * Send the roam trigger bitmap received to SME |
| * |
| * Return: QDF_STATUS |
| */ |
| static QDF_STATUS |
| hdd_send_roam_triggers_to_sme(struct hdd_context *hdd_ctx, |
| uint8_t vdev_id, |
| uint32_t roam_trigger_bitmap) |
| { |
| QDF_STATUS status; |
| struct wlan_roam_triggers triggers; |
| struct hdd_adapter *adapter; |
| |
| adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); |
| if (!adapter) { |
| hdd_err("adapter NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| if (adapter->device_mode != QDF_STA_MODE) { |
| hdd_err("Roam trigger bitmap supported only in STA mode"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| triggers.vdev_id = vdev_id; |
| triggers.trigger_bitmap = |
| wlan_hdd_convert_control_roam_trigger_bitmap(roam_trigger_bitmap); |
| hdd_debug("trigger bitmap: 0x%x converted trigger_bitmap: 0x%x", |
| roam_trigger_bitmap, triggers.trigger_bitmap); |
| /* |
| * In standalone STA, if this vendor command is received between |
| * ROAM_START and roam synch indication, it is better to reject |
| * roam disable since driver would send vdev_params command to |
| * de-initialize roaming structures in fw. |
| * In STA+STA mode, if this vendor command to enable roaming is |
| * received for one STA vdev and ROAM_START was received for other |
| * STA vdev, then also driver would be send vdev_params command to |
| * de-initialize roaming structures in fw on the roaming enabled |
| * vdev. |
| */ |
| if (hdd_is_roaming_in_progress(hdd_ctx)) { |
| mlme_set_roam_trigger_bitmap(hdd_ctx->psoc, adapter->vdev_id, |
| triggers.trigger_bitmap); |
| hdd_err("Reject set roam trigger as roaming is in progress"); |
| |
| return QDF_STATUS_E_BUSY; |
| } |
| |
| /* |
| * roam trigger bitmap is > 0 - Roam triggers are set. |
| * roam trigger bitmap is 0 - Disable roaming |
| * |
| * For both the above modes, reset the roam scan scheme bitmap to |
| * 0. |
| */ |
| status = ucfg_cm_update_roam_scan_scheme_bitmap(hdd_ctx->psoc, |
| vdev_id, 0); |
| |
| status = ucfg_cm_rso_set_roam_trigger(hdd_ctx->pdev, vdev_id, |
| &triggers); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("Failed to set roam control trigger bitmap"); |
| |
| return status; |
| } |
| |
| /* |
| * Disable default scoring algorithm. This is intended to set all bits of the |
| * disable_bitmap in struct scoring_param. |
| */ |
| #define DISABLE_SCORING 0 |
| |
| /* |
| * Enable scoring algorithm. This is intended to clear all bits of the |
| * disable_bitmap in struct scoring_param. |
| */ |
| #define ENABLE_SCORING 1 |
| |
| /* |
| * Controlled roam candidate selection is enabled from userspace. |
| * Driver/firmware should honor the selection criteria |
| */ |
| #define CONTROL_ROAM_CAND_SEL_ENABLE 1 |
| |
| /* |
| * Controlled roam candidate selection is disabled from userspace. |
| * Driver/firmware can use its internal candidate selection criteria |
| */ |
| #define CONTROL_ROAM_CAND_SEL_DISABLE 0 |
| |
| static const struct nla_policy |
| roam_scan_cand_sel_policy[PARAM_CAND_SEL_CRITERIA_MAX + 1] = { |
| [PARAM_CAND_SEL_SCORE_RSSI] = {.type = NLA_U8}, |
| }; |
| |
| /** |
| * hdd_send_roam_cand_sel_criteria_to_sme() - Send candidate sel criteria to SME |
| * @hdd_ctx: HDD context |
| * @vdev_id: vdev id |
| * @attr: Nested attribute carrying candidate selection criteria |
| * |
| * Extract different candidate sel criteria mentioned and convert it to |
| * driver/firmware understable format. |
| * |
| * Return: QDF_STATUS |
| */ |
| static QDF_STATUS |
| hdd_send_roam_cand_sel_criteria_to_sme(struct hdd_context *hdd_ctx, |
| uint8_t vdev_id, |
| struct nlattr *attr) |
| { |
| QDF_STATUS status; |
| struct nlattr *tb2[PARAM_CAND_SEL_CRITERIA_MAX + 1]; |
| struct nlattr *curr_attr; |
| uint8_t sel_criteria = 0, rssi_score = 0, scoring; |
| int rem; |
| |
| hdd_debug("Received Command to Set candidate selection criteria "); |
| nla_for_each_nested(curr_attr, attr, rem) { |
| sel_criteria++; |
| break; |
| } |
| |
| if (sel_criteria && |
| wlan_cfg80211_nla_parse_nested(tb2, PARAM_CAND_SEL_CRITERIA_MAX, |
| attr, roam_scan_cand_sel_policy)) { |
| hdd_err("nla_parse failed"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| /* |
| * Firmware supports the below configurations currently, |
| * 1. Default selection criteria where all scoring params |
| * are enabled and different weightages/scores are given to |
| * different parameters. |
| * When userspace doesn't specify any candidate selection criteria, |
| * this will be enabled. |
| * 2. Legacy candidate selection criteria where scoring |
| * algorithm is disabled and only RSSI is considered for |
| * roam candidate selection. |
| * When userspace specify 100% weightage for RSSI, this will |
| * be enabled. |
| * Rest of the combinations are not supported for now. |
| */ |
| if (sel_criteria == CONTROL_ROAM_CAND_SEL_ENABLE) { |
| /* Legacy selection criteria: 100% weightage to RSSI */ |
| if (tb2[PARAM_CAND_SEL_SCORE_RSSI]) |
| rssi_score = nla_get_u8(tb2[PARAM_CAND_SEL_SCORE_RSSI]); |
| |
| if (rssi_score != 100) { |
| hdd_debug("Ignore the candidate selection criteria"); |
| return QDF_STATUS_E_INVAL; |
| } |
| scoring = DISABLE_SCORING; |
| } else { |
| /* Default selection criteria */ |
| scoring = ENABLE_SCORING; |
| } |
| |
| status = sme_modify_roam_cand_sel_criteria(hdd_ctx->mac_handle, vdev_id, |
| !!scoring); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("Failed to disable scoring"); |
| |
| return status; |
| } |
| |
| /** |
| * hdd_send_roam_scan_period_to_sme() - Send roam scan period to SME |
| * @hdd_ctx: HDD context |
| * @vdev_id: vdev id |
| * @roam_scan_period: Roam scan period in seconds |
| * @check_and_update: If this is true/set, update the value only if the current |
| * configured value is not same as global value read from |
| * ini param. This is to give priority to the user configured |
| * values and retain the value, if updated already. |
| * If this is not set, update the value without any check. |
| * |
| * Validate the roam scan period and send it to firmware if valid. |
| * |
| * Return: QDF_STATUS |
| */ |
| static QDF_STATUS |
| hdd_send_roam_scan_period_to_sme(struct hdd_context *hdd_ctx, |
| uint8_t vdev_id, |
| uint32_t roam_scan_period, |
| bool check_and_update) |
| { |
| QDF_STATUS status; |
| uint16_t roam_scan_period_current, roam_scan_period_global; |
| |
| if (!ucfg_mlme_validate_scan_period(roam_scan_period * 1000)) |
| return QDF_STATUS_E_INVAL; |
| |
| hdd_debug("Received Command to Set roam scan period (Empty Scan refresh period) = %d", |
| roam_scan_period); |
| |
| status = sme_get_empty_scan_refresh_period(hdd_ctx->mac_handle, vdev_id, |
| &roam_scan_period_current); |
| if (QDF_IS_STATUS_ERROR(status)) |
| return status; |
| |
| roam_scan_period_global = |
| sme_get_empty_scan_refresh_period_global(hdd_ctx->mac_handle); |
| if (check_and_update && |
| roam_scan_period_current != roam_scan_period_global) { |
| hdd_debug("roam scan period is already updated, value: %u", |
| roam_scan_period_current / 1000); |
| return QDF_STATUS_SUCCESS; |
| } |
| status = sme_update_empty_scan_refresh_period(hdd_ctx->mac_handle, |
| vdev_id, |
| roam_scan_period * 1000); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("Failed to set scan period"); |
| |
| return status; |
| } |
| |
| /** |
| * hdd_set_roam_with_control_config() - Set roam control configuration |
| * @hdd_ctx: HDD context |
| * @tb: List of attributes carrying roam subcmd data |
| * @vdev_id: vdev id |
| * |
| * Extracts the attribute PARAM_ROAM_CONTROL_CONFIG from the attributes list tb |
| * and sends the corresponding roam control configuration to driver/firmware. |
| * |
| * Return: 0 on success; error number on failure |
| */ |
| static int |
| hdd_set_roam_with_control_config(struct hdd_context *hdd_ctx, |
| struct nlattr **tb, |
| uint8_t vdev_id) |
| { |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| struct nlattr *tb2[QCA_ATTR_ROAM_CONTROL_MAX + 1], *attr; |
| uint32_t value; |
| struct wlan_cm_roam_vendor_btm_params param = {0}; |
| bool is_wtc_param_updated = false; |
| uint32_t band_mask; |
| |
| hdd_enter(); |
| /* The command must carry PARAM_ROAM_CONTROL_CONFIG */ |
| if (!tb[PARAM_ROAM_CONTROL_CONFIG]) { |
| hdd_err("Attribute CONTROL_CONFIG is not present"); |
| return -EINVAL; |
| } |
| |
| if (wlan_cfg80211_nla_parse_nested(tb2, QCA_ATTR_ROAM_CONTROL_MAX, |
| tb[PARAM_ROAM_CONTROL_CONFIG], |
| roam_control_policy)) { |
| hdd_err("nla_parse failed"); |
| return -EINVAL; |
| } |
| |
| attr = tb2[PARAM_FREQ_LIST_SCHEME]; |
| if (attr) { |
| status = hdd_send_roam_scan_channel_freq_list_to_sme(hdd_ctx, |
| vdev_id, |
| attr); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("failed to config roam control"); |
| } |
| |
| if (tb2[QCA_ATTR_ROAM_CONTROL_TRIGGERS]) { |
| value = nla_get_u32(tb2[QCA_ATTR_ROAM_CONTROL_TRIGGERS]); |
| hdd_debug("Received roam trigger bitmap: 0x%x", value); |
| status = hdd_send_roam_triggers_to_sme(hdd_ctx, |
| vdev_id, |
| value); |
| if (status) |
| hdd_err("failed to config roam triggers"); |
| } |
| |
| attr = tb2[QCA_ATTR_ROAM_CONTROL_ENABLE]; |
| if (attr) { |
| status = sme_set_roam_config_enable(hdd_ctx->mac_handle, |
| vdev_id, |
| nla_get_u8(attr)); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("failed to enable/disable roam control config"); |
| |
| hdd_debug("Parse and send roam control %s:", |
| nla_get_u8(attr) ? "Enable" : "Disable"); |
| |
| attr = tb2[QCA_ATTR_ROAM_CONTROL_SCAN_PERIOD]; |
| if (attr) { |
| /* Default value received as part of Roam control enable |
| * Set this only if user hasn't configured any value so |
| * far. |
| */ |
| value = nla_get_u32(attr); |
| status = hdd_send_roam_scan_period_to_sme(hdd_ctx, |
| vdev_id, |
| value, true); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("failed to send scan period to firmware"); |
| } |
| |
| attr = tb2[QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD]; |
| if (attr) { |
| value = nla_get_u32(attr); |
| /* Default value received as part of Roam control enable |
| * Set this only if user hasn't configured any value so |
| * far. |
| */ |
| status = hdd_send_roam_full_scan_period_to_sme(hdd_ctx, |
| vdev_id, |
| value, |
| true); |
| if (status) |
| hdd_err("failed to config full scan period"); |
| } |
| } else { |
| attr = tb2[QCA_ATTR_ROAM_CONTROL_SCAN_PERIOD]; |
| if (attr) { |
| /* User configured value, cache the value directly */ |
| value = nla_get_u32(attr); |
| status = hdd_send_roam_scan_period_to_sme(hdd_ctx, |
| vdev_id, |
| value, false); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("failed to send scan period to firmware"); |
| } |
| |
| attr = tb2[QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD]; |
| if (attr) { |
| value = nla_get_u32(attr); |
| /* User configured value, cache the value directly */ |
| status = hdd_send_roam_full_scan_period_to_sme(hdd_ctx, |
| vdev_id, |
| value, |
| false); |
| if (status) |
| hdd_err("failed to config full scan period"); |
| } |
| } |
| |
| attr = tb2[QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS]; |
| if (attr) { |
| value = wlan_hdd_convert_control_roam_scan_scheme_bitmap( |
| nla_get_u32(attr)); |
| status = ucfg_cm_update_roam_scan_scheme_bitmap(hdd_ctx->psoc, |
| vdev_id, |
| value); |
| } |
| |
| /* Scoring and roam candidate selection criteria */ |
| attr = tb2[QCA_ATTR_ROAM_CONTROL_SELECTION_CRITERIA]; |
| if (attr) { |
| status = hdd_send_roam_cand_sel_criteria_to_sme(hdd_ctx, |
| vdev_id, attr); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("failed to set candidate selection criteria"); |
| } |
| |
| attr = tb2[QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME]; |
| if (attr) { |
| param.scan_freq_scheme = nla_get_u32(attr); |
| is_wtc_param_updated = true; |
| } |
| |
| attr = tb2[QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD]; |
| if (attr) { |
| param.connected_rssi_threshold = nla_get_u32(attr); |
| is_wtc_param_updated = true; |
| } |
| |
| attr = tb2[QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD]; |
| if (attr) { |
| param.candidate_rssi_threshold_2g = nla_get_u32(attr); |
| is_wtc_param_updated = true; |
| } |
| |
| attr = tb2[QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_2P4GHZ]; |
| if (attr) { |
| param.candidate_rssi_threshold_2g = nla_get_u32(attr); |
| is_wtc_param_updated = true; |
| } |
| |
| attr = tb2[QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_5GHZ]; |
| if (attr) { |
| param.candidate_rssi_threshold_5g = nla_get_u32(attr); |
| is_wtc_param_updated = true; |
| } else { |
| param.candidate_rssi_threshold_5g = |
| param.candidate_rssi_threshold_2g; |
| } |
| |
| attr = tb2[QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_6GHZ]; |
| if (attr) { |
| param.candidate_rssi_threshold_6g = nla_get_u32(attr); |
| is_wtc_param_updated = true; |
| } else { |
| param.candidate_rssi_threshold_6g = |
| param.candidate_rssi_threshold_2g; |
| } |
| |
| attr = tb2[QCA_ATTR_ROAM_CONTROL_USER_REASON]; |
| if (attr) { |
| param.user_roam_reason = nla_get_u32(attr); |
| is_wtc_param_updated = true; |
| } else { |
| param.user_roam_reason = DISABLE_VENDOR_BTM_CONFIG; |
| } |
| |
| if (tb2[QCA_ATTR_ROAM_CONTROL_BAND_MASK]) { |
| band_mask = |
| nla_get_u32(tb2[QCA_ATTR_ROAM_CONTROL_BAND_MASK]); |
| band_mask = |
| wlan_vendor_bitmap_to_reg_wifi_band_bitmap(hdd_ctx->psoc, |
| band_mask); |
| hdd_debug("[ROAM BAND] set roam band mask:%d", band_mask); |
| if (band_mask) { |
| ucfg_cm_set_roam_band_mask(hdd_ctx->psoc, vdev_id, |
| band_mask); |
| } else { |
| hdd_debug("Invalid roam BAND_MASK"); |
| return -EINVAL; |
| } |
| |
| if (ucfg_cm_is_change_in_band_allowed(hdd_ctx->psoc, vdev_id, |
| band_mask)) { |
| |
| /* Disable roaming on Vdev before setting PCL */ |
| sme_stop_roaming(hdd_ctx->mac_handle, vdev_id, |
| REASON_DRIVER_DISABLED, RSO_SET_PCL); |
| |
| policy_mgr_set_pcl_for_existing_combo(hdd_ctx->psoc, |
| PM_STA_MODE, |
| vdev_id); |
| |
| /* Enable roaming once SET pcl is done */ |
| sme_start_roaming(hdd_ctx->mac_handle, vdev_id, |
| REASON_DRIVER_ENABLED, RSO_SET_PCL); |
| } |
| } |
| |
| if (is_wtc_param_updated) { |
| wlan_cm_roam_set_vendor_btm_params(hdd_ctx->psoc, ¶m); |
| /* Sends RSO update */ |
| sme_send_vendor_btm_params(hdd_ctx->mac_handle, vdev_id); |
| } |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| #define ENABLE_ROAM_TRIGGERS_ALL (QCA_ROAM_TRIGGER_REASON_PER | \ |
| QCA_ROAM_TRIGGER_REASON_BEACON_MISS | \ |
| QCA_ROAM_TRIGGER_REASON_POOR_RSSI | \ |
| QCA_ROAM_TRIGGER_REASON_BETTER_RSSI | \ |
| QCA_ROAM_TRIGGER_REASON_PERIODIC | \ |
| QCA_ROAM_TRIGGER_REASON_DENSE | \ |
| QCA_ROAM_TRIGGER_REASON_BTM | \ |
| QCA_ROAM_TRIGGER_REASON_BSS_LOAD | \ |
| QCA_ROAM_TRIGGER_REASON_USER_TRIGGER | \ |
| QCA_ROAM_TRIGGER_REASON_DEAUTH | \ |
| QCA_ROAM_TRIGGER_REASON_IDLE | \ |
| QCA_ROAM_TRIGGER_REASON_TX_FAILURES | \ |
| QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN) |
| |
| static int |
| hdd_clear_roam_control_config(struct hdd_context *hdd_ctx, |
| struct nlattr **tb, |
| uint8_t vdev_id) |
| { |
| QDF_STATUS status; |
| struct nlattr *tb2[QCA_ATTR_ROAM_CONTROL_MAX + 1]; |
| mac_handle_t mac_handle = hdd_ctx->mac_handle; |
| uint32_t value; |
| |
| /* The command must carry PARAM_ROAM_CONTROL_CONFIG */ |
| if (!tb[PARAM_ROAM_CONTROL_CONFIG]) { |
| hdd_err("Attribute CONTROL_CONFIG is not present"); |
| return -EINVAL; |
| } |
| |
| if (wlan_cfg80211_nla_parse_nested(tb2, QCA_ATTR_ROAM_CONTROL_MAX, |
| tb[PARAM_ROAM_CONTROL_CONFIG], |
| roam_control_policy)) { |
| hdd_err("nla_parse failed"); |
| return -EINVAL; |
| } |
| |
| hdd_debug("Clear the control config done through SET"); |
| if (tb2[QCA_ATTR_ROAM_CONTROL_CLEAR_ALL]) { |
| hdd_debug("Disable roam control config done through SET"); |
| status = sme_set_roam_config_enable(hdd_ctx->mac_handle, |
| vdev_id, 0); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("failed to enable/disable roam control config"); |
| return qdf_status_to_os_return(status); |
| } |
| |
| value = ENABLE_ROAM_TRIGGERS_ALL; |
| hdd_debug("Reset roam trigger bitmap to 0x%x", value); |
| status = hdd_send_roam_triggers_to_sme(hdd_ctx, vdev_id, value); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("failed to restore roam trigger bitmap"); |
| return qdf_status_to_os_return(status); |
| } |
| |
| status = sme_roam_control_restore_default_config(mac_handle, |
| vdev_id); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("failed to config roam control"); |
| return qdf_status_to_os_return(status); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_roam_control_config_buf_size() - Calculate the skb size to be allocated |
| * @hdd_ctx: HDD context |
| * @tb: List of attributes to be populated |
| * |
| * Calculate the buffer size to be allocated based on the attributes |
| * mentioned in tb. |
| * |
| * Return: buffer size to be allocated |
| */ |
| static uint16_t |
| hdd_roam_control_config_buf_size(struct hdd_context *hdd_ctx, |
| struct nlattr **tb) |
| { |
| uint16_t skb_len = 0; |
| |
| if (tb[QCA_ATTR_ROAM_CONTROL_STATUS]) |
| skb_len += NLA_HDRLEN + sizeof(uint8_t); |
| |
| if (tb[QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD]) |
| skb_len += NLA_HDRLEN + sizeof(uint32_t); |
| |
| if (tb[QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME]) |
| /* |
| * Response has 3 nests, 1 atrribure value and a |
| * attribute list of frequencies. |
| */ |
| skb_len += 3 * nla_total_size(0) + |
| nla_total_size(sizeof(uint32_t)) + |
| (nla_total_size(sizeof(uint32_t)) * |
| NUM_CHANNELS); |
| |
| if (tb[QCA_ATTR_ROAM_CONTROL_BAND_MASK]) |
| skb_len += NLA_HDRLEN + sizeof(uint32_t); |
| |
| return skb_len; |
| } |
| |
| /** |
| * wlan_reg_wifi_band_bitmap_to_vendor_bitmap() - Convert enum reg_wifi_band |
| * to enum qca_set_band |
| * @reg_wifi_band_bitmap: enum reg_wifi_band |
| * |
| * Return: qca_set_band value |
| */ |
| static uint32_t |
| wlan_reg_wifi_band_bitmap_to_vendor_bitmap(uint32_t reg_wifi_band_bitmap) |
| { |
| uint32_t vendor_mask = 0; |
| |
| if (reg_wifi_band_bitmap & BIT(REG_BAND_2G)) |
| vendor_mask |= QCA_SETBAND_2G; |
| if (reg_wifi_band_bitmap & BIT(REG_BAND_5G)) |
| vendor_mask |= QCA_SETBAND_5G; |
| if (reg_wifi_band_bitmap & BIT(REG_BAND_6G)) |
| vendor_mask |= QCA_SETBAND_6G; |
| |
| return vendor_mask; |
| } |
| |
| /** |
| * hdd_roam_control_config_fill_data() - Fill the data requested by userspace |
| * @hdd_ctx: HDD context |
| * @vdev_id: vdev id |
| * @skb: SK buffer |
| * @tb: List of attributes |
| * |
| * Get the data corresponding to the attribute list specified in tb and |
| * update the same to skb by populating the same attributes. |
| * |
| * Return: 0 on success; error number on failure |
| */ |
| static int |
| hdd_roam_control_config_fill_data(struct hdd_context *hdd_ctx, uint8_t vdev_id, |
| struct sk_buff *skb, struct nlattr **tb) |
| { |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| uint8_t roam_control; |
| struct nlattr *config, *get_freq_scheme, *get_freq; |
| uint32_t full_roam_scan_period, roam_band, vendor_band_mask; |
| uint8_t num_channels = 0; |
| uint32_t i = 0, freq_list[NUM_CHANNELS] = { 0 }; |
| struct hdd_adapter *hdd_adapter = NULL; |
| |
| config = nla_nest_start(skb, PARAM_ROAM_CONTROL_CONFIG); |
| if (!config) { |
| hdd_err("nla nest start failure"); |
| return -EINVAL; |
| } |
| |
| if (tb[QCA_ATTR_ROAM_CONTROL_STATUS]) { |
| status = sme_get_roam_config_status(hdd_ctx->mac_handle, |
| vdev_id, |
| &roam_control); |
| if (QDF_IS_STATUS_ERROR(status)) |
| goto out; |
| hdd_debug("Roam control: %s", |
| roam_control ? "Enabled" : "Disabled"); |
| if (nla_put_u8(skb, QCA_ATTR_ROAM_CONTROL_STATUS, |
| roam_control)) { |
| hdd_info("failed to put vendor_roam_control"); |
| return -ENOMEM; |
| } |
| } |
| |
| if (tb[QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD]) { |
| status = sme_get_full_roam_scan_period(hdd_ctx->mac_handle, |
| vdev_id, |
| &full_roam_scan_period); |
| if (QDF_IS_STATUS_ERROR(status)) |
| goto out; |
| hdd_debug("full_roam_scan_period: %u", full_roam_scan_period); |
| |
| if (nla_put_u32(skb, QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD, |
| full_roam_scan_period)) { |
| hdd_info("failed to put full_roam_scan_period"); |
| return -EINVAL; |
| } |
| } |
| |
| if (tb[QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME]) { |
| hdd_adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); |
| if (!hdd_adapter) { |
| hdd_info("HDD adapter is NULL"); |
| return -EINVAL; |
| } |
| |
| hdd_debug("Get roam scan frequencies req received"); |
| status = hdd_get_roam_scan_freq(hdd_adapter, |
| hdd_ctx->mac_handle, |
| freq_list, &num_channels); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_info("failed to get roam scan freq"); |
| goto out; |
| } |
| |
| hdd_debug("num_channels %d", num_channels); |
| get_freq_scheme = nla_nest_start( |
| skb, QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME); |
| if (!get_freq_scheme) { |
| hdd_info("failed to nest start for roam scan freq"); |
| return -EINVAL; |
| } |
| |
| if (nla_put_u32(skb, PARAM_SCAN_FREQ_LIST_TYPE, 0)) { |
| hdd_info("failed to put list type"); |
| return -EINVAL; |
| } |
| |
| get_freq = nla_nest_start( |
| skb, QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST); |
| if (!get_freq) { |
| hdd_info("failed to nest start for roam scan freq"); |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < num_channels; i++) { |
| if (nla_put_u32(skb, PARAM_SCAN_FREQ_LIST, |
| freq_list[i])) { |
| hdd_info("failed to put freq at index %d", i); |
| return -EINVAL; |
| } |
| } |
| nla_nest_end(skb, get_freq); |
| nla_nest_end(skb, get_freq_scheme); |
| } |
| |
| if (tb[QCA_ATTR_ROAM_CONTROL_BAND_MASK]) { |
| status = ucfg_cm_get_roam_band(hdd_ctx->psoc, vdev_id, |
| &roam_band); |
| if (QDF_IS_STATUS_ERROR(status)) |
| goto out; |
| vendor_band_mask = |
| wlan_reg_wifi_band_bitmap_to_vendor_bitmap(roam_band); |
| if (nla_put_u32(skb, QCA_ATTR_ROAM_CONTROL_BAND_MASK, |
| vendor_band_mask)) { |
| hdd_info("failed to put roam_band"); |
| return -EINVAL; |
| } |
| hdd_debug("sending vendor_band_mask: %d reg band:%d", |
| vendor_band_mask, roam_band); |
| } |
| |
| nla_nest_end(skb, config); |
| |
| out: |
| return qdf_status_to_os_return(status); |
| } |
| |
| /** |
| * hdd_send_roam_control_config() - Send the roam config as vendor cmd reply |
| * @mac_handle: Opaque handle to the MAC context |
| * @vdev_id: vdev id |
| * @tb: List of attributes |
| * |
| * Parse the attributes list tb and get the data corresponding to the |
| * attributes specified in tb. Send them as a vendor response. |
| * |
| * Return: 0 on success; error number on failure |
| */ |
| static int |
| hdd_send_roam_control_config(struct hdd_context *hdd_ctx, |
| uint8_t vdev_id, |
| struct nlattr **tb) |
| { |
| struct sk_buff *skb; |
| uint16_t skb_len; |
| int status; |
| |
| skb_len = hdd_roam_control_config_buf_size(hdd_ctx, tb); |
| if (!skb_len) { |
| hdd_err("No data requested"); |
| return -EINVAL; |
| } |
| |
| skb_len += NLMSG_HDRLEN; |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, skb_len); |
| if (!skb) { |
| hdd_info("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| return -ENOMEM; |
| } |
| |
| status = hdd_roam_control_config_fill_data(hdd_ctx, vdev_id, skb, tb); |
| if (status) |
| goto fail; |
| |
| return cfg80211_vendor_cmd_reply(skb); |
| |
| fail: |
| hdd_err("nla put fail"); |
| kfree_skb(skb); |
| return status; |
| } |
| |
| /** |
| * hdd_get_roam_control_config() - Send requested roam config to userspace |
| * @hdd_ctx: HDD context |
| * @tb: list of attributes |
| * @vdev_id: vdev id |
| * |
| * Return: 0 on success; error number on failure |
| */ |
| static int hdd_get_roam_control_config(struct hdd_context *hdd_ctx, |
| struct nlattr **tb, |
| uint8_t vdev_id) |
| { |
| QDF_STATUS status; |
| struct nlattr *tb2[QCA_ATTR_ROAM_CONTROL_MAX + 1]; |
| |
| /* The command must carry PARAM_ROAM_CONTROL_CONFIG */ |
| if (!tb[PARAM_ROAM_CONTROL_CONFIG]) { |
| hdd_err("Attribute CONTROL_CONFIG is not present"); |
| return -EINVAL; |
| } |
| |
| if (wlan_cfg80211_nla_parse_nested(tb2, QCA_ATTR_ROAM_CONTROL_MAX, |
| tb[PARAM_ROAM_CONTROL_CONFIG], |
| roam_control_policy)) { |
| hdd_err("nla_parse failed"); |
| return -EINVAL; |
| } |
| |
| status = hdd_send_roam_control_config(hdd_ctx, vdev_id, tb2); |
| if (status) { |
| hdd_err("failed to enable/disable roam control"); |
| return status; |
| } |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| #undef PARAM_ROAM_CONTROL_CONFIG |
| #undef PARAM_FREQ_LIST_SCHEME_MAX |
| #undef PARAM_FREQ_LIST_SCHEME |
| #undef PARAM_SCAN_FREQ_LIST |
| #undef PARAM_SCAN_FREQ_LIST_TYPE |
| #undef PARAM_CAND_SEL_CRITERIA_MAX |
| #undef PARAM_CAND_SEL_SCORE_RSSI |
| |
| /** |
| * hdd_set_ext_roam_params() - parse ext roam params |
| * @hdd_ctx: HDD context |
| * @tb: list of attributes |
| * @vdev_id: vdev id |
| * @rso_config: roam params |
| * |
| * Return: 0 on success; error number on failure |
| */ |
| static int hdd_set_ext_roam_params(struct hdd_context *hdd_ctx, |
| const void *data, int data_len, |
| uint8_t vdev_id, |
| struct rso_config_params *rso_config) |
| { |
| uint32_t cmd_type, req_id; |
| struct nlattr *tb[MAX_ROAMING_PARAM + 1]; |
| int ret; |
| mac_handle_t mac_handle; |
| |
| if (wlan_cfg80211_nla_parse(tb, MAX_ROAMING_PARAM, data, data_len, |
| wlan_hdd_set_roam_param_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| /* Parse and fetch Command Type */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD]) { |
| hdd_err("roam cmd type failed"); |
| goto fail; |
| } |
| |
| cmd_type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD]); |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID]) { |
| hdd_err("attr request id failed"); |
| goto fail; |
| } |
| mac_handle = hdd_ctx->mac_handle; |
| req_id = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID]); |
| hdd_debug("Req Id: %u Cmd Type: %u", req_id, cmd_type); |
| switch (cmd_type) { |
| case QCA_WLAN_VENDOR_ROAMING_SUBCMD_SSID_WHITE_LIST: |
| ret = hdd_set_white_list(hdd_ctx, rso_config, tb, vdev_id); |
| if (ret) |
| goto fail; |
| break; |
| |
| case QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_EXTSCAN_ROAM_PARAMS: |
| /* Parse and fetch 5G Boost Threshold */ |
| if (!tb[PARAM_A_BAND_BOOST_THLD]) { |
| hdd_err("5G boost threshold failed"); |
| goto fail; |
| } |
| rso_config->raise_rssi_thresh_5g = nla_get_s32( |
| tb[PARAM_A_BAND_BOOST_THLD]); |
| hdd_debug("5G Boost Threshold (%d)", |
| rso_config->raise_rssi_thresh_5g); |
| /* Parse and fetch 5G Penalty Threshold */ |
| if (!tb[PARAM_A_BAND_PELT_THLD]) { |
| hdd_err("5G penalty threshold failed"); |
| goto fail; |
| } |
| rso_config->drop_rssi_thresh_5g = nla_get_s32( |
| tb[PARAM_A_BAND_PELT_THLD]); |
| hdd_debug("5G Penalty Threshold (%d)", |
| rso_config->drop_rssi_thresh_5g); |
| /* Parse and fetch 5G Boost Factor */ |
| if (!tb[PARAM_A_BAND_BOOST_FACTOR]) { |
| hdd_err("5G boost Factor failed"); |
| goto fail; |
| } |
| rso_config->raise_factor_5g = nla_get_u32( |
| tb[PARAM_A_BAND_BOOST_FACTOR]); |
| hdd_debug("5G Boost Factor (%d)", |
| rso_config->raise_factor_5g); |
| /* Parse and fetch 5G Penalty factor */ |
| if (!tb[PARAM_A_BAND_PELT_FACTOR]) { |
| hdd_err("5G Penalty Factor failed"); |
| goto fail; |
| } |
| rso_config->drop_factor_5g = nla_get_u32( |
| tb[PARAM_A_BAND_PELT_FACTOR]); |
| hdd_debug("5G Penalty factor (%d)", |
| rso_config->drop_factor_5g); |
| /* Parse and fetch 5G Max Boost */ |
| if (!tb[PARAM_A_BAND_MAX_BOOST]) { |
| hdd_err("5G Max Boost failed"); |
| goto fail; |
| } |
| rso_config->max_raise_rssi_5g = nla_get_u32( |
| tb[PARAM_A_BAND_MAX_BOOST]); |
| hdd_debug("5G Max Boost (%d)", |
| rso_config->max_raise_rssi_5g); |
| /* Parse and fetch Rssi Diff */ |
| if (!tb[PARAM_ROAM_HISTERESYS]) { |
| hdd_err("Rssi Diff failed"); |
| goto fail; |
| } |
| rso_config->rssi_diff = nla_get_s32( |
| tb[PARAM_ROAM_HISTERESYS]); |
| hdd_debug("RSSI Diff (%d)", |
| rso_config->rssi_diff); |
| /* Parse and fetch Alert Rssi Threshold */ |
| if (!tb[PARAM_RSSI_TRIGGER]) { |
| hdd_err("Alert Rssi Threshold failed"); |
| goto fail; |
| } |
| rso_config->alert_rssi_threshold = nla_get_u32( |
| tb[PARAM_RSSI_TRIGGER]); |
| hdd_debug("Alert RSSI Threshold (%d)", |
| rso_config->alert_rssi_threshold); |
| sme_update_roam_params(mac_handle, vdev_id, rso_config, |
| REASON_ROAM_EXT_SCAN_PARAMS_CHANGED); |
| break; |
| case QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_LAZY_ROAM: |
| /* Parse and fetch Activate Good Rssi Roam */ |
| if (!tb[PARAM_ROAM_ENABLE]) { |
| hdd_err("Activate Good Rssi Roam failed"); |
| goto fail; |
| } |
| rso_config->good_rssi_roam = nla_get_s32( |
| tb[PARAM_ROAM_ENABLE]); |
| hdd_debug("Activate Good Rssi Roam (%d)", |
| rso_config->good_rssi_roam); |
| sme_update_roam_params(mac_handle, vdev_id, rso_config, |
| REASON_ROAM_GOOD_RSSI_CHANGED); |
| break; |
| case QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BSSID_PREFS: |
| ret = hdd_set_bssid_prefs(hdd_ctx, rso_config, tb, vdev_id); |
| if (ret) |
| goto fail; |
| break; |
| case QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID: |
| ret = hdd_set_blacklist_bssid(hdd_ctx, rso_config, tb, vdev_id); |
| if (ret) |
| goto fail; |
| break; |
| case QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET: |
| ret = hdd_set_roam_with_control_config(hdd_ctx, tb, vdev_id); |
| if (ret) |
| goto fail; |
| break; |
| case QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR: |
| ret = hdd_clear_roam_control_config(hdd_ctx, tb, vdev_id); |
| if (ret) |
| goto fail; |
| break; |
| case QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_GET: |
| ret = hdd_get_roam_control_config(hdd_ctx, tb, vdev_id); |
| if (ret) |
| goto fail; |
| break; |
| } |
| |
| return 0; |
| |
| fail: |
| return -EINVAL; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_set_ext_roam_params() - Settings for roaming parameters |
| * @wiphy: The wiphy structure |
| * @wdev: The wireless device |
| * @data: Data passed by framework |
| * @data_len: Parameters to be configured passed as data |
| * |
| * The roaming related parameters are configured by the framework |
| * using this interface. |
| * |
| * Return: Return either success or failure code. |
| */ |
| static int |
| __wlan_hdd_cfg80211_set_ext_roam_params(struct wiphy *wiphy, |
| struct wireless_dev *wdev, const void *data, int data_len) |
| { |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct rso_config_params *rso_config; |
| int ret; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { |
| hdd_err("Driver Modules are closed"); |
| return -EINVAL; |
| } |
| |
| rso_config = qdf_mem_malloc(sizeof(*rso_config)); |
| if (!rso_config) |
| return -ENOMEM; |
| |
| ret = hdd_set_ext_roam_params(hdd_ctx, data, data_len, |
| adapter->vdev_id, rso_config); |
| qdf_mem_free(rso_config); |
| if (ret) |
| goto fail; |
| |
| return 0; |
| fail: |
| return ret; |
| } |
| #undef PARAM_NUM_NW |
| #undef PARAM_SET_BSSID |
| #undef PARAM_SET_BSSID_HINT |
| #undef PARAM_SSID_LIST |
| #undef PARAM_LIST_SSID |
| #undef MAX_ROAMING_PARAM |
| #undef PARAM_NUM_BSSID |
| #undef PARAM_BSSID_PREFS |
| #undef PARAM_ROAM_BSSID |
| #undef PARAM_RSSI_MODIFIER |
| #undef PARAMS_NUM_BSSID |
| #undef PARAM_BSSID_PARAMS |
| #undef PARAM_A_BAND_BOOST_THLD |
| #undef PARAM_A_BAND_PELT_THLD |
| #undef PARAM_A_BAND_BOOST_FACTOR |
| #undef PARAM_A_BAND_PELT_FACTOR |
| #undef PARAM_A_BAND_MAX_BOOST |
| #undef PARAM_ROAM_HISTERESYS |
| #undef PARAM_RSSI_TRIGGER |
| #undef PARAM_ROAM_ENABLE |
| |
| |
| /** |
| * wlan_hdd_cfg80211_set_ext_roam_params() - set ext scan roam params |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the data to be passed via vendor interface |
| * @data_len:Length of the data to be passed |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int |
| wlan_hdd_cfg80211_set_ext_roam_params(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_ext_roam_params(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #define RATEMASK_PARAMS_TYPE_MAX 4 |
| #define RATEMASK_PARAMS_MAX QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_MAX |
| const struct nla_policy wlan_hdd_set_ratemask_param_policy[ |
| RATEMASK_PARAMS_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_LIST] = |
| VENDOR_NLA_POLICY_NESTED(wlan_hdd_set_ratemask_param_policy), |
| [QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_TYPE] = {.type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_BITMAP] = {.type = NLA_BINARY, |
| .len = 128}, |
| }; |
| |
| /** |
| * hdd_set_ratemask_params() - parse ratemask params |
| * @hdd_ctx: HDD context |
| * @tb: list of attributes |
| * @vdev_id: vdev id |
| * |
| * Return: 0 on success; error number on failure |
| */ |
| static int hdd_set_ratemask_params(struct hdd_context *hdd_ctx, |
| const void *data, int data_len, |
| struct wlan_objmgr_vdev *vdev) |
| { |
| struct nlattr *tb[RATEMASK_PARAMS_MAX + 1]; |
| struct nlattr *tb2[RATEMASK_PARAMS_MAX + 1]; |
| struct nlattr *curr_attr; |
| int ret, rem; |
| struct config_ratemask_params rate_params[RATEMASK_PARAMS_TYPE_MAX]; |
| uint8_t ratemask_type, num_ratemask = 0, len; |
| uint32_t bitmap[RATEMASK_PARAMS_TYPE_MAX] = {0}; |
| |
| ret = wlan_cfg80211_nla_parse(tb, |
| RATEMASK_PARAMS_MAX, |
| data, data_len, |
| wlan_hdd_set_ratemask_param_policy); |
| if (ret) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_LIST]) { |
| hdd_err("ratemask array attribute not present"); |
| return -EINVAL; |
| } |
| |
| memset(rate_params, 0, (RATEMASK_PARAMS_TYPE_MAX * |
| sizeof(struct config_ratemask_params))); |
| |
| nla_for_each_nested(curr_attr, |
| tb[QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_LIST], |
| rem) { |
| if (num_ratemask >= RATEMASK_PARAMS_TYPE_MAX) { |
| hdd_err("Exceeding ratemask_list_param_num value"); |
| return -EINVAL; |
| } |
| |
| if (wlan_cfg80211_nla_parse( |
| tb2, RATEMASK_PARAMS_MAX, |
| nla_data(curr_attr), nla_len(curr_attr), |
| wlan_hdd_set_ratemask_param_policy)) { |
| hdd_err("nla_parse failed"); |
| return -EINVAL; |
| } |
| |
| if (!tb2[QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_TYPE]) { |
| hdd_err("type attribute not present"); |
| return -EINVAL; |
| } |
| |
| if (!tb2[QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_BITMAP]) { |
| hdd_err("bitmap attribute not present"); |
| return -EINVAL; |
| } |
| |
| ratemask_type = |
| nla_get_u8(tb2[QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_TYPE]); |
| if (ratemask_type >= RATEMASK_PARAMS_TYPE_MAX) { |
| hdd_err("invalid ratemask type"); |
| return -EINVAL; |
| } |
| |
| len = nla_len(tb2[QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_BITMAP]); |
| nla_memcpy((void *)bitmap, |
| tb2[QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_BITMAP], |
| len); |
| |
| hdd_debug("rate_type:%d, lower32 0x%x, lower32_2 0x%x, higher32 0x%x, higher32_2 0x%x", |
| ratemask_type, bitmap[0], bitmap[1], |
| bitmap[2], bitmap[3]); |
| |
| rate_params[num_ratemask].type = ratemask_type; |
| rate_params[num_ratemask].lower32 = bitmap[0]; |
| rate_params[num_ratemask].lower32_2 = bitmap[1]; |
| rate_params[num_ratemask].higher32 = bitmap[2]; |
| rate_params[num_ratemask].higher32_2 = bitmap[3]; |
| |
| num_ratemask += 1; |
| } |
| |
| ret = ucfg_set_ratemask_params(vdev, num_ratemask, rate_params); |
| if (ret) |
| hdd_err("ucfg_set_ratemask_params failed"); |
| return ret; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_set_ratemask_config() - Ratemask parameters |
| * @wiphy: The wiphy structure |
| * @wdev: The wireless device |
| * @data: Data passed by framework |
| * @data_len: Parameters to be configured passed as data |
| * |
| * The ratemask parameters are configured by the framework |
| * using this interface. |
| * |
| * Return: Return either success or failure code. |
| */ |
| static int |
| __wlan_hdd_cfg80211_set_ratemask_config(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct wlan_objmgr_vdev *vdev; |
| int ret; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { |
| hdd_err("Driver Modules are closed"); |
| return -EINVAL; |
| } |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_POWER_ID); |
| if (!vdev) { |
| hdd_err("vdev not present"); |
| return -EINVAL; |
| } |
| |
| ret = hdd_set_ratemask_params(hdd_ctx, data, data_len, vdev); |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_POWER_ID); |
| if (ret) |
| goto fail; |
| |
| return 0; |
| fail: |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_ratemask_config() - set ratemask config |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the data to be passed via vendor interface |
| * @data_len:Length of the data to be passed |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int |
| wlan_hdd_cfg80211_set_ratemask_config(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_ratemask_config(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #define PWR_SAVE_FAIL_CMD_INDEX \ |
| QCA_NL80211_VENDOR_SUBCMD_PWR_SAVE_FAIL_DETECTED_INDEX |
| |
| void hdd_chip_pwr_save_fail_detected_cb(hdd_handle_t hdd_handle, |
| struct chip_pwr_save_fail_detected_params |
| *data) |
| { |
| struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); |
| struct sk_buff *skb; |
| int flags = cds_get_gfp_flags(); |
| |
| hdd_enter(); |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| |
| if (!data) { |
| hdd_debug("data is null"); |
| return; |
| } |
| |
| skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, |
| NULL, NLMSG_HDRLEN + |
| sizeof(data->failure_reason_code) + |
| NLMSG_HDRLEN, PWR_SAVE_FAIL_CMD_INDEX, |
| flags); |
| |
| if (!skb) { |
| hdd_info("cfg80211_vendor_event_alloc failed"); |
| return; |
| } |
| |
| hdd_debug("failure reason code: %u", data->failure_reason_code); |
| |
| if (nla_put_u32(skb, |
| QCA_ATTR_CHIP_POWER_SAVE_FAILURE_REASON, |
| data->failure_reason_code)) |
| goto fail; |
| |
| cfg80211_vendor_event(skb, flags); |
| hdd_exit(); |
| return; |
| |
| fail: |
| kfree_skb(skb); |
| } |
| #undef PWR_SAVE_FAIL_CMD_INDEX |
| |
| const struct nla_policy |
| wlan_hdd_set_no_dfs_flag_config_policy[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX |
| +1] = { |
| [QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG] = {.type = NLA_U32 }, |
| }; |
| |
| /** |
| * wlan_hdd_check_dfs_channel_for_adapter() - check dfs channel in adapter |
| * @hdd_ctx: HDD context |
| * @device_mode: device mode |
| * Return: bool |
| */ |
| static bool wlan_hdd_check_dfs_channel_for_adapter(struct hdd_context *hdd_ctx, |
| enum QDF_OPMODE device_mode) |
| { |
| struct hdd_adapter *adapter, *next_adapter = NULL; |
| struct hdd_ap_ctx *ap_ctx; |
| struct hdd_station_ctx *sta_ctx; |
| wlan_net_dev_ref_dbgid dbgid = |
| NET_DEV_HOLD_CHECK_DFS_CHANNEL_FOR_ADAPTER; |
| |
| hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, |
| dbgid) { |
| if ((device_mode == adapter->device_mode) && |
| (device_mode == QDF_SAP_MODE)) { |
| ap_ctx = |
| WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| /* |
| * if there is SAP already running on DFS channel, |
| * do not disable scan on dfs channels. Note that |
| * with SAP on DFS, there cannot be conurrency on |
| * single radio. But then we can have multiple |
| * radios !! |
| * |
| * Indoor channels are also marked DFS, therefore |
| * check if the channel has REGULATORY_CHAN_RADAR |
| * channel flag to identify if the channel is DFS |
| */ |
| if (wlan_reg_is_dfs_for_freq( |
| hdd_ctx->pdev, |
| ap_ctx->operating_chan_freq)) { |
| hdd_err("SAP running on DFS channel"); |
| hdd_adapter_dev_put_debug(adapter, dbgid); |
| if (next_adapter) |
| hdd_adapter_dev_put_debug(next_adapter, |
| dbgid); |
| return true; |
| } |
| } |
| |
| if ((device_mode == adapter->device_mode) && |
| (device_mode == QDF_STA_MODE)) { |
| sta_ctx = |
| WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| /* |
| * if STA is already connected on DFS channel, |
| * do not disable scan on dfs channels. |
| * |
| * Indoor channels are also marked DFS, therefore |
| * check if the channel has REGULATORY_CHAN_RADAR |
| * channel flag to identify if the channel is DFS |
| */ |
| if (hdd_cm_is_vdev_associated(adapter) && |
| wlan_reg_is_dfs_for_freq( |
| hdd_ctx->pdev, |
| sta_ctx->conn_info.chan_freq)) { |
| hdd_err("client connected on DFS channel"); |
| hdd_adapter_dev_put_debug(adapter, dbgid); |
| if (next_adapter) |
| hdd_adapter_dev_put_debug(next_adapter, |
| dbgid); |
| return true; |
| } |
| } |
| hdd_adapter_dev_put_debug(adapter, dbgid); |
| } |
| |
| return false; |
| } |
| |
| /** |
| * wlan_hdd_enable_dfs_chan_scan() - disable/enable DFS channels |
| * @hdd_ctx: HDD context within host driver |
| * @enable_dfs_channels: If true, DFS channels can be used for scanning |
| * |
| * Loops through devices to see who is operating on DFS channels |
| * and then disables/enables DFS channels. |
| * Fails the disable request if any device is active on a DFS channel. |
| * |
| * Return: 0 or other error codes. |
| */ |
| |
| int wlan_hdd_enable_dfs_chan_scan(struct hdd_context *hdd_ctx, |
| bool enable_dfs_channels) |
| { |
| QDF_STATUS status; |
| bool err; |
| mac_handle_t mac_handle; |
| bool enable_dfs_scan = true; |
| |
| ucfg_scan_cfg_get_dfs_chan_scan_allowed(hdd_ctx->psoc, |
| &enable_dfs_scan); |
| |
| if (enable_dfs_channels == enable_dfs_scan) { |
| hdd_debug("DFS channels are already %s", |
| enable_dfs_channels ? "enabled" : "disabled"); |
| return 0; |
| } |
| |
| if (!enable_dfs_channels) { |
| err = wlan_hdd_check_dfs_channel_for_adapter(hdd_ctx, |
| QDF_STA_MODE); |
| if (err) |
| return -EOPNOTSUPP; |
| |
| err = wlan_hdd_check_dfs_channel_for_adapter(hdd_ctx, |
| QDF_SAP_MODE); |
| if (err) |
| return -EOPNOTSUPP; |
| } |
| |
| ucfg_scan_cfg_set_dfs_chan_scan_allowed(hdd_ctx->psoc, |
| enable_dfs_channels); |
| |
| mac_handle = hdd_ctx->mac_handle; |
| status = sme_enable_dfs_chan_scan(mac_handle, enable_dfs_channels); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("Failed to set DFS channel scan flag to %d", |
| enable_dfs_channels); |
| return qdf_status_to_os_return(status); |
| } |
| |
| hdd_abort_mac_scan_all_adapters(hdd_ctx); |
| |
| /* pass dfs channel status to regulatory component */ |
| status = ucfg_reg_enable_dfs_channels(hdd_ctx->pdev, |
| enable_dfs_channels); |
| |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("Failed to %s DFS channels", |
| enable_dfs_channels ? "enable" : "disable"); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_disable_dfs_chan_scan() - DFS channel configuration |
| * @wiphy: corestack handler |
| * @wdev: wireless device |
| * @data: data |
| * @data_len: data length |
| * Return: success(0) or reason code for failure |
| */ |
| static int __wlan_hdd_cfg80211_disable_dfs_chan_scan(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct net_device *dev = wdev->netdev; |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX + 1]; |
| int ret_val; |
| uint32_t no_dfs_flag = 0; |
| bool enable_dfs_scan = true; |
| hdd_enter_dev(dev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| return ret_val; |
| |
| if (wlan_cfg80211_nla_parse(tb, |
| QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX, |
| data, data_len, |
| wlan_hdd_set_no_dfs_flag_config_policy)) { |
| hdd_err("invalid attr"); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG]) { |
| hdd_err("attr dfs flag failed"); |
| return -EINVAL; |
| } |
| |
| no_dfs_flag = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG]); |
| |
| hdd_debug("DFS flag: %d", no_dfs_flag); |
| |
| if (no_dfs_flag > 1) { |
| hdd_err("invalid value of dfs flag"); |
| return -EINVAL; |
| } |
| ucfg_scan_cfg_get_dfs_chan_scan_allowed(hdd_ctx->psoc, |
| &enable_dfs_scan); |
| |
| if (enable_dfs_scan) { |
| ret_val = wlan_hdd_enable_dfs_chan_scan(hdd_ctx, !no_dfs_flag); |
| } else { |
| if ((!no_dfs_flag) != enable_dfs_scan) { |
| hdd_err("DFS chan ini configured %d, no dfs flag: %d", |
| enable_dfs_scan, |
| no_dfs_flag); |
| return -EINVAL; |
| } |
| } |
| |
| return ret_val; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_disable_dfs_chan_scan () - DFS scan vendor command |
| * |
| * @wiphy: wiphy device pointer |
| * @wdev: wireless device pointer |
| * @data: Vendor command data buffer |
| * @data_len: Buffer length |
| * |
| * Handles QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX. Validate it and |
| * call wlan_hdd_disable_dfs_chan_scan to send it to firmware. |
| * |
| * Return: EOK or other error codes. |
| */ |
| |
| static int wlan_hdd_cfg80211_disable_dfs_chan_scan(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_disable_dfs_chan_scan(wiphy, wdev, |
| data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| const struct nla_policy |
| wlan_hdd_wisa_cmd_policy[QCA_WLAN_VENDOR_ATTR_WISA_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_WISA_MODE] = {.type = NLA_U32 }, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_handle_wisa_cmd() - Handle WISA vendor cmd |
| * @wiphy: wiphy device pointer |
| * @wdev: wireless device pointer |
| * @data: Vendor command data buffer |
| * @data_len: Buffer length |
| * |
| * Handles QCA_WLAN_VENDOR_SUBCMD_WISA. Validate cmd attributes and |
| * setup WISA Mode features. |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int __wlan_hdd_cfg80211_handle_wisa_cmd(struct wiphy *wiphy, |
| struct wireless_dev *wdev, const void *data, int data_len) |
| { |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_WISA_MAX + 1]; |
| struct sir_wisa_params wisa; |
| int ret_val; |
| QDF_STATUS status; |
| bool wisa_mode; |
| void *soc = cds_get_context(QDF_MODULE_ID_SOC); |
| mac_handle_t mac_handle; |
| |
| hdd_enter_dev(dev); |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| goto err; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_WISA_MAX, data, |
| data_len, wlan_hdd_wisa_cmd_policy)) { |
| hdd_err("Invalid WISA cmd attributes"); |
| ret_val = -EINVAL; |
| goto err; |
| } |
| if (!tb[QCA_WLAN_VENDOR_ATTR_WISA_MODE]) { |
| hdd_err("Invalid WISA mode"); |
| ret_val = -EINVAL; |
| goto err; |
| } |
| |
| wisa_mode = !!nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_WISA_MODE]); |
| hdd_debug("WISA Mode: %d", wisa_mode); |
| wisa.mode = wisa_mode; |
| wisa.vdev_id = adapter->vdev_id; |
| mac_handle = hdd_ctx->mac_handle; |
| status = sme_set_wisa_params(mac_handle, &wisa); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("Unable to set WISA mode: %d to FW", wisa_mode); |
| ret_val = -EINVAL; |
| } |
| if (QDF_IS_STATUS_SUCCESS(status) || !wisa_mode) |
| cdp_set_wisa_mode(soc, adapter->vdev_id, wisa_mode); |
| err: |
| hdd_exit(); |
| return ret_val; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_handle_wisa_cmd() - Handle WISA vendor cmd |
| * @wiphy: corestack handler |
| * @wdev: wireless device |
| * @data: data |
| * @data_len: data length |
| * |
| * Handles QCA_WLAN_VENDOR_SUBCMD_WISA. Validate cmd attributes and |
| * setup WISA mode features. |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int wlan_hdd_cfg80211_handle_wisa_cmd(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_handle_wisa_cmd(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| struct hdd_station_info *hdd_get_stainfo(struct hdd_station_info *astainfo, |
| struct qdf_mac_addr mac_addr) |
| { |
| struct hdd_station_info *stainfo = NULL; |
| int i; |
| |
| for (i = 0; i < WLAN_MAX_STA_COUNT; i++) { |
| if (!qdf_mem_cmp(&astainfo[i].sta_mac, |
| &mac_addr, |
| QDF_MAC_ADDR_SIZE)) { |
| stainfo = &astainfo[i]; |
| break; |
| } |
| } |
| |
| return stainfo; |
| } |
| |
| /* |
| * undef short names defined for get station command |
| * used by __wlan_hdd_cfg80211_get_station_cmd() |
| */ |
| #undef STATION_INVALID |
| #undef STATION_INFO |
| #undef STATION_ASSOC_FAIL_REASON |
| #undef STATION_REMOTE |
| #undef STATION_MAX |
| #undef LINK_INFO_STANDARD_NL80211_ATTR |
| #undef AP_INFO_STANDARD_NL80211_ATTR |
| #undef INFO_ROAM_COUNT |
| #undef INFO_AKM |
| #undef WLAN802_11_MODE |
| #undef AP_INFO_HS20_INDICATION |
| #undef HT_OPERATION |
| #undef VHT_OPERATION |
| #undef INFO_ASSOC_FAIL_REASON |
| #undef REMOTE_MAX_PHY_RATE |
| #undef REMOTE_TX_PACKETS |
| #undef REMOTE_TX_BYTES |
| #undef REMOTE_RX_PACKETS |
| #undef REMOTE_RX_BYTES |
| #undef REMOTE_LAST_TX_RATE |
| #undef REMOTE_LAST_RX_RATE |
| #undef REMOTE_WMM |
| #undef REMOTE_SUPPORTED_MODE |
| #undef REMOTE_AMPDU |
| #undef REMOTE_TX_STBC |
| #undef REMOTE_RX_STBC |
| #undef REMOTE_CH_WIDTH |
| #undef REMOTE_SGI_ENABLE |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) |
| #undef REMOTE_PAD |
| #endif |
| |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| /** |
| * __wlan_hdd_cfg80211_keymgmt_set_key() - Store the Keys in the driver session |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the Key data |
| * @data_len:Length of the data passed |
| * |
| * This is called when wlan driver needs to save the keys received via |
| * vendor specific command. |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int __wlan_hdd_cfg80211_keymgmt_set_key(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *hdd_adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx; |
| struct hdd_station_ctx *sta_ctx = |
| WLAN_HDD_GET_STATION_CTX_PTR(hdd_adapter); |
| struct wlan_crypto_pmksa pmksa; |
| int status; |
| mac_handle_t mac_handle; |
| |
| hdd_enter_dev(dev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| if ((!data) || (data_len <= 0) || |
| (data_len > ROAM_SCAN_PSK_SIZE)) { |
| hdd_err("Invalid data"); |
| return -EINVAL; |
| } |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(hdd_adapter); |
| if (!hdd_ctx) { |
| hdd_err("HDD context is null"); |
| return -EINVAL; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (status) |
| return status; |
| |
| mac_handle = hdd_ctx->mac_handle; |
| qdf_mem_zero(&pmksa, sizeof(pmksa)); |
| pmksa.pmk_len = data_len; |
| qdf_mem_copy(pmksa.pmk, data, data_len); |
| |
| qdf_mem_copy(&pmksa.bssid, &sta_ctx->conn_info.bssid, |
| QDF_MAC_ADDR_SIZE); |
| |
| sme_roam_set_psk_pmk(mac_handle, &pmksa, hdd_adapter->vdev_id, true); |
| qdf_mem_zero(&pmksa, sizeof(pmksa)); |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_keymgmt_set_key() - Store the Keys in the driver session |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the Key data |
| * @data_len:Length of the data passed |
| * |
| * This is called when wlan driver needs to save the keys received via |
| * vendor specific command. |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int wlan_hdd_cfg80211_keymgmt_set_key(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_keymgmt_set_key(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| |
| const struct nla_policy qca_wlan_vendor_get_wifi_info_policy[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX] = {.type = NLA_U32 }, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_get_wifi_info() - Get the wifi driver related info |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the data to be passed via vendor interface |
| * @data_len:Length of the data to be passed |
| * |
| * This is called when wlan driver needs to send wifi driver related info |
| * (driver/fw version) to the user space application upon request. |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int |
| __wlan_hdd_cfg80211_get_wifi_info(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX + 1]; |
| tSirVersionString driver_version; |
| tSirVersionString firmware_version; |
| int status; |
| struct sk_buff *reply_skb; |
| uint32_t skb_len = 0, count = 0; |
| |
| hdd_enter_dev(wdev->netdev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (status) |
| return status; |
| |
| if (wlan_cfg80211_nla_parse(tb_vendor, |
| QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX, |
| data, data_len, |
| qca_wlan_vendor_get_wifi_info_policy)) { |
| hdd_err("WIFI_INFO_GET NL CMD parsing failed"); |
| return -EINVAL; |
| } |
| |
| if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION]) { |
| hdd_debug("Rcvd req for Driver version"); |
| strlcpy(driver_version, QWLAN_VERSIONSTR, |
| sizeof(driver_version)); |
| skb_len += strlen(driver_version) + 1; |
| count++; |
| } |
| |
| if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION]) { |
| hdd_debug("Rcvd req for FW version"); |
| snprintf(firmware_version, sizeof(firmware_version), |
| "FW:%d.%d.%d.%d.%d.%d HW:%s", |
| hdd_ctx->fw_version_info.major_spid, |
| hdd_ctx->fw_version_info.minor_spid, |
| hdd_ctx->fw_version_info.siid, |
| hdd_ctx->fw_version_info.rel_id, |
| hdd_ctx->fw_version_info.crmid, |
| hdd_ctx->fw_version_info.sub_id, |
| hdd_ctx->target_hw_name); |
| skb_len += strlen(firmware_version) + 1; |
| count++; |
| } |
| |
| if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX]) { |
| hdd_debug("Rcvd req for Radio index"); |
| skb_len += sizeof(uint32_t); |
| count++; |
| } |
| |
| if (count == 0) { |
| hdd_err("unknown attribute in get_wifi_info request"); |
| return -EINVAL; |
| } |
| |
| skb_len += (NLA_HDRLEN * count) + NLMSG_HDRLEN; |
| reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, skb_len); |
| |
| if (!reply_skb) { |
| hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| return -ENOMEM; |
| } |
| |
| if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION]) { |
| if (nla_put_string(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION, |
| driver_version)) |
| goto error_nla_fail; |
| } |
| |
| if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION]) { |
| if (nla_put_string(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION, |
| firmware_version)) |
| goto error_nla_fail; |
| } |
| |
| if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX]) { |
| if (nla_put_u32(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX, |
| hdd_ctx->radio_index)) |
| goto error_nla_fail; |
| } |
| |
| return cfg80211_vendor_cmd_reply(reply_skb); |
| |
| error_nla_fail: |
| hdd_err("nla put fail"); |
| kfree_skb(reply_skb); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_get_wifi_info() - Get the wifi driver related info |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the data to be passed via vendor interface |
| * @data_len:Length of the data to be passed |
| * |
| * This is called when wlan driver needs to send wifi driver related info |
| * (driver/fw version) to the user space application upon request. |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int |
| wlan_hdd_cfg80211_get_wifi_info(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_wifi_info(wiphy, wdev, data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| const struct nla_policy get_logger_set_policy[ |
| QCA_WLAN_VENDOR_ATTR_LOGGER_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED] = {.type = NLA_U32}, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_get_logger_supp_feature() - Get the wifi logger features |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the data to be passed via vendor interface |
| * @data_len:Length of the data to be passed |
| * |
| * This is called by userspace to know the supported logger features |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int |
| __wlan_hdd_cfg80211_get_logger_supp_feature(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| int status; |
| uint32_t features; |
| struct sk_buff *reply_skb = NULL; |
| bool enable_ring_buffer; |
| |
| hdd_enter_dev(wdev->netdev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (status) |
| return status; |
| |
| features = 0; |
| wlan_mlme_get_status_ring_buffer(hdd_ctx->psoc, &enable_ring_buffer); |
| if (enable_ring_buffer) { |
| features |= WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_SUPPORTED; |
| features |= WIFI_LOGGER_CONNECT_EVENT_SUPPORTED; |
| features |= WIFI_LOGGER_WAKE_LOCK_SUPPORTED; |
| features |= WIFI_LOGGER_DRIVER_DUMP_SUPPORTED; |
| features |= WIFI_LOGGER_PACKET_FATE_SUPPORTED; |
| hdd_debug("Supported logger features: 0x%0x", features); |
| } else { |
| hdd_info("Ring buffer disable"); |
| } |
| |
| reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, |
| sizeof(uint32_t) + NLA_HDRLEN + NLMSG_HDRLEN); |
| if (!reply_skb) { |
| hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| return -ENOMEM; |
| } |
| |
| if (nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED, |
| features)) { |
| hdd_err("nla put fail"); |
| kfree_skb(reply_skb); |
| return -EINVAL; |
| } |
| |
| return cfg80211_vendor_cmd_reply(reply_skb); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_get_logger_supp_feature() - Get the wifi logger features |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the data to be passed via vendor interface |
| * @data_len:Length of the data to be passed |
| * |
| * This is called by userspace to know the supported logger features |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int |
| wlan_hdd_cfg80211_get_logger_supp_feature(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_logger_supp_feature(wiphy, wdev, |
| data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| #ifdef WLAN_FEATURE_GTK_OFFLOAD |
| void wlan_hdd_save_gtk_offload_params(struct hdd_adapter *adapter, |
| uint8_t *kck_ptr, uint8_t kck_len, |
| uint8_t *kek_ptr, uint32_t kek_len, |
| uint8_t *replay_ctr, bool big_endian) |
| { |
| struct hdd_station_ctx *hdd_sta_ctx; |
| uint8_t *buf; |
| int i; |
| struct pmo_gtk_req *gtk_req = NULL; |
| struct wlan_objmgr_vdev *vdev; |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| |
| gtk_req = qdf_mem_malloc(sizeof(*gtk_req)); |
| if (!gtk_req) |
| return; |
| |
| hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| if (kck_ptr) { |
| if (kck_len > sizeof(gtk_req->kck)) { |
| kck_len = sizeof(gtk_req->kck); |
| QDF_ASSERT(0); |
| } |
| qdf_mem_copy(gtk_req->kck, kck_ptr, kck_len); |
| gtk_req->kck_len = kck_len; |
| } |
| |
| if (kek_ptr) { |
| /* paranoia */ |
| if (kek_len > sizeof(gtk_req->kek)) { |
| kek_len = sizeof(gtk_req->kek); |
| QDF_ASSERT(0); |
| } |
| qdf_mem_copy(gtk_req->kek, kek_ptr, kek_len); |
| } |
| |
| qdf_copy_macaddr(>k_req->bssid, &hdd_sta_ctx->conn_info.bssid); |
| |
| gtk_req->kek_len = kek_len; |
| gtk_req->is_fils_connection = hdd_is_fils_connection(hdd_ctx, adapter); |
| |
| /* convert big to little endian since driver work on little endian */ |
| buf = (uint8_t *)>k_req->replay_counter; |
| for (i = 0; i < 8; i++) |
| buf[7 - i] = replay_ctr[i]; |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_POWER_ID); |
| if (!vdev) |
| goto end; |
| status = ucfg_pmo_cache_gtk_offload_req(vdev, gtk_req); |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_POWER_ID); |
| if (status != QDF_STATUS_SUCCESS) |
| hdd_err("Failed to cache GTK Offload"); |
| |
| end: |
| qdf_mem_free(gtk_req); |
| } |
| #endif |
| |
| #ifdef WLAN_CFR_ENABLE |
| void hdd_cfr_data_send_nl_event(uint8_t vdev_id, uint32_t pid, |
| const void *data, uint32_t data_len) |
| { |
| uint32_t len, ret; |
| struct sk_buff *vendor_event; |
| struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); |
| struct hdd_adapter *adapter; |
| struct nlmsghdr *nlhdr = NULL; |
| |
| if (!hdd_ctx) { |
| hdd_err("HDD context is NULL"); |
| return; |
| } |
| |
| adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); |
| if (!adapter) { |
| hdd_err("adapter NULL for vdev id %d", vdev_id); |
| return; |
| } |
| |
| hdd_debug("vdev id %d pid %d data len %d", vdev_id, pid, data_len); |
| len = nla_total_size(data_len) + NLMSG_HDRLEN; |
| vendor_event = cfg80211_vendor_event_alloc( |
| hdd_ctx->wiphy, &adapter->wdev, len, |
| QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG_INDEX, |
| GFP_KERNEL); |
| |
| if (!vendor_event) { |
| hdd_err("cfg80211_vendor_event_alloc failed vdev id %d, data len %d", |
| vdev_id, data_len); |
| return; |
| } |
| |
| ret = nla_put(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA, |
| data_len, data); |
| if (ret) { |
| hdd_err("CFR event put fails status %d", ret); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| if (pid) { |
| nlhdr = nlmsg_hdr(vendor_event); |
| if (nlhdr) |
| nlhdr->nlmsg_pid = pid; |
| else |
| hdd_err_rl("nlhdr is null"); |
| } |
| |
| cfg80211_vendor_event(vendor_event, GFP_ATOMIC); |
| } |
| #endif |
| |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| void hdd_send_roam_scan_ch_list_event(struct hdd_context *hdd_ctx, |
| uint8_t vdev_id, uint16_t buf_len, |
| uint8_t *buf) |
| { |
| struct sk_buff *vendor_event; |
| struct hdd_adapter *adapter; |
| uint32_t len, ret; |
| |
| if (!hdd_ctx) { |
| hdd_err_rl("hdd context is null"); |
| return; |
| } |
| |
| adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); |
| if (!adapter) |
| return; |
| |
| len = nla_total_size(buf_len) + NLMSG_HDRLEN; |
| vendor_event = |
| cfg80211_vendor_event_alloc( |
| hdd_ctx->wiphy, &(adapter->wdev), len, |
| QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO_INDEX, |
| GFP_KERNEL); |
| |
| if (!vendor_event) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return; |
| } |
| |
| ret = nla_put(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_UPDATE_STA_INFO_CONNECT_CHANNELS, |
| buf_len, buf); |
| if (ret) { |
| hdd_err("OEM event put fails status %d", ret); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| cfg80211_vendor_event(vendor_event, GFP_KERNEL); |
| } |
| #endif |
| |
| #define ANT_DIV_SET_PERIOD(probe_period, stay_period) \ |
| ((1 << 26) | \ |
| (((probe_period) & 0x1fff) << 13) | \ |
| ((stay_period) & 0x1fff)) |
| |
| #define ANT_DIV_SET_SNR_DIFF(snr_diff) \ |
| ((1 << 27) | \ |
| ((snr_diff) & 0x1fff)) |
| |
| #define ANT_DIV_SET_PROBE_DWELL_TIME(probe_dwell_time) \ |
| ((1 << 28) | \ |
| ((probe_dwell_time) & 0x1fff)) |
| |
| #define ANT_DIV_SET_WEIGHT(mgmt_snr_weight, data_snr_weight, ack_snr_weight) \ |
| ((1 << 29) | \ |
| (((mgmt_snr_weight) & 0xff) << 16) | \ |
| (((data_snr_weight) & 0xff) << 8) | \ |
| ((ack_snr_weight) & 0xff)) |
| |
| #define RX_REORDER_TIMEOUT_VOICE \ |
| QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE |
| #define RX_REORDER_TIMEOUT_VIDEO \ |
| QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO |
| #define RX_REORDER_TIMEOUT_BESTEFFORT \ |
| QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT |
| #define RX_REORDER_TIMEOUT_BACKGROUND \ |
| QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND |
| #define RX_BLOCKSIZE_PEER_MAC \ |
| QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC |
| #define RX_BLOCKSIZE_WINLIMIT \ |
| QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT |
| |
| const struct nla_policy wlan_hdd_wifi_config_policy[ |
| QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES] = {.type = NLA_BINARY}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_COMMAND] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_VALUE] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_LENGTH] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_FLAGS] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC] = |
| VENDOR_NLA_POLICY_MAC_ADDR, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_24] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_5] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_REPORT_FAIL] = {.type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_CONF_TX_RATE] = {.type = NLA_U16}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_MODULATED_DTIM] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR] = {.type = NLA_U16 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY] = { |
| .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL] = {.type = NLA_U8}, |
| [RX_REORDER_TIMEOUT_VOICE] = {.type = NLA_U32}, |
| [RX_REORDER_TIMEOUT_VIDEO] = {.type = NLA_U32}, |
| [RX_REORDER_TIMEOUT_BESTEFFORT] = {.type = NLA_U32}, |
| [RX_REORDER_TIMEOUT_BACKGROUND] = {.type = NLA_U32}, |
| [RX_BLOCKSIZE_PEER_MAC] = VENDOR_NLA_POLICY_MAC_ADDR, |
| [RX_BLOCKSIZE_WINLIMIT] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_LRO] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL] = { |
| .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL] = {.type = NLA_U16 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE] = {.type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_GTX] = {.type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ELNA_BYPASS] = {.type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST] = { |
| .type = NLA_BINARY, |
| .len = WLAN_MAX_IE_LEN + 2}, |
| [QCA_WLAN_VENDOR_ATTR_DISCONNECT_IES] = { |
| .type = NLA_BINARY, |
| .len = SIR_MAC_MAX_ADD_IE_LENGTH + 2}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ROAM_REASON] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MSDU_AGGREGATION] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MSDU_AGGREGATION] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_LDPC] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_TX_STBC] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_RX_STBC] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_PHY_MODE] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_WIDTH] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_BW] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_NSS] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT] = { |
| .type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE] = { |
| .type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_SETTING] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_LEVEL] = {.type = NLA_S32 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_CONCURRENT_STA_PRIMARY] = { |
| .type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_FT_OVER_DS] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_ARP_NS_OFFLOAD] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_WFC_STATE] = { |
| .type = NLA_U8 }, |
| }; |
| |
| static const struct nla_policy |
| qca_wlan_vendor_attr_he_omi_tx_policy [QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE] = {.type = NLA_U8 }, |
| }; |
| |
| static const struct nla_policy |
| wlan_oci_override_policy [QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FRAME_TYPE] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FREQUENCY] = {.type = NLA_U32 }, |
| }; |
| |
| const struct nla_policy |
| wlan_hdd_wifi_test_config_policy[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_KEEP_ALIVE_FRAME_TYPE] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE] = { |
| .type = NLA_U16}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_RX_CTRL_FRAME_TO_MBSS] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BCAST_TWT_SUPPORT] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AC] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_BW] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_NSS] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG] = { |
| .type = NLA_FLAG}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_SUPPDU] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_USE_BSSID_IN_PROBE_REQ_RA] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX] = { |
| .type = NLA_NESTED}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS] |
| = {.type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP] = { |
| .type = NLA_NESTED}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_TERMINATE] = { |
| .type = NLA_NESTED}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BSS_MAX_IDLE_PERIOD_ENABLE] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_PMF_PROTECTION] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_DISABLE_DATA_MGMT_RSP_TX] |
| = {.type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BSS_MAX_IDLE_PERIOD] = { |
| .type = NLA_U16}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ER_SU_PPDU_TYPE] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_PUNCTURED_PREAMBLE_RX] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_DISASSOC_TX] = { |
| .type = NLA_FLAG}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FT_REASSOCREQ_RSNXE_USED] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_CSA] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OCI_OVERRIDE] = { |
| .type = NLA_NESTED}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_SA_QUERY_TIMEOUT] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FILS_DISCOVERY_FRAMES_TX] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FULL_BW_UL_MU_MIMO] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_RU_242_TONE_TX] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_6GHZ_SECURITY_TEST_MODE] |
| = {.type = NLA_U8}, |
| }; |
| |
| /** |
| * wlan_hdd_save_default_scan_ies() - API to store the default scan IEs |
| * @hdd_ctx: HDD context |
| * @adapter: Pointer to HDD adapter |
| * @ie_data: Pointer to Scan IEs buffer |
| * @ie_len: Length of Scan IEs |
| * |
| * This API is used to store the default scan ies received from |
| * supplicant. Also saves QCN IE if g_qcn_ie_support INI is enabled |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int wlan_hdd_save_default_scan_ies(struct hdd_context *hdd_ctx, |
| struct hdd_adapter *adapter, |
| uint8_t *ie_data, uint16_t ie_len) |
| { |
| struct hdd_scan_info *scan_info = &adapter->scan_info; |
| bool add_qcn_ie; |
| |
| if (!scan_info) |
| return -EINVAL; |
| |
| if (scan_info->default_scan_ies) { |
| qdf_mem_free(scan_info->default_scan_ies); |
| scan_info->default_scan_ies = NULL; |
| } |
| |
| scan_info->default_scan_ies_len = ie_len; |
| ucfg_mlme_get_qcn_ie_support(hdd_ctx->psoc, &add_qcn_ie); |
| if (add_qcn_ie) |
| ie_len += (QCN_IE_HDR_LEN + QCN_IE_VERSION_SUBATTR_LEN); |
| |
| scan_info->default_scan_ies = qdf_mem_malloc(ie_len); |
| if (!scan_info->default_scan_ies) { |
| scan_info->default_scan_ies_len = 0; |
| return -ENOMEM; |
| } |
| |
| qdf_mem_copy(scan_info->default_scan_ies, ie_data, |
| scan_info->default_scan_ies_len); |
| |
| /* Add QCN IE if g_qcn_ie_support INI is enabled */ |
| if (add_qcn_ie) |
| sme_add_qcn_ie(hdd_ctx->mac_handle, |
| scan_info->default_scan_ies, |
| &scan_info->default_scan_ies_len); |
| |
| hdd_debug("Saved default scan IE:len %d", |
| scan_info->default_scan_ies_len); |
| qdf_trace_hex_dump(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG, |
| (uint8_t *) scan_info->default_scan_ies, |
| scan_info->default_scan_ies_len); |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_handle_restrict_offchan_config() - |
| * Handle wifi configuration attribute : |
| * QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL |
| * @adapter: Pointer to HDD adapter |
| * @restrict_offchan: Restrict offchannel setting done by |
| * application |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int wlan_hdd_handle_restrict_offchan_config(struct hdd_adapter *adapter, |
| u8 restrict_offchan) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| enum QDF_OPMODE dev_mode = adapter->device_mode; |
| struct wlan_objmgr_vdev *vdev; |
| int ret_val = 0; |
| |
| if (!(dev_mode == QDF_SAP_MODE || dev_mode == QDF_P2P_GO_MODE)) { |
| hdd_err("Invalid interface type:%d", dev_mode); |
| return -EINVAL; |
| } |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_ID); |
| if (!vdev) |
| return -EINVAL; |
| if (restrict_offchan == 1) { |
| enum policy_mgr_con_mode pmode = |
| policy_mgr_convert_device_mode_to_qdf_type(dev_mode); |
| uint32_t freq; |
| |
| u32 vdev_id = wlan_vdev_get_id(vdev); |
| |
| wlan_vdev_obj_lock(vdev); |
| wlan_vdev_mlme_cap_set(vdev, WLAN_VDEV_C_RESTRICT_OFFCHAN); |
| wlan_vdev_obj_unlock(vdev); |
| freq = policy_mgr_get_channel(hdd_ctx->psoc, pmode, &vdev_id); |
| if (!freq || |
| wlan_hdd_send_avoid_freq_for_dnbs(hdd_ctx, freq)) { |
| hdd_err("unable to send avoid_freq"); |
| ret_val = -EINVAL; |
| } |
| hdd_info("vdev %d mode %d dnbs enabled", vdev_id, dev_mode); |
| } else if (restrict_offchan == 0) { |
| wlan_vdev_obj_lock(vdev); |
| wlan_vdev_mlme_cap_clear(vdev, WLAN_VDEV_C_RESTRICT_OFFCHAN); |
| wlan_vdev_obj_unlock(vdev); |
| if (wlan_hdd_send_avoid_freq_for_dnbs(hdd_ctx, 0)) { |
| hdd_err("unable to clear avoid_freq"); |
| ret_val = -EINVAL; |
| } |
| hdd_info("vdev mode %d dnbs disabled", dev_mode); |
| } else { |
| ret_val = -EINVAL; |
| hdd_err("Invalid RESTRICT_OFFCHAN setting"); |
| } |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| return ret_val; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_wifi_set_reorder_timeout() - set reorder timeout |
| * @adapter: Pointer to HDD adapter |
| * @tb: array of pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static |
| int wlan_hdd_cfg80211_wifi_set_reorder_timeout(struct hdd_adapter *adapter, |
| struct nlattr *tb[]) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| int ret_val = 0; |
| QDF_STATUS qdf_status; |
| struct sir_set_rx_reorder_timeout_val reorder_timeout; |
| mac_handle_t mac_handle; |
| |
| #define RX_TIMEOUT_VAL_MIN 10 |
| #define RX_TIMEOUT_VAL_MAX 1000 |
| |
| if (tb[RX_REORDER_TIMEOUT_VOICE] || |
| tb[RX_REORDER_TIMEOUT_VIDEO] || |
| tb[RX_REORDER_TIMEOUT_BESTEFFORT] || |
| tb[RX_REORDER_TIMEOUT_BACKGROUND]) { |
| |
| /* if one is specified, all must be specified */ |
| if (!tb[RX_REORDER_TIMEOUT_VOICE] || |
| !tb[RX_REORDER_TIMEOUT_VIDEO] || |
| !tb[RX_REORDER_TIMEOUT_BESTEFFORT] || |
| !tb[RX_REORDER_TIMEOUT_BACKGROUND]) { |
| hdd_err("four AC timeout val are required MAC"); |
| return -EINVAL; |
| } |
| |
| reorder_timeout.rx_timeout_pri[0] = nla_get_u32( |
| tb[RX_REORDER_TIMEOUT_VOICE]); |
| reorder_timeout.rx_timeout_pri[1] = nla_get_u32( |
| tb[RX_REORDER_TIMEOUT_VIDEO]); |
| reorder_timeout.rx_timeout_pri[2] = nla_get_u32( |
| tb[RX_REORDER_TIMEOUT_BESTEFFORT]); |
| reorder_timeout.rx_timeout_pri[3] = nla_get_u32( |
| tb[RX_REORDER_TIMEOUT_BACKGROUND]); |
| /* timeout value is required to be in the rang 10 to 1000ms */ |
| if (reorder_timeout.rx_timeout_pri[0] >= RX_TIMEOUT_VAL_MIN && |
| reorder_timeout.rx_timeout_pri[0] <= RX_TIMEOUT_VAL_MAX && |
| reorder_timeout.rx_timeout_pri[1] >= RX_TIMEOUT_VAL_MIN && |
| reorder_timeout.rx_timeout_pri[1] <= RX_TIMEOUT_VAL_MAX && |
| reorder_timeout.rx_timeout_pri[2] >= RX_TIMEOUT_VAL_MIN && |
| reorder_timeout.rx_timeout_pri[2] <= RX_TIMEOUT_VAL_MAX && |
| reorder_timeout.rx_timeout_pri[3] >= RX_TIMEOUT_VAL_MIN && |
| reorder_timeout.rx_timeout_pri[3] <= RX_TIMEOUT_VAL_MAX) { |
| mac_handle = hdd_ctx->mac_handle; |
| qdf_status = sme_set_reorder_timeout(mac_handle, |
| &reorder_timeout); |
| if (qdf_status != QDF_STATUS_SUCCESS) { |
| hdd_err("failed to set reorder timeout err %d", |
| qdf_status); |
| ret_val = -EPERM; |
| } |
| } else { |
| hdd_err("one of the timeout value is not in range"); |
| ret_val = -EINVAL; |
| } |
| } |
| |
| return ret_val; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_wifi_set_rx_blocksize() - set rx blocksize |
| * @adapter: hdd adapter |
| * @tb: array of pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int wlan_hdd_cfg80211_wifi_set_rx_blocksize(struct hdd_adapter *adapter, |
| struct nlattr *tb[]) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| int ret_val = 0; |
| uint32_t set_value; |
| QDF_STATUS qdf_status; |
| struct sir_peer_set_rx_blocksize rx_blocksize; |
| mac_handle_t mac_handle; |
| |
| #define WINDOW_SIZE_VAL_MIN 1 |
| #define WINDOW_SIZE_VAL_MAX 64 |
| |
| if (tb[RX_BLOCKSIZE_PEER_MAC] || |
| tb[RX_BLOCKSIZE_WINLIMIT]) { |
| |
| /* if one is specified, both must be specified */ |
| if (!tb[RX_BLOCKSIZE_PEER_MAC] || |
| !tb[RX_BLOCKSIZE_WINLIMIT]) { |
| hdd_err("Both Peer MAC and windows limit required"); |
| return -EINVAL; |
| } |
| |
| memcpy(&rx_blocksize.peer_macaddr, |
| nla_data(tb[RX_BLOCKSIZE_PEER_MAC]), |
| sizeof(rx_blocksize.peer_macaddr)), |
| |
| rx_blocksize.vdev_id = adapter->vdev_id; |
| set_value = nla_get_u32(tb[RX_BLOCKSIZE_WINLIMIT]); |
| /* maximum window size is 64 */ |
| if (set_value >= WINDOW_SIZE_VAL_MIN && |
| set_value <= WINDOW_SIZE_VAL_MAX) { |
| rx_blocksize.rx_block_ack_win_limit = set_value; |
| mac_handle = hdd_ctx->mac_handle; |
| qdf_status = sme_set_rx_set_blocksize(mac_handle, |
| &rx_blocksize); |
| if (qdf_status != QDF_STATUS_SUCCESS) { |
| hdd_err("failed to set aggr sizes err %d", |
| qdf_status); |
| ret_val = -EPERM; |
| } |
| } else { |
| hdd_err("window size val is not in range"); |
| ret_val = -EINVAL; |
| } |
| } |
| |
| return ret_val; |
| } |
| |
| int hdd_set_phy_mode(struct hdd_adapter *adapter, |
| enum qca_wlan_vendor_phy_mode vendor_phy_mode) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; |
| eCsrPhyMode phymode; |
| uint8_t supported_band; |
| uint32_t bonding_mode; |
| int ret = 0; |
| |
| if (!psoc) { |
| hdd_err("psoc is NULL"); |
| return -EINVAL; |
| } |
| |
| ret = hdd_vendor_mode_to_phymode(vendor_phy_mode, &phymode); |
| if (ret < 0) |
| return ret; |
| |
| ret = hdd_vendor_mode_to_band(vendor_phy_mode, &supported_band, |
| wlan_reg_is_6ghz_supported(psoc)); |
| if (ret < 0) |
| return ret; |
| |
| ret = hdd_vendor_mode_to_bonding_mode(vendor_phy_mode, &bonding_mode); |
| if (ret < 0) |
| return ret; |
| |
| return hdd_update_phymode(adapter, phymode, supported_band, |
| bonding_mode); |
| } |
| |
| /** |
| * hdd_config_phy_mode() - set PHY mode |
| * @adapter: hdd adapter |
| * @attr: nla attr sent from userspace |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_config_phy_mode(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| enum qca_wlan_vendor_phy_mode vendor_phy_mode; |
| |
| vendor_phy_mode = nla_get_u32(attr); |
| |
| return hdd_set_phy_mode(adapter, vendor_phy_mode); |
| } |
| |
| /** |
| * hdd_set_roam_reason_vsie_status() - enable/disable inclusion of |
| * roam reason vsie in Reassoc |
| * |
| * @adapter: hdd adapter |
| * @attr: nla attr sent by supplicant |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| static int hdd_set_roam_reason_vsie_status(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t roam_reason_vsie_enabled; |
| int errno; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| struct hdd_context *hdd_ctx = NULL; |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (!hdd_ctx) { |
| hdd_err("hdd_ctx failure"); |
| return -EINVAL; |
| } |
| |
| roam_reason_vsie_enabled = nla_get_u8(attr); |
| if (roam_reason_vsie_enabled > 1) |
| roam_reason_vsie_enabled = 1; |
| |
| status = |
| ucfg_mlme_set_roam_reason_vsie_status(hdd_ctx->psoc, |
| roam_reason_vsie_enabled); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("set roam reason vsie failed"); |
| return -EINVAL; |
| } |
| |
| errno = sme_cli_set_command |
| (adapter->vdev_id, |
| WMI_VDEV_PARAM_ENABLE_DISABLE_ROAM_REASON_VSIE, |
| roam_reason_vsie_enabled, VDEV_CMD); |
| if (errno) { |
| hdd_err("Failed to set beacon report error vsie"); |
| status = QDF_STATUS_E_FAILURE; |
| } |
| |
| return qdf_status_to_os_return(status); |
| } |
| #else |
| static int hdd_set_roam_reason_vsie_status(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| return -ENOTSUPP; |
| } |
| #endif |
| |
| static int hdd_set_ft_over_ds(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t ft_over_ds_enable; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| struct hdd_context *hdd_ctx = NULL; |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (!hdd_ctx) { |
| hdd_err("hdd_ctx failure"); |
| return -EINVAL; |
| } |
| |
| ft_over_ds_enable = nla_get_u8(attr); |
| |
| if (ft_over_ds_enable != 0 && ft_over_ds_enable != 1) { |
| hdd_err_rl("Invalid ft_over_ds_enable: %d", ft_over_ds_enable); |
| return -EINVAL; |
| } |
| |
| status = ucfg_mlme_set_ft_over_ds(hdd_ctx->psoc, ft_over_ds_enable); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("set ft_over_ds failed"); |
| return -EINVAL; |
| } |
| |
| return status; |
| } |
| |
| static int hdd_config_ldpc(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t ldpc; |
| int ret; |
| |
| ldpc = nla_get_u8(attr); |
| |
| ret = hdd_set_ldpc(adapter, ldpc); |
| |
| return ret; |
| } |
| |
| static int hdd_config_tx_stbc(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t tx_stbc; |
| int ret; |
| |
| tx_stbc = nla_get_u8(attr); |
| |
| ret = hdd_set_tx_stbc(adapter, tx_stbc); |
| |
| return ret; |
| } |
| |
| static int hdd_config_rx_stbc(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t rx_stbc; |
| int ret; |
| |
| rx_stbc = nla_get_u8(attr); |
| |
| ret = hdd_set_rx_stbc(adapter, rx_stbc); |
| |
| return ret; |
| } |
| |
| static int hdd_config_access_policy(struct hdd_adapter *adapter, |
| struct nlattr *tb[]) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| struct nlattr *policy_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY]; |
| struct nlattr *ielist_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST]; |
| uint32_t access_policy; |
| uint8_t ie[WLAN_MAX_IE_LEN + 2]; |
| QDF_STATUS status; |
| |
| /* nothing to do if neither attribute is present */ |
| if (!ielist_attr && !policy_attr) |
| return 0; |
| |
| /* if one is present, both must be present */ |
| if (!ielist_attr || !policy_attr) { |
| hdd_err("Missing attribute for %s", |
| policy_attr ? |
| "ACCESS_POLICY_IE_LIST" : "ACCESS_POLICY"); |
| return -EINVAL; |
| } |
| |
| /* validate the access policy */ |
| access_policy = nla_get_u32(policy_attr); |
| switch (access_policy) { |
| case QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED: |
| case QCA_ACCESS_POLICY_DENY_UNLESS_LISTED: |
| /* valid */ |
| break; |
| default: |
| hdd_err("Invalid value. access_policy %u", access_policy); |
| return -EINVAL; |
| } |
| |
| /* |
| * ie length is validated by the nla_policy. need to make a |
| * copy since SME will always read WLAN_MAX_IE_LEN+2 bytes |
| */ |
| nla_memcpy(ie, ielist_attr, sizeof(ie)); |
| |
| hdd_debug("calling sme_update_access_policy_vendor_ie"); |
| status = sme_update_access_policy_vendor_ie(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| ie, access_policy); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("Failed to set vendor ie and access policy, %d", |
| status); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| static int hdd_config_mpdu_aggregation(struct hdd_adapter *adapter, |
| struct nlattr *tb[]) |
| { |
| struct nlattr *tx_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION]; |
| struct nlattr *rx_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION]; |
| uint8_t tx_size, rx_size; |
| QDF_STATUS status; |
| |
| /* nothing to do if neither attribute is present */ |
| if (!tx_attr && !rx_attr) |
| return 0; |
| |
| /* if one is present, both must be present */ |
| if (!tx_attr || !rx_attr) { |
| hdd_err("Missing attribute for %s", |
| tx_attr ? "RX" : "TX"); |
| return -EINVAL; |
| } |
| |
| tx_size = nla_get_u8(tx_attr); |
| rx_size = nla_get_u8(rx_attr); |
| if (!cfg_in_range(CFG_TX_AGGREGATION_SIZE, tx_size) || |
| !cfg_in_range(CFG_RX_AGGREGATION_SIZE, rx_size)) { |
| hdd_err("TX %d RX %d MPDU aggr size not in range", |
| tx_size, rx_size); |
| |
| return -EINVAL; |
| } |
| |
| status = wma_set_tx_rx_aggr_size(adapter->vdev_id, |
| tx_size, |
| rx_size, |
| WMI_VDEV_CUSTOM_AGGR_TYPE_AMPDU); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| static int hdd_config_msdu_aggregation(struct hdd_adapter *adapter, |
| struct nlattr *tb[]) |
| { |
| struct nlattr *tx_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MSDU_AGGREGATION]; |
| struct nlattr *rx_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MSDU_AGGREGATION]; |
| uint8_t tx_size, rx_size; |
| QDF_STATUS status; |
| |
| /* nothing to do if neither attribute is present */ |
| if (!tx_attr && !rx_attr) |
| return 0; |
| |
| /* if one is present, both must be present */ |
| if (!tx_attr || !rx_attr) { |
| hdd_err("Missing attribute for %s", |
| tx_attr ? "RX" : "TX"); |
| return -EINVAL; |
| } |
| |
| tx_size = nla_get_u8(tx_attr); |
| rx_size = nla_get_u8(rx_attr); |
| if (!cfg_in_range(CFG_TX_AGGREGATION_SIZE, tx_size) || |
| !cfg_in_range(CFG_RX_AGGREGATION_SIZE, rx_size)) { |
| hdd_err("TX %d RX %d MSDU aggr size not in range", |
| tx_size, rx_size); |
| |
| return -EINVAL; |
| } |
| |
| status = wma_set_tx_rx_aggr_size(adapter->vdev_id, |
| tx_size, |
| rx_size, |
| WMI_VDEV_CUSTOM_AGGR_TYPE_AMSDU); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| static QDF_STATUS |
| hdd_populate_vdev_chains(struct wlan_mlme_nss_chains *nss_chains_cfg, |
| uint8_t tx_chains, |
| uint8_t rx_chains, |
| enum nss_chains_band_info band, |
| struct wlan_objmgr_vdev *vdev) |
| { |
| struct wlan_mlme_nss_chains *dynamic_cfg; |
| |
| nss_chains_cfg->num_rx_chains[band] = rx_chains; |
| nss_chains_cfg->num_tx_chains[band] = tx_chains; |
| |
| dynamic_cfg = ucfg_mlme_get_dynamic_vdev_config(vdev); |
| if (!dynamic_cfg) { |
| hdd_err("nss chain dynamic config NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| /* |
| * If user gives any nss value, then chains will be adjusted based on |
| * nss (in SME func sme_validate_user_nss_chain_params). |
| * If Chains are not suitable as per current NSS then, we need to |
| * return, and the below logic is added for the same. |
| */ |
| |
| if ((dynamic_cfg->rx_nss[band] > rx_chains) || |
| (dynamic_cfg->tx_nss[band] > tx_chains)) { |
| hdd_err("Chains less than nss, configure correct nss first."); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| int |
| hdd_set_dynamic_antenna_mode(struct hdd_adapter *adapter, |
| uint8_t num_rx_chains, |
| uint8_t num_tx_chains) |
| { |
| enum nss_chains_band_info band; |
| struct wlan_mlme_nss_chains user_cfg; |
| QDF_STATUS status; |
| mac_handle_t mac_handle; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| struct wlan_objmgr_vdev *vdev; |
| int ret; |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| mac_handle = hdd_ctx->mac_handle; |
| if (!mac_handle) { |
| hdd_err("NULL MAC handle"); |
| return -EINVAL; |
| } |
| |
| if (!hdd_is_vdev_in_conn_state(adapter)) { |
| hdd_debug("Vdev (id %d) not in connected/started state, cannot accept command", |
| adapter->vdev_id); |
| return -EINVAL; |
| } |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_ID); |
| if (!vdev) { |
| hdd_err("vdev is NULL"); |
| return -EINVAL; |
| } |
| |
| qdf_mem_zero(&user_cfg, sizeof(user_cfg)); |
| for (band = NSS_CHAINS_BAND_2GHZ; band < NSS_CHAINS_BAND_MAX; band++) { |
| status = hdd_populate_vdev_chains(&user_cfg, |
| num_rx_chains, |
| num_tx_chains, band, vdev); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| return -EINVAL; |
| } |
| } |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| |
| status = sme_nss_chains_update(mac_handle, |
| &user_cfg, |
| adapter->vdev_id); |
| if (QDF_IS_STATUS_ERROR(status)) |
| return -EINVAL; |
| |
| return 0; |
| } |
| |
| static int hdd_config_vdev_chains(struct hdd_adapter *adapter, |
| struct nlattr *tb[]) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint8_t tx_chains, rx_chains; |
| struct nlattr *tx_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS]; |
| struct nlattr *rx_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS]; |
| |
| if (!tx_attr && !rx_attr) |
| return 0; |
| |
| /* if one is present, both must be present */ |
| if (!tx_attr || !rx_attr) { |
| hdd_err("Missing attribute for %s", |
| tx_attr ? "RX" : "TX"); |
| return -EINVAL; |
| } |
| |
| tx_chains = nla_get_u8(tx_attr); |
| rx_chains = nla_get_u8(rx_attr); |
| |
| if (hdd_ctx->dynamic_nss_chains_support) |
| return hdd_set_dynamic_antenna_mode(adapter, rx_chains, |
| tx_chains); |
| return 0; |
| } |
| |
| static int hdd_config_tx_rx_nss(struct hdd_adapter *adapter, |
| struct nlattr *tb[]) |
| { |
| uint8_t tx_nss, rx_nss; |
| QDF_STATUS status; |
| |
| struct nlattr *tx_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS]; |
| struct nlattr *rx_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS]; |
| |
| if (!tx_attr && !rx_attr) |
| return 0; |
| |
| /* if one is present, both must be present */ |
| if (!tx_attr || !rx_attr) { |
| hdd_err("Missing attribute for %s", |
| tx_attr ? "RX" : "TX"); |
| return -EINVAL; |
| } |
| |
| tx_nss = nla_get_u8(tx_attr); |
| rx_nss = nla_get_u8(rx_attr); |
| hdd_debug("tx_nss %d rx_nss %d", tx_nss, rx_nss); |
| /* Only allow NSS for tx_rx_nss for 1x1, 1x2, 2x2 */ |
| if (!((tx_nss == 1 && rx_nss == 2) || (tx_nss == 1 && rx_nss == 1) || |
| (tx_nss == 2 && rx_nss == 2))) { |
| hdd_err("Setting tx_nss %d rx_nss %d not allowed", tx_nss, |
| rx_nss); |
| return 0; |
| } |
| status = hdd_update_nss(adapter, tx_nss, rx_nss); |
| if (status != QDF_STATUS_SUCCESS) |
| hdd_debug("Can't set tx_nss %d rx_nss %d", tx_nss, rx_nss); |
| |
| return 0; |
| } |
| |
| static int hdd_config_ani(struct hdd_adapter *adapter, |
| struct nlattr *tb[]) |
| { |
| int errno; |
| uint8_t ani_setting_type; |
| int32_t ani_level = 0, enable_ani; |
| struct nlattr *ani_setting_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_SETTING]; |
| struct nlattr *ani_level_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_LEVEL]; |
| |
| if (!ani_setting_attr) |
| return 0; |
| |
| ani_setting_type = nla_get_u8(ani_setting_attr); |
| if (ani_setting_type != QCA_WLAN_ANI_SETTING_AUTO && |
| ani_setting_type != QCA_WLAN_ANI_SETTING_FIXED) { |
| hdd_err("invalid ani_setting_type %d", ani_setting_type); |
| return -EINVAL; |
| } |
| |
| if (ani_setting_type == QCA_WLAN_ANI_SETTING_AUTO && |
| ani_level_attr) { |
| hdd_err("Not support to set ani level in QCA_WLAN_ANI_SETTING_AUTO"); |
| return -EINVAL; |
| } |
| |
| if (ani_setting_type == QCA_WLAN_ANI_SETTING_FIXED) { |
| if (!ani_level_attr) { |
| hdd_err("invalid ani_level_attr"); |
| return -EINVAL; |
| } |
| ani_level = nla_get_s32(ani_level_attr); |
| } |
| hdd_debug("ani_setting_type %u, ani_level %d", |
| ani_setting_type, ani_level); |
| |
| /* ANI (Adaptive noise immunity) */ |
| if (ani_setting_type == QCA_WLAN_ANI_SETTING_AUTO) |
| enable_ani = 1; |
| else |
| enable_ani = 0; |
| |
| errno = wma_cli_set_command(adapter->vdev_id, |
| WMI_PDEV_PARAM_ANI_ENABLE, |
| enable_ani, PDEV_CMD); |
| if (errno) { |
| hdd_err("Failed to set ani enable, errno %d", errno); |
| return errno; |
| } |
| |
| if (ani_setting_type == QCA_WLAN_ANI_SETTING_FIXED) { |
| errno = wma_cli_set_command(adapter->vdev_id, |
| WMI_PDEV_PARAM_ANI_OFDM_LEVEL, |
| ani_level, PDEV_CMD); |
| if (errno) { |
| hdd_err("Failed to set ani level, errno %d", errno); |
| return errno; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int hdd_config_ant_div_period(struct hdd_adapter *adapter, |
| struct nlattr *tb[]) |
| { |
| struct nlattr *probe_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD]; |
| struct nlattr *stay_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD]; |
| uint32_t probe_period, stay_period, ant_div_usrcfg; |
| int errno; |
| |
| /* nothing to do if neither attribute is present */ |
| if (!probe_attr && !stay_attr) |
| return 0; |
| |
| /* if one is present, both must be present */ |
| if (!probe_attr || !stay_attr) { |
| hdd_err("Missing attribute for %s", |
| probe_attr ? "STAY" : "PROBE"); |
| return -EINVAL; |
| } |
| |
| probe_period = nla_get_u32(probe_attr); |
| stay_period = nla_get_u32(stay_attr); |
| ant_div_usrcfg = ANT_DIV_SET_PERIOD(probe_period, stay_period); |
| hdd_debug("ant div set period: %x", ant_div_usrcfg); |
| errno = wma_cli_set_command(adapter->vdev_id, |
| WMI_PDEV_PARAM_ANT_DIV_USRCFG, |
| ant_div_usrcfg, PDEV_CMD); |
| if (errno) |
| hdd_err("Failed to set ant div period, %d", errno); |
| |
| return errno; |
| } |
| |
| static int hdd_config_ant_div_snr_weight(struct hdd_adapter *adapter, |
| struct nlattr *tb[]) |
| { |
| struct nlattr *mgmt_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT]; |
| struct nlattr *data_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT]; |
| struct nlattr *ack_attr = |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT]; |
| uint32_t mgmt_snr, data_snr, ack_snr, ant_div_usrcfg; |
| int errno; |
| |
| /* nothing to do if none of the attributes are present */ |
| if (!mgmt_attr && !data_attr && !ack_attr) |
| return 0; |
| |
| /* if one is present, all must be present */ |
| if (!mgmt_attr || !data_attr || !ack_attr) { |
| hdd_err("Missing attribute"); |
| return -EINVAL; |
| } |
| |
| mgmt_snr = nla_get_u32(mgmt_attr); |
| data_snr = nla_get_u32(data_attr); |
| ack_snr = nla_get_u32(ack_attr); |
| ant_div_usrcfg = ANT_DIV_SET_WEIGHT(mgmt_snr, data_snr, ack_snr); |
| hdd_debug("ant div set weight: %x", ant_div_usrcfg); |
| errno = wma_cli_set_command(adapter->vdev_id, |
| WMI_PDEV_PARAM_ANT_DIV_USRCFG, |
| ant_div_usrcfg, PDEV_CMD); |
| if (errno) |
| hdd_err("Failed to set ant div weight, %d", errno); |
| |
| return errno; |
| } |
| |
| static int hdd_config_fine_time_measurement(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint32_t user_capability; |
| uint32_t target_capability; |
| uint32_t final_capability; |
| QDF_STATUS status; |
| |
| user_capability = nla_get_u32(attr); |
| target_capability = hdd_ctx->fine_time_meas_cap_target; |
| final_capability = user_capability & target_capability; |
| |
| status = ucfg_mlme_set_fine_time_meas_cap(hdd_ctx->psoc, |
| final_capability); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("Unable to set value, status %d", status); |
| return -EINVAL; |
| } |
| |
| sme_update_fine_time_measurement_capab(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| final_capability); |
| ucfg_wifi_pos_set_ftm_cap(hdd_ctx->psoc, final_capability); |
| |
| hdd_debug("user: 0x%x, target: 0x%x, final: 0x%x", |
| user_capability, target_capability, final_capability); |
| |
| return 0; |
| } |
| |
| static int hdd_config_modulated_dtim(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct wlan_objmgr_vdev *vdev; |
| uint32_t modulated_dtim; |
| QDF_STATUS status; |
| |
| modulated_dtim = nla_get_u32(attr); |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_ID); |
| if (!vdev) |
| return -EINVAL; |
| |
| status = ucfg_pmo_config_modulated_dtim(vdev, modulated_dtim); |
| |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| static int hdd_config_listen_interval(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct wlan_objmgr_vdev *vdev; |
| uint32_t listen_interval; |
| QDF_STATUS status; |
| |
| listen_interval = nla_get_u32(attr); |
| if (listen_interval > cfg_max(CFG_PMO_ENABLE_DYNAMIC_DTIM)) { |
| hdd_err_rl("Invalid value for listen interval - %d", |
| listen_interval); |
| return -EINVAL; |
| } |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_PMO_ID); |
| if (!vdev) |
| return -EINVAL; |
| |
| status = ucfg_pmo_config_listen_interval(vdev, listen_interval); |
| |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_PMO_ID); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| static int hdd_config_lro(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint8_t enable_flag; |
| |
| enable_flag = nla_get_u8(attr); |
| |
| return hdd_lro_set_reset(hdd_ctx, adapter, enable_flag); |
| } |
| |
| static int hdd_config_scan_enable(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint8_t enable_flag; |
| |
| enable_flag = nla_get_u8(attr); |
| if (enable_flag) |
| ucfg_scan_psoc_set_enable(hdd_ctx->psoc, |
| REASON_USER_SPACE); |
| else |
| ucfg_scan_psoc_set_disable(hdd_ctx->psoc, |
| REASON_USER_SPACE); |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_config_udp_qos_upgrade_threshold() - NL attribute handler to parse |
| * priority upgrade threshold value. |
| * @adapter: adapter for which this configuration is to be applied |
| * @attr: NL attribute |
| * |
| * Returns: 0 on success, -EINVAL on failure |
| */ |
| static int hdd_config_udp_qos_upgrade_threshold(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t priority = nla_get_u8(attr); |
| |
| return hdd_set_udp_qos_upgrade_config(adapter, priority); |
| } |
| |
| static int hdd_config_power(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint8_t power; |
| |
| if (!ucfg_pmo_get_default_power_save_mode(hdd_ctx->psoc)) { |
| hdd_err_rl("OPM power save is disabled in ini"); |
| return -EINVAL; |
| } |
| |
| power = nla_get_u8(attr); |
| |
| return hdd_set_power_config(hdd_ctx, adapter, power); |
| } |
| |
| static int hdd_config_stats_avg_factor(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint16_t stats_avg_factor; |
| QDF_STATUS status; |
| |
| stats_avg_factor = nla_get_u16(attr); |
| status = sme_configure_stats_avg_factor(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| stats_avg_factor); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| static int hdd_config_non_agg_retry(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t retry; |
| |
| retry = nla_get_u8(attr); |
| |
| /* Value less than CFG_AGG_RETRY_MIN has side effect to t-put */ |
| retry = (retry > CFG_NON_AGG_RETRY_MAX) ? CFG_NON_AGG_RETRY_MAX : |
| ((retry < CFG_NON_AGG_RETRY_MIN) ? CFG_NON_AGG_RETRY_MIN : |
| retry); |
| hdd_debug("sending Non-Agg Retry Th: %d", retry); |
| |
| return sme_set_vdev_sw_retry(adapter->vdev_id, retry, |
| WMI_VDEV_CUSTOM_SW_RETRY_TYPE_NONAGGR); |
| } |
| |
| static int hdd_config_agg_retry(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t retry; |
| |
| retry = nla_get_u8(attr); |
| |
| /* Value less than CFG_AGG_RETRY_MIN has side effect to t-put */ |
| retry = (retry > CFG_AGG_RETRY_MAX) ? CFG_AGG_RETRY_MAX : |
| ((retry < CFG_AGG_RETRY_MIN) ? CFG_AGG_RETRY_MIN : |
| retry); |
| hdd_debug("sending Agg Retry Th: %d", retry); |
| |
| return sme_set_vdev_sw_retry(adapter->vdev_id, retry, |
| WMI_VDEV_CUSTOM_SW_RETRY_TYPE_AGGR); |
| } |
| |
| static int hdd_config_mgmt_retry(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t retry; |
| int param_id; |
| uint8_t max_mgmt_retry; |
| |
| retry = nla_get_u8(attr); |
| max_mgmt_retry = (cfg_max(CFG_MGMT_RETRY_MAX)); |
| retry = retry > max_mgmt_retry ? |
| max_mgmt_retry : retry; |
| param_id = WMI_PDEV_PARAM_MGMT_RETRY_LIMIT; |
| |
| return wma_cli_set_command(adapter->vdev_id, param_id, |
| retry, PDEV_CMD); |
| } |
| |
| static int hdd_config_ctrl_retry(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t retry; |
| int param_id; |
| |
| retry = nla_get_u8(attr); |
| retry = retry > CFG_CTRL_RETRY_MAX ? |
| CFG_CTRL_RETRY_MAX : retry; |
| param_id = WMI_PDEV_PARAM_CTRL_RETRY_LIMIT; |
| |
| return wma_cli_set_command(adapter->vdev_id, param_id, |
| retry, PDEV_CMD); |
| } |
| |
| static int hdd_config_propagation_delay(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t delay; |
| uint32_t abs_delay; |
| int param_id; |
| |
| delay = nla_get_u8(attr); |
| delay = delay > CFG_PROPAGATION_DELAY_MAX ? |
| CFG_PROPAGATION_DELAY_MAX : delay; |
| abs_delay = delay + CFG_PROPAGATION_DELAY_BASE; |
| param_id = WMI_PDEV_PARAM_PROPAGATION_DELAY; |
| |
| return wma_cli_set_command(adapter->vdev_id, param_id, |
| abs_delay, PDEV_CMD); |
| } |
| |
| static int hdd_config_propagation_abs_delay(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint32_t abs_delay; |
| int param_id; |
| |
| abs_delay = nla_get_u32(attr); |
| param_id = WMI_PDEV_PARAM_PROPAGATION_DELAY; |
| |
| return wma_cli_set_command(adapter->vdev_id, param_id, |
| abs_delay, PDEV_CMD); |
| } |
| |
| static int hdd_config_tx_fail_count(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint32_t tx_fail_count; |
| QDF_STATUS status; |
| |
| tx_fail_count = nla_get_u32(attr); |
| if (!tx_fail_count) |
| return 0; |
| |
| status = sme_update_tx_fail_cnt_threshold(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| tx_fail_count); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("sme_update_tx_fail_cnt_threshold (err=%d)", |
| status); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| static int hdd_config_channel_avoidance_ind(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint8_t set_value; |
| |
| set_value = nla_get_u8(attr); |
| hdd_debug("set_value: %d", set_value); |
| |
| return hdd_enable_disable_ca_event(hdd_ctx, set_value); |
| } |
| |
| static int hdd_config_guard_time(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint32_t guard_time; |
| QDF_STATUS status; |
| |
| guard_time = nla_get_u32(attr); |
| status = sme_configure_guard_time(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| guard_time); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| static int hdd_config_scan_default_ies(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint8_t *scan_ie; |
| uint16_t scan_ie_len; |
| QDF_STATUS status; |
| mac_handle_t mac_handle; |
| |
| scan_ie_len = nla_len(attr); |
| hdd_debug("IE len %d session %d device mode %d", |
| scan_ie_len, adapter->vdev_id, adapter->device_mode); |
| |
| if (!scan_ie_len) { |
| hdd_err("zero-length IE prohibited"); |
| return -EINVAL; |
| } |
| |
| if (scan_ie_len > MAX_DEFAULT_SCAN_IE_LEN) { |
| hdd_err("IE length %d exceeds max of %d", |
| scan_ie_len, MAX_DEFAULT_SCAN_IE_LEN); |
| return -EINVAL; |
| } |
| |
| scan_ie = nla_data(attr); |
| if (!wlan_is_ie_valid(scan_ie, scan_ie_len)) { |
| hdd_err("Invalid default scan IEs"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_save_default_scan_ies(hdd_ctx, adapter, |
| scan_ie, scan_ie_len)) |
| hdd_err("Failed to save default scan IEs"); |
| |
| if (adapter->device_mode == QDF_STA_MODE) { |
| mac_handle = hdd_ctx->mac_handle; |
| status = sme_set_default_scan_ie(mac_handle, |
| adapter->vdev_id, scan_ie, |
| scan_ie_len); |
| if (QDF_STATUS_SUCCESS != status) { |
| hdd_err("failed to set default scan IEs in sme: %d", |
| status); |
| return -EPERM; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int hdd_config_ant_div_ena(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint32_t antdiv_enable; |
| int errno; |
| |
| antdiv_enable = nla_get_u32(attr); |
| hdd_debug("antdiv_enable: %d", antdiv_enable); |
| errno = wma_cli_set_command(adapter->vdev_id, |
| WMI_PDEV_PARAM_ENA_ANT_DIV, |
| antdiv_enable, PDEV_CMD); |
| if (errno) |
| hdd_err("Failed to set antdiv_enable, %d", errno); |
| |
| return errno; |
| } |
| |
| static int hdd_config_ant_div_snr_diff(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint32_t ant_div_snr_diff; |
| uint32_t ant_div_usrcfg; |
| int errno; |
| |
| ant_div_snr_diff = nla_get_u32(attr); |
| hdd_debug("snr diff: %x", ant_div_snr_diff); |
| |
| ant_div_usrcfg = ANT_DIV_SET_SNR_DIFF(ant_div_snr_diff); |
| hdd_debug("usrcfg: %x", ant_div_usrcfg); |
| |
| errno = wma_cli_set_command(adapter->vdev_id, |
| WMI_PDEV_PARAM_ANT_DIV_USRCFG, |
| ant_div_usrcfg, PDEV_CMD); |
| if (errno) |
| hdd_err("Failed to set snr diff, %d", errno); |
| |
| return errno; |
| } |
| |
| static int hdd_config_ant_div_probe_dwell_time(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint32_t dwell_time; |
| uint32_t ant_div_usrcfg; |
| int errno; |
| |
| dwell_time = nla_get_u32(attr); |
| hdd_debug("dwell time: %x", dwell_time); |
| |
| ant_div_usrcfg = ANT_DIV_SET_PROBE_DWELL_TIME(dwell_time); |
| hdd_debug("usrcfg: %x", ant_div_usrcfg); |
| |
| errno = wma_cli_set_command(adapter->vdev_id, |
| WMI_PDEV_PARAM_ANT_DIV_USRCFG, |
| ant_div_usrcfg, PDEV_CMD); |
| if (errno) |
| hdd_err("Failed to set probe dwell time, %d", errno); |
| |
| return errno; |
| } |
| |
| static int hdd_config_ant_div_chain(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint32_t antdiv_chain; |
| int errno; |
| |
| antdiv_chain = nla_get_u32(attr); |
| hdd_debug("antdiv_chain: %d", antdiv_chain); |
| |
| errno = wma_cli_set_command(adapter->vdev_id, |
| WMI_PDEV_PARAM_FORCE_CHAIN_ANT, |
| antdiv_chain, PDEV_CMD); |
| if (errno) |
| hdd_err("Failed to set chain, %d", errno); |
| |
| return errno; |
| } |
| |
| static int hdd_config_ant_div_selftest(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint32_t antdiv_selftest; |
| int errno; |
| |
| antdiv_selftest = nla_get_u32(attr); |
| hdd_debug("antdiv_selftest: %d", antdiv_selftest); |
| |
| errno = wma_cli_set_command(adapter->vdev_id, |
| WMI_PDEV_PARAM_ANT_DIV_SELFTEST, |
| antdiv_selftest, PDEV_CMD); |
| if (errno) |
| hdd_err("Failed to set selftest, %d", errno); |
| |
| return errno; |
| } |
| |
| static int hdd_config_ant_div_selftest_intvl(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint32_t antdiv_selftest_intvl; |
| int errno; |
| |
| antdiv_selftest_intvl = nla_get_u32(attr); |
| hdd_debug("antdiv_selftest_intvl: %d", antdiv_selftest_intvl); |
| |
| errno = wma_cli_set_command(adapter->vdev_id, |
| WMI_PDEV_PARAM_ANT_DIV_SELFTEST_INTVL, |
| antdiv_selftest_intvl, PDEV_CMD); |
| if (errno) |
| hdd_err("Failed to set selftest interval, %d", errno); |
| |
| return errno; |
| } |
| |
| static int hdd_config_ignore_assoc_disallowed(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint8_t ignore_assoc_disallowed; |
| |
| ignore_assoc_disallowed = nla_get_u8(attr); |
| hdd_debug("%u", ignore_assoc_disallowed); |
| if ((ignore_assoc_disallowed < QCA_IGNORE_ASSOC_DISALLOWED_DISABLE) || |
| (ignore_assoc_disallowed > QCA_IGNORE_ASSOC_DISALLOWED_ENABLE)) |
| return -EINVAL; |
| |
| sme_set_check_assoc_disallowed(hdd_ctx->mac_handle, |
| !ignore_assoc_disallowed); |
| |
| return 0; |
| } |
| |
| static int hdd_config_restrict_offchannel(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t restrict_offchan; |
| |
| restrict_offchan = nla_get_u8(attr); |
| hdd_debug("%u", restrict_offchan); |
| |
| if (restrict_offchan > 1) { |
| hdd_err("Invalid value %u", restrict_offchan); |
| return -EINVAL; |
| } |
| |
| return wlan_hdd_handle_restrict_offchan_config(adapter, |
| restrict_offchan); |
| } |
| |
| static int hdd_config_total_beacon_miss_count(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint8_t first_miss_count; |
| uint8_t final_miss_count; |
| uint8_t total_miss_count; |
| QDF_STATUS status; |
| |
| if (adapter->device_mode != QDF_STA_MODE) { |
| hdd_err("Only supported in sta mode"); |
| return -EINVAL; |
| } |
| |
| total_miss_count = nla_get_u8(attr); |
| ucfg_mlme_get_roam_bmiss_first_bcnt(hdd_ctx->psoc, |
| &first_miss_count); |
| if (total_miss_count <= first_miss_count) { |
| hdd_err("Total %u needs to exceed first %u", |
| total_miss_count, first_miss_count); |
| return -EINVAL; |
| } |
| |
| final_miss_count = total_miss_count - first_miss_count; |
| |
| if (!ucfg_mlme_validate_roam_bmiss_final_bcnt(final_miss_count)) |
| return -EINVAL; |
| |
| hdd_debug("First count %u, final count %u", |
| first_miss_count, final_miss_count); |
| |
| status = sme_set_roam_bmiss_final_bcnt(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| final_miss_count); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("Failed to set final count, status %u", status); |
| return qdf_status_to_os_return(status); |
| } |
| |
| status = sme_set_bmiss_bcnt(adapter->vdev_id, |
| first_miss_count, |
| final_miss_count); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("Failed to set count, status %u", status); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| #ifdef WLAN_FEATURE_LL_MODE |
| static inline |
| void wlan_hdd_set_wlm_mode(struct hdd_context *hdd_ctx, uint16_t latency_level) |
| { |
| if (latency_level == |
| QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW) |
| wlan_hdd_set_pm_qos_request(hdd_ctx, true); |
| else |
| wlan_hdd_set_pm_qos_request(hdd_ctx, false); |
| } |
| #else |
| static inline |
| void wlan_hdd_set_wlm_mode(struct hdd_context *hdd_ctx, uint16_t latency_level) |
| { |
| } |
| #endif |
| |
| /** |
| * hdd_set_wlm_host_latency_level() - set latency flags based on latency flags |
| * @hdd_ctx: hdd context |
| * @adapter: adapter context |
| * @latency_host_flags: host latency flags |
| * |
| * Return: none |
| */ |
| static void hdd_set_wlm_host_latency_level(struct hdd_context *hdd_ctx, |
| struct hdd_adapter *adapter, |
| uint32_t latency_host_flags) |
| { |
| ol_txrx_soc_handle soc_hdl = cds_get_context(QDF_MODULE_ID_SOC); |
| |
| if (!soc_hdl) |
| return; |
| |
| if (latency_host_flags & WLM_HOST_PM_QOS_FLAG) |
| hdd_ctx->pm_qos_request_flags |= (1 << adapter->vdev_id); |
| else |
| hdd_ctx->pm_qos_request_flags &= ~(1 << adapter->vdev_id); |
| |
| if (hdd_ctx->pm_qos_request_flags) |
| wlan_hdd_set_pm_qos_request(hdd_ctx, true); |
| else |
| wlan_hdd_set_pm_qos_request(hdd_ctx, false); |
| |
| if (latency_host_flags & WLM_HOST_HBB_FLAG) |
| hdd_ctx->high_bus_bw_request |= (1 << adapter->vdev_id); |
| else |
| hdd_ctx->high_bus_bw_request &= ~(1 << adapter->vdev_id); |
| |
| if (latency_host_flags & WLM_HOST_RX_THREAD_FLAG) |
| adapter->runtime_disable_rx_thread = true; |
| else |
| adapter->runtime_disable_rx_thread = false; |
| } |
| |
| #ifdef MULTI_CLIENT_LL_SUPPORT |
| void |
| hdd_latency_level_event_handler_cb(const struct latency_level_data *event_data, |
| uint8_t vdev_id) |
| { |
| struct osif_request *request; |
| struct latency_level_data *data; |
| struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); |
| struct hdd_adapter *hdd_adapter; |
| uint32_t latency_host_flags = 0; |
| QDF_STATUS status; |
| |
| hdd_enter(); |
| |
| hdd_adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); |
| if (!hdd_adapter) { |
| hdd_err("adapter is NULL vdev_id = %d", vdev_id); |
| return; |
| } |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| |
| if (!event_data) { |
| hdd_err("Invalid latency level event data"); |
| return; |
| } |
| |
| if (hdd_adapter->multi_ll_resp_expected) { |
| request = |
| osif_request_get(hdd_adapter->multi_ll_response_cookie); |
| if (!request) { |
| hdd_err("Invalid request"); |
| return; |
| } |
| data = osif_request_priv(request); |
| data->latency_level = event_data->latency_level; |
| data->vdev_id = event_data->vdev_id; |
| osif_request_complete(request); |
| osif_request_put(request); |
| } else { |
| hdd_adapter->latency_level = event_data->latency_level; |
| wlan_hdd_set_wlm_mode(hdd_ctx, hdd_adapter->latency_level); |
| hdd_debug("adapter->latency_level:%d", |
| hdd_adapter->latency_level); |
| status = ucfg_mlme_get_latency_host_flags(hdd_ctx->psoc, |
| hdd_adapter->latency_level, |
| &latency_host_flags); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("failed to get latency host flags"); |
| else |
| hdd_set_wlm_host_latency_level(hdd_ctx, hdd_adapter, |
| latency_host_flags); |
| } |
| |
| hdd_exit(); |
| } |
| |
| uint8_t wlan_hdd_get_client_id_bitmap(struct hdd_adapter *adapter) |
| { |
| uint8_t i, client_id_bitmap = 0; |
| |
| for (i = 0; i < WLM_MAX_HOST_CLIENT; i++) { |
| if (!adapter->client_info[i].in_use) |
| continue; |
| client_id_bitmap |= |
| BIT(adapter->client_info[i].client_id); |
| } |
| |
| return client_id_bitmap; |
| } |
| |
| QDF_STATUS wlan_hdd_get_set_client_info_id(struct hdd_adapter *adapter, |
| uint32_t port_id, |
| uint32_t *client_id) |
| { |
| uint8_t i; |
| QDF_STATUS status = QDF_STATUS_E_INVAL; |
| |
| for (i = 0; i < WLM_MAX_HOST_CLIENT; i++) { |
| if (adapter->client_info[i].in_use) { |
| /* Receives set latency cmd for an existing port id */ |
| if (port_id == adapter->client_info[i].port_id) { |
| *client_id = adapter->client_info[i].client_id; |
| status = QDF_STATUS_SUCCESS; |
| break; |
| } |
| continue; |
| } else { |
| /* Process set latency level from a new client */ |
| adapter->client_info[i].in_use = true; |
| adapter->client_info[i].port_id = port_id; |
| *client_id = adapter->client_info[i].client_id; |
| status = QDF_STATUS_SUCCESS; |
| break; |
| } |
| } |
| |
| if (i == WLM_MAX_HOST_CLIENT) |
| hdd_debug("Max client ID reached"); |
| |
| return status; |
| } |
| |
| QDF_STATUS wlan_hdd_set_wlm_latency_level(struct hdd_adapter *adapter, |
| uint16_t latency_level, |
| uint32_t client_id_bitmap, |
| bool force_reset) |
| { |
| QDF_STATUS status; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| int ret; |
| struct osif_request *request = NULL; |
| struct latency_level_data *priv; |
| static const struct osif_request_params params = { |
| .priv_size = sizeof(*priv), |
| .timeout_ms = WLAN_WAIT_WLM_LATENCY_LEVEL, |
| .dealloc = NULL, |
| }; |
| |
| /* ignore unless in STA mode */ |
| if (adapter->device_mode != QDF_STA_MODE) { |
| hdd_debug_rl("WLM offload is supported in STA mode only"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| adapter->multi_ll_resp_expected = true; |
| |
| request = osif_request_alloc(¶ms); |
| if (!request) { |
| hdd_err("Request allocation failure"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| adapter->multi_ll_response_cookie = osif_request_cookie(request); |
| adapter->multi_ll_req_in_progress = true; |
| |
| status = sme_set_wlm_latency_level(hdd_ctx->mac_handle, |
| adapter->vdev_id, latency_level, |
| client_id_bitmap, force_reset); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("Failure while sending command to fw"); |
| goto err; |
| } |
| |
| ret = osif_request_wait_for_response(request); |
| if (ret) { |
| hdd_err("SME timed out while retrieving latency level"); |
| status = qdf_status_from_os_return(ret); |
| goto err; |
| } |
| priv = osif_request_priv(request); |
| if (!priv) { |
| hdd_err("invalid get latency level"); |
| status = QDF_STATUS_E_FAILURE; |
| goto err; |
| } |
| |
| hdd_debug("latency level received from FW:%d", priv->latency_level); |
| adapter->latency_level = priv->latency_level; |
| err: |
| if (request) |
| osif_request_put(request); |
| adapter->multi_ll_req_in_progress = false; |
| adapter->multi_ll_resp_expected = false; |
| adapter->multi_ll_response_cookie = NULL; |
| |
| return status; |
| } |
| |
| bool hdd_get_multi_client_ll_support(struct hdd_adapter *adapter) |
| { |
| return adapter->multi_client_ll_support; |
| } |
| |
| /** |
| * wlan_hdd_reset_client_info() - reset multi client info table |
| * @adapter: adapter context |
| * @client_id: client id |
| * |
| * Return: none |
| */ |
| static void wlan_hdd_reset_client_info(struct hdd_adapter *adapter, |
| uint32_t client_id) |
| { |
| adapter->client_info[client_id].in_use = false; |
| adapter->client_info[client_id].port_id = 0; |
| adapter->client_info[client_id].client_id = client_id; |
| } |
| |
| QDF_STATUS wlan_hdd_set_wlm_client_latency_level(struct hdd_adapter *adapter, |
| uint32_t port_id, |
| uint16_t latency_level) |
| { |
| uint32_t client_id, client_id_bitmap; |
| QDF_STATUS status; |
| |
| status = wlan_hdd_get_set_client_info_id(adapter, port_id, |
| &client_id); |
| if (QDF_IS_STATUS_ERROR(status)) |
| return status; |
| |
| client_id_bitmap = BIT(client_id); |
| status = wlan_hdd_set_wlm_latency_level(adapter, |
| latency_level, |
| client_id_bitmap, |
| false); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_debug("Fail to set latency level for client_id:%d", |
| client_id); |
| wlan_hdd_reset_client_info(adapter, client_id); |
| return status; |
| } |
| return status; |
| } |
| |
| /** |
| * wlan_hdd_get_multi_ll_req_in_progress() - get multi_ll_req_in_progress flag |
| * @adapter: adapter context |
| * |
| * Return: true if multi ll req in progress |
| */ |
| static bool wlan_hdd_get_multi_ll_req_in_progress(struct hdd_adapter *adapter) |
| { |
| return adapter->multi_ll_req_in_progress; |
| } |
| #else |
| static inline bool |
| wlan_hdd_get_multi_ll_req_in_progress(struct hdd_adapter *adapter) |
| { |
| return false; |
| } |
| #endif |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) |
| static QDF_STATUS hdd_get_netlink_sender_portid(struct hdd_context *hdd_ctx, |
| uint32_t *port_id) |
| { |
| struct wiphy *wiphy = hdd_ctx->wiphy; |
| |
| /* get netlink portid of sender */ |
| *port_id = cfg80211_vendor_cmd_get_sender(wiphy); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| #else |
| static inline QDF_STATUS |
| hdd_get_netlink_sender_portid(struct hdd_context *hdd_ctx, uint32_t *port_id) |
| { |
| return QDF_STATUS_E_NOSUPPORT; |
| } |
| #endif |
| |
| static int hdd_config_latency_level(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint32_t port_id; |
| uint16_t latency_level, host_latency_level; |
| QDF_STATUS status; |
| uint32_t latency_host_flags = 0; |
| int ret; |
| |
| if (hdd_validate_adapter(adapter)) |
| return -EINVAL; |
| |
| if (!hdd_is_wlm_latency_manager_supported(hdd_ctx)) |
| return -ENOTSUPP; |
| |
| latency_level = nla_get_u16(attr); |
| switch (latency_level) { |
| case QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL: |
| case QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_XR: |
| case QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW: |
| case QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW: |
| /* valid values */ |
| break; |
| default: |
| hdd_err("Invalid value %u", latency_level); |
| return -EINVAL; |
| } |
| |
| host_latency_level = latency_level - 1; |
| |
| if (hdd_get_multi_client_ll_support(adapter)) { |
| if (wlan_hdd_get_multi_ll_req_in_progress(adapter)) { |
| hdd_err_rl("multi ll request already in progress"); |
| return -EBUSY; |
| } |
| /* get netlink portid of sender */ |
| status = hdd_get_netlink_sender_portid(hdd_ctx, &port_id); |
| if (QDF_IS_STATUS_ERROR(status)) |
| goto error; |
| status = wlan_hdd_set_wlm_client_latency_level(adapter, port_id, |
| host_latency_level); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_debug("Fail to set latency level"); |
| goto error; |
| } |
| } else { |
| status = sme_set_wlm_latency_level(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| host_latency_level, 0, |
| false); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("set latency level failed, %u", status); |
| goto error; |
| } |
| adapter->latency_level = host_latency_level; |
| } |
| |
| wlan_hdd_set_wlm_mode(hdd_ctx, adapter->latency_level); |
| hdd_debug("adapter->latency_level:%d", adapter->latency_level); |
| |
| status = ucfg_mlme_get_latency_host_flags(hdd_ctx->psoc, |
| adapter->latency_level, |
| &latency_host_flags); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("failed to get latency host flags"); |
| else |
| hdd_set_wlm_host_latency_level(hdd_ctx, adapter, |
| latency_host_flags); |
| error: |
| ret = qdf_status_to_os_return(status); |
| |
| return ret; |
| } |
| |
| static int hdd_config_disable_fils(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint8_t disable_fils; |
| bool enabled; |
| QDF_STATUS status; |
| |
| /* ignore unless in STA mode */ |
| if (adapter->device_mode != QDF_STA_MODE) |
| return 0; |
| |
| disable_fils = nla_get_u8(attr); |
| hdd_debug("%u", disable_fils); |
| |
| enabled = !disable_fils; |
| status = ucfg_mlme_set_fils_enabled_info(hdd_ctx->psoc, enabled); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("could not set fils enabled info, %d", status); |
| |
| status = ucfg_mlme_set_enable_bcast_probe_rsp(hdd_ctx->psoc, enabled); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("could not set enable bcast probe resp info, %d", |
| status); |
| |
| status = wma_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_ENABLE_BCAST_PROBE_RESPONSE, |
| !disable_fils, VDEV_CMD); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("failed to set enable bcast probe resp, %d", |
| status); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| static int hdd_set_primary_interface(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| bool is_set_primary_iface; |
| QDF_STATUS status; |
| uint8_t primary_vdev_id, dual_sta_policy; |
| int set_value; |
| uint32_t count; |
| bool enable_mcc_adaptive_sch = false; |
| |
| /* ignore unless in STA mode */ |
| if (adapter->device_mode != QDF_STA_MODE) |
| return 0; |
| |
| is_set_primary_iface = nla_get_u8(attr); |
| |
| primary_vdev_id = |
| is_set_primary_iface ? adapter->vdev_id : WLAN_UMAC_VDEV_ID_MAX; |
| |
| status = ucfg_mlme_set_primary_interface(hdd_ctx->psoc, |
| primary_vdev_id); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("could not set primary interface, %d", status); |
| return -EINVAL; |
| } |
| |
| /* After SSR, the dual sta configuration is lost. As SSR is hidden from |
| * userland, this command will not come from userspace after a SSR. To |
| * restore this configuration, save this in hdd context and restore |
| * after re-init. |
| */ |
| hdd_ctx->dual_sta_policy.primary_vdev_id = primary_vdev_id; |
| |
| count = policy_mgr_mode_specific_connection_count(hdd_ctx->psoc, |
| PM_STA_MODE, NULL); |
| |
| if (count < 2) { |
| hdd_debug("STA + STA concurrency not present, count:%d", count); |
| return 0; |
| } |
| |
| /* if dual sta roaming enabled and both sta in DBS then no need |
| * to enable roaming on primary as both STA's have roaming enabled. |
| * if dual sta roaming enabled and both sta in MCC or SCC then need |
| * to enable roaming on primary vdev. |
| * if dual sta roaming NOT enabled then need to enable roaming on |
| * primary vdev for dual STA concurrency in MCC or DBS. |
| */ |
| if (primary_vdev_id != WLAN_UMAC_VDEV_ID_MAX) |
| if ((ucfg_mlme_get_dual_sta_roaming_enabled(hdd_ctx->psoc) && |
| !policy_mgr_concurrent_sta_doing_dbs(hdd_ctx->psoc)) || |
| !ucfg_mlme_get_dual_sta_roaming_enabled(hdd_ctx->psoc)) { |
| hdd_err("Enable roaming on requested interface: %d", |
| adapter->vdev_id); |
| hdd_debug("Enable roaming on requested interface: %d", |
| adapter->vdev_id); |
| wlan_cm_roam_state_change(hdd_ctx->pdev, |
| adapter->vdev_id, |
| WLAN_ROAM_RSO_ENABLED, |
| REASON_ROAM_SET_PRIMARY); |
| } |
| |
| /* |
| * send duty cycle percentage to FW only if STA + STA |
| * concurrency is in MCC. |
| */ |
| if (!policy_mgr_current_concurrency_is_mcc(hdd_ctx->psoc)) { |
| hdd_debug("STA + STA concurrency not in MCC"); |
| return 0; |
| } |
| |
| status = ucfg_mlme_get_dual_sta_policy(hdd_ctx->psoc, &dual_sta_policy); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("could not get dual sta policy, %d", status); |
| return -EINVAL; |
| } |
| |
| hdd_debug("is_set_primary_iface: %d, primary vdev id: %d, dual_sta_policy:%d", |
| is_set_primary_iface, primary_vdev_id, dual_sta_policy); |
| |
| if (is_set_primary_iface && dual_sta_policy == |
| QCA_WLAN_CONCURRENT_STA_POLICY_PREFER_PRIMARY) { |
| hdd_debug("Disable mcc_adaptive_scheduler"); |
| ucfg_policy_mgr_get_mcc_adaptive_sch(hdd_ctx->psoc, |
| &enable_mcc_adaptive_sch); |
| if (enable_mcc_adaptive_sch) { |
| ucfg_policy_mgr_set_dynamic_mcc_adaptive_sch( |
| hdd_ctx->psoc, false); |
| if (QDF_IS_STATUS_ERROR(sme_set_mas(false))) { |
| hdd_err("Fail to disable mcc adaptive sched."); |
| return -EINVAL; |
| } |
| } |
| /* Configure mcc duty cycle percentage */ |
| set_value = |
| ucfg_mlme_get_mcc_duty_cycle_percentage(hdd_ctx->pdev); |
| if (set_value < 0) { |
| hdd_err("Invalid mcc duty cycle"); |
| return -EINVAL; |
| } |
| wlan_hdd_send_mcc_vdev_quota(adapter, set_value); |
| } else { |
| hdd_debug("Enable mcc_adaptive_scheduler"); |
| ucfg_policy_mgr_get_mcc_adaptive_sch(hdd_ctx->psoc, |
| &enable_mcc_adaptive_sch); |
| if (enable_mcc_adaptive_sch) { |
| ucfg_policy_mgr_set_dynamic_mcc_adaptive_sch( |
| hdd_ctx->psoc, true); |
| if (QDF_STATUS_SUCCESS != sme_set_mas(true)) { |
| hdd_err("Fail to enable mcc_adaptive_sched."); |
| return -EAGAIN; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int hdd_config_rsn_ie(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint8_t force_rsne_override; |
| |
| force_rsne_override = nla_get_u8(attr); |
| if (force_rsne_override > 1) { |
| hdd_err("Invalid value %d", force_rsne_override); |
| return -EINVAL; |
| } |
| |
| hdd_ctx->force_rsne_override = force_rsne_override; |
| hdd_debug("force_rsne_override - %d", force_rsne_override); |
| |
| return 0; |
| } |
| |
| static int hdd_config_gtx(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t config_gtx; |
| int errno; |
| |
| config_gtx = nla_get_u8(attr); |
| if (config_gtx > 1) { |
| hdd_err_rl("Invalid config_gtx value %d", config_gtx); |
| return -EINVAL; |
| } |
| |
| errno = sme_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_GTX_ENABLE, |
| config_gtx, VDEV_CMD); |
| if (errno) |
| hdd_err("Failed to set GTX, %d", errno); |
| |
| return errno; |
| } |
| |
| /** |
| * hdd_config_disconnect_ies() - Configure disconnect IEs |
| * @adapter: Pointer to HDD adapter |
| * @attr: array of pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_config_disconnect_ies(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| QDF_STATUS status; |
| |
| hdd_debug("IE len %u session %u device mode %u", |
| nla_len(attr), adapter->vdev_id, adapter->device_mode); |
| if (!nla_len(attr) || |
| nla_len(attr) > SIR_MAC_MAX_ADD_IE_LENGTH + 2 || |
| !wlan_is_ie_valid(nla_data(attr), nla_len(attr))) { |
| hdd_err("Invalid disconnect IEs"); |
| return -EINVAL; |
| } |
| |
| status = sme_set_disconnect_ies(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| nla_data(attr), |
| nla_len(attr)); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("Failed to set disconnect_ies"); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| #ifdef WLAN_FEATURE_ELNA |
| /** |
| * hdd_set_elna_bypass() - Set eLNA bypass |
| * @adapter: Pointer to HDD adapter |
| * @attr: Pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_set_elna_bypass(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| int ret; |
| struct wlan_objmgr_vdev *vdev; |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_FWOL_NB_ID); |
| if (!vdev) |
| return -EINVAL; |
| |
| ret = os_if_fwol_set_elna_bypass(vdev, attr); |
| |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_FWOL_NB_ID); |
| |
| return ret; |
| } |
| #endif |
| |
| /** |
| * hdd_mac_chwidth_to_bonding_mode() - get bonding_mode from chan width |
| * @chwidth: chan width |
| * |
| * Return: bonding mode |
| */ |
| static uint32_t hdd_mac_chwidth_to_bonding_mode( |
| enum eSirMacHTChannelWidth chwidth) |
| { |
| uint32_t bonding_mode; |
| |
| switch (chwidth) { |
| case eHT_CHANNEL_WIDTH_20MHZ: |
| bonding_mode = WNI_CFG_CHANNEL_BONDING_MODE_DISABLE; |
| break; |
| default: |
| bonding_mode = WNI_CFG_CHANNEL_BONDING_MODE_ENABLE; |
| } |
| |
| return bonding_mode; |
| } |
| |
| int hdd_set_mac_chan_width(struct hdd_adapter *adapter, |
| enum eSirMacHTChannelWidth chwidth) |
| { |
| uint32_t bonding_mode; |
| |
| bonding_mode = hdd_mac_chwidth_to_bonding_mode(chwidth); |
| |
| return hdd_update_channel_width(adapter, chwidth, bonding_mode); |
| } |
| |
| /** |
| * hdd_set_channel_width() - set channel width |
| * |
| * @adapter: hdd adapter |
| * @attr: nla attr sent by supplicant |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int hdd_set_channel_width(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t nl80211_chwidth; |
| enum eSirMacHTChannelWidth chwidth; |
| |
| nl80211_chwidth = nla_get_u8(attr); |
| chwidth = hdd_nl80211_chwidth_to_chwidth(nl80211_chwidth); |
| if (chwidth < 0) { |
| hdd_err("Invalid channel width"); |
| return -EINVAL; |
| } |
| |
| return hdd_set_mac_chan_width(adapter, chwidth); |
| } |
| |
| /** |
| * hdd_set_dynamic_bw() - enable / disable dynamic bandwidth |
| * |
| * @adapter: hdd adapter |
| * @attr: nla attr sent by supplicant |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int hdd_set_dynamic_bw(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t enable; |
| |
| enable = nla_get_u8(attr); |
| |
| return wma_cli_set_command(adapter->vdev_id, WMI_PDEV_PARAM_DYNAMIC_BW, |
| enable, PDEV_CMD); |
| } |
| |
| /** |
| * hdd_set_nss() - set the number of spatial streams supported by the adapter |
| * |
| * @adapter: hdd adapter |
| * @attr: pointer to nla attr |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int hdd_set_nss(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t nss; |
| int ret; |
| QDF_STATUS status; |
| |
| nss = nla_get_u8(attr); |
| |
| status = hdd_update_nss(adapter, nss, nss); |
| ret = qdf_status_to_os_return(status); |
| |
| if (ret == 0 && adapter->device_mode == QDF_SAP_MODE) |
| ret = wma_cli_set_command(adapter->vdev_id, WMI_VDEV_PARAM_NSS, |
| nss, VDEV_CMD); |
| |
| return ret; |
| } |
| |
| #ifdef FEATURE_WLAN_DYNAMIC_ARP_NS_OFFLOAD |
| #define DYNAMIC_ARP_NS_ENABLE 1 |
| #define DYNAMIC_ARP_NS_DISABLE 0 |
| |
| /** |
| * hdd_set_arp_ns_offload() - enable/disable arp/ns offload feature |
| * @adapter: hdd adapter |
| * @attr: pointer to nla attr |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int hdd_set_arp_ns_offload(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t offload_state; |
| int errno; |
| QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| struct wlan_objmgr_vdev *vdev; |
| |
| errno = wlan_hdd_validate_context(hdd_ctx); |
| if (errno) |
| return errno; |
| |
| if (!ucfg_pmo_is_arp_offload_enabled(hdd_ctx->psoc) || |
| !ucfg_pmo_is_ns_offloaded(hdd_ctx->psoc)) { |
| hdd_err_rl("ARP/NS Offload is disabled by ini"); |
| return -EINVAL; |
| } |
| |
| if (!ucfg_pmo_is_active_mode_offloaded(hdd_ctx->psoc)) { |
| hdd_err_rl("active mode offload is disabled by ini"); |
| return -EINVAL; |
| } |
| |
| if (adapter->device_mode != QDF_STA_MODE && |
| adapter->device_mode != QDF_P2P_CLIENT_MODE) { |
| hdd_err_rl("only support on sta/p2p-cli mode"); |
| return -EINVAL; |
| } |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_ID); |
| if (!vdev) { |
| hdd_err("vdev is NULL"); |
| return -EINVAL; |
| } |
| |
| offload_state = nla_get_u8(attr); |
| |
| if (offload_state == DYNAMIC_ARP_NS_ENABLE) |
| qdf_status = ucfg_pmo_dynamic_arp_ns_offload_enable(vdev); |
| else if (offload_state == DYNAMIC_ARP_NS_DISABLE) |
| qdf_status = ucfg_pmo_dynamic_arp_ns_offload_disable(vdev); |
| |
| if (QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| if (offload_state == DYNAMIC_ARP_NS_ENABLE) |
| ucfg_pmo_dynamic_arp_ns_offload_runtime_allow(vdev); |
| else |
| ucfg_pmo_dynamic_arp_ns_offload_runtime_prevent(vdev); |
| } |
| |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| |
| if (QDF_IS_STATUS_ERROR(qdf_status)) { |
| if (qdf_status == QDF_STATUS_E_ALREADY) { |
| hdd_info_rl("already set arp/ns offload %d", |
| offload_state); |
| return 0; |
| } |
| return qdf_status_to_os_return(qdf_status); |
| } |
| |
| if (!hdd_is_vdev_in_conn_state(adapter)) { |
| hdd_info("set not in connect state, updated state %d", |
| offload_state); |
| return 0; |
| } |
| |
| if (offload_state == DYNAMIC_ARP_NS_ENABLE) { |
| hdd_enable_arp_offload(adapter, |
| pmo_arp_ns_offload_dynamic_update); |
| hdd_enable_ns_offload(adapter, |
| pmo_arp_ns_offload_dynamic_update); |
| } else if (offload_state == DYNAMIC_ARP_NS_DISABLE) { |
| hdd_disable_arp_offload(adapter, |
| pmo_arp_ns_offload_dynamic_update); |
| hdd_disable_ns_offload(adapter, |
| pmo_arp_ns_offload_dynamic_update); |
| } |
| |
| return 0; |
| } |
| |
| #undef DYNAMIC_ARP_NS_ENABLE |
| #undef DYNAMIC_ARP_NS_DISABLE |
| #endif |
| |
| /** |
| * hdd_set_wfc_state() - Set wfc state |
| * @adapter: hdd adapter |
| * @attr: pointer to nla attr |
| * |
| * Return: 0 on success, negative on failure |
| */ |
| static int hdd_set_wfc_state(struct hdd_adapter *adapter, |
| const struct nlattr *attr) |
| { |
| uint8_t cfg_val; |
| enum pld_wfc_mode set_val; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| int errno; |
| |
| errno = wlan_hdd_validate_context(hdd_ctx); |
| if (errno) |
| return errno; |
| |
| cfg_val = nla_get_u8(attr); |
| |
| hdd_debug_rl("set wfc state %d", cfg_val); |
| if (cfg_val == 0) |
| set_val = PLD_WFC_MODE_OFF; |
| else if (cfg_val == 1) |
| set_val = PLD_WFC_MODE_ON; |
| else |
| return -EINVAL; |
| |
| return pld_set_wfc_mode(hdd_ctx->parent_dev, set_val); |
| |
| } |
| |
| /** |
| * typedef independent_setter_fn - independent attribute handler |
| * @adapter: The adapter being configured |
| * @attr: The nl80211 attribute being applied |
| * |
| * Defines the signature of functions in the independent attribute vtable |
| * |
| * Return: 0 if the attribute was handled successfully, otherwise an errno |
| */ |
| typedef int (*independent_setter_fn)(struct hdd_adapter *adapter, |
| const struct nlattr *attr); |
| |
| /** |
| * struct independent_setters |
| * @id: vendor attribute which this entry handles |
| * @cb: callback function to invoke to process the attribute when present |
| */ |
| struct independent_setters { |
| uint32_t id; |
| independent_setter_fn cb; |
| }; |
| |
| /* vtable for independent setters */ |
| static const struct independent_setters independent_setters[] = { |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES, |
| hdd_config_scan_default_ies}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT, |
| hdd_config_fine_time_measurement}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_MODULATED_DTIM, |
| hdd_config_modulated_dtim}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL, |
| hdd_config_listen_interval}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_LRO, |
| hdd_config_lro}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE, |
| hdd_config_scan_enable}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER, |
| hdd_config_power}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR, |
| hdd_config_stats_avg_factor}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME, |
| hdd_config_guard_time}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY, |
| hdd_config_non_agg_retry}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY, |
| hdd_config_agg_retry}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY, |
| hdd_config_mgmt_retry}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY, |
| hdd_config_ctrl_retry}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY, |
| hdd_config_propagation_delay}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY, |
| hdd_config_propagation_abs_delay}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT, |
| hdd_config_tx_fail_count}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND, |
| hdd_config_channel_avoidance_ind}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA, |
| hdd_config_ant_div_ena}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF, |
| hdd_config_ant_div_snr_diff}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME, |
| hdd_config_ant_div_probe_dwell_time}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN, |
| hdd_config_ant_div_chain}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST, |
| hdd_config_ant_div_selftest}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL, |
| hdd_config_ant_div_selftest_intvl}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED, |
| hdd_config_ignore_assoc_disallowed}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL, |
| hdd_config_restrict_offchannel}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT, |
| hdd_config_total_beacon_miss_count}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL, |
| hdd_config_latency_level}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS, |
| hdd_config_disable_fils}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE, |
| hdd_config_rsn_ie}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_GTX, |
| hdd_config_gtx}, |
| {QCA_WLAN_VENDOR_ATTR_DISCONNECT_IES, |
| hdd_config_disconnect_ies}, |
| #ifdef WLAN_FEATURE_ELNA |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_ELNA_BYPASS, |
| hdd_set_elna_bypass}, |
| #endif |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_ROAM_REASON, |
| hdd_set_roam_reason_vsie_status}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_LDPC, |
| hdd_config_ldpc}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_TX_STBC, |
| hdd_config_tx_stbc}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_RX_STBC, |
| hdd_config_rx_stbc}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_PHY_MODE, |
| hdd_config_phy_mode}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_WIDTH, |
| hdd_set_channel_width}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_BW, |
| hdd_set_dynamic_bw}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_NSS, |
| hdd_set_nss}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT, |
| hdd_config_power}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE, |
| hdd_config_udp_qos_upgrade_threshold}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_CONCURRENT_STA_PRIMARY, |
| hdd_set_primary_interface}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_FT_OVER_DS, |
| hdd_set_ft_over_ds}, |
| #ifdef FEATURE_WLAN_DYNAMIC_ARP_NS_OFFLOAD |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_ARP_NS_OFFLOAD, |
| hdd_set_arp_ns_offload}, |
| #endif |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_WFC_STATE, |
| hdd_set_wfc_state}, |
| }; |
| |
| #ifdef WLAN_FEATURE_ELNA |
| /** |
| * hdd_get_elna_bypass() - Get eLNA bypass |
| * @adapter: Pointer to HDD adapter |
| * @skb: sk buffer to hold nl80211 attributes |
| * @attr: Pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_get_elna_bypass(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| int ret; |
| struct wlan_objmgr_vdev *vdev; |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_FWOL_NB_ID); |
| if (!vdev) |
| return -EINVAL; |
| |
| ret = os_if_fwol_get_elna_bypass(vdev, skb, attr); |
| |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_FWOL_NB_ID); |
| |
| return ret; |
| } |
| #endif |
| |
| /** |
| * hdd_get_roam_reason_vsie_status() - Get roam_reason_vsie |
| * @adapter: Pointer to HDD adapter |
| * @skb: sk buffer to hold nl80211 attributes |
| * @attr: Pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| static int hdd_get_roam_reason_vsie_status(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| uint8_t roam_reason_vsie_enabled; |
| struct hdd_context *hdd_ctx = NULL; |
| QDF_STATUS status; |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| |
| status = ucfg_mlme_get_roam_reason_vsie_status |
| (hdd_ctx->psoc, |
| &roam_reason_vsie_enabled); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("get roam reason vsie failed"); |
| return -EINVAL; |
| } |
| hdd_debug("is roam_reason_vsie_enabled %d", roam_reason_vsie_enabled); |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_ROAM_REASON, |
| roam_reason_vsie_enabled)) { |
| hdd_err("nla_put failure"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| #else |
| static int hdd_get_roam_reason_vsie_status(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| return -EINVAL; |
| } |
| #endif |
| |
| static int hdd_vendor_attr_ldpc_get(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| int ldpc; |
| int ret; |
| |
| ret = hdd_get_ldpc(adapter, &ldpc); |
| if (ret) { |
| hdd_err("get ldpc failed"); |
| return -EINVAL; |
| } |
| |
| hdd_debug("ldpc %u", ldpc); |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_LDPC, |
| (uint8_t)ldpc)) { |
| hdd_err("nla_put failure"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int hdd_vendor_attr_tx_stbc_get(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| int tx_stbc; |
| int ret; |
| |
| ret = hdd_get_tx_stbc(adapter, &tx_stbc); |
| if (ret) { |
| hdd_err("get tx_stbc failed"); |
| return -EINVAL; |
| } |
| |
| hdd_debug("tx_stbc %u", tx_stbc); |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_TX_STBC, |
| (uint8_t)tx_stbc)) { |
| hdd_err("nla_put failure"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int hdd_vendor_attr_rx_stbc_get(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| int rx_stbc; |
| int ret; |
| |
| ret = hdd_get_rx_stbc(adapter, &rx_stbc); |
| if (ret) { |
| hdd_err("get rx_stbc failed"); |
| return -EINVAL; |
| } |
| |
| hdd_debug("rx_stbc %u", rx_stbc); |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_RX_STBC, |
| (uint8_t)rx_stbc)) { |
| hdd_err("nla_put failure"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_get_tx_ampdu() - Get TX AMPDU |
| * @adapter: Pointer to HDD adapter |
| * @skb: sk buffer to hold nl80211 attributes |
| * @attr: Pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_get_tx_ampdu(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| int value; |
| |
| value = wma_cli_get_command(adapter->vdev_id, GEN_VDEV_PARAM_TX_AMPDU, |
| GEN_CMD); |
| if (value < 0) { |
| hdd_err("Failed to get tx_ampdu"); |
| return -EINVAL; |
| } |
| |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION, |
| (uint8_t)value)) { |
| hdd_err("nla_put failure"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_get_rx_ampdu() - Get RX AMPDU |
| * @adapter: Pointer to HDD adapter |
| * @skb: sk buffer to hold nl80211 attributes |
| * @attr: Pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_get_rx_ampdu(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| int value; |
| |
| value = wma_cli_get_command(adapter->vdev_id, GEN_VDEV_PARAM_RX_AMPDU, |
| GEN_CMD); |
| if (value < 0) { |
| hdd_err("Failed to get rx_ampdu"); |
| return -EINVAL; |
| } |
| |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION, |
| (uint8_t)value)) { |
| hdd_err("nla_put failure"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_get_tx_amsdu() - Get TX AMSDU |
| * @adapter: Pointer to HDD adapter |
| * @skb: sk buffer to hold nl80211 attributes |
| * @attr: Pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_get_tx_amsdu(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| int value; |
| |
| value = wma_cli_get_command(adapter->vdev_id, GEN_VDEV_PARAM_TX_AMSDU, |
| GEN_CMD); |
| if (value < 0) { |
| hdd_err("Failed to get tx_amsdu"); |
| return -EINVAL; |
| } |
| |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MSDU_AGGREGATION, |
| (uint8_t)value)) { |
| hdd_err("nla_put failure"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_get_rx_amsdu() - Get RX AMSDU |
| * @adapter: Pointer to HDD adapter |
| * @skb: sk buffer to hold nl80211 attributes |
| * @attr: Pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_get_rx_amsdu(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| int value; |
| |
| value = wma_cli_get_command(adapter->vdev_id, GEN_VDEV_PARAM_RX_AMSDU, |
| GEN_CMD); |
| if (value < 0) { |
| hdd_err("Failed to get rx_amsdu"); |
| return -EINVAL; |
| } |
| |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MSDU_AGGREGATION, |
| (uint8_t)value)) { |
| hdd_err("nla_put failure"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_get_channel_width() - Get channel width |
| * @adapter: Pointer to HDD adapter |
| * @skb: sk buffer to hold nl80211 attributes |
| * @attr: Pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_get_channel_width(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| enum eSirMacHTChannelWidth chwidth; |
| uint8_t nl80211_chwidth; |
| |
| chwidth = wma_cli_get_command(adapter->vdev_id, WMI_VDEV_PARAM_CHWIDTH, |
| VDEV_CMD); |
| if (chwidth < 0) { |
| hdd_err("Failed to get chwidth"); |
| return -EINVAL; |
| } |
| |
| nl80211_chwidth = hdd_chwidth_to_nl80211_chwidth(chwidth); |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_WIDTH, |
| nl80211_chwidth)) { |
| hdd_err("nla_put failure"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_get_dynamic_bw() - Get dynamic bandwidth disabled / enabled |
| * @adapter: Pointer to HDD adapter |
| * @skb: sk buffer to hold nl80211 attributes |
| * @attr: Pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_get_dynamic_bw(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| int enable; |
| |
| enable = wma_cli_get_command(adapter->vdev_id, |
| WMI_PDEV_PARAM_DYNAMIC_BW, PDEV_CMD); |
| if (enable < 0) { |
| hdd_err("Failed to get dynamic_bw"); |
| return -EINVAL; |
| } |
| |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_BW, |
| (uint8_t)enable)) { |
| hdd_err("nla_put failure"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_get_nss_config() - Get the number of spatial streams supported by |
| * the adapter |
| * @adapter: Pointer to HDD adapter |
| * @skb: sk buffer to hold nl80211 attributes |
| * @attr: Pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_get_nss_config(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| uint8_t nss; |
| |
| if (adapter->device_mode == QDF_SAP_MODE) { |
| int value; |
| |
| value = wma_cli_get_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_NSS, VDEV_CMD); |
| if (value < 0) { |
| hdd_err("Failed to get nss"); |
| return -EINVAL; |
| } |
| nss = (uint8_t)value; |
| } else { |
| QDF_STATUS status; |
| |
| status = hdd_get_nss(adapter, &nss); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("Failed to get nss"); |
| return -EINVAL; |
| } |
| } |
| |
| hdd_debug("nss %d", nss); |
| |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_NSS, nss)) { |
| hdd_err("nla_put failure"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_get_tx_nss_config() - Get the number of tx spatial streams supported by |
| * the adapter |
| * @adapter: Pointer to HDD adapter |
| * @skb: sk buffer to hold nl80211 attributes |
| * @attr: Pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_get_tx_nss_config(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| uint8_t tx_nss; |
| QDF_STATUS status; |
| |
| if (!hdd_is_vdev_in_conn_state(adapter)) { |
| hdd_err("Not in connected state"); |
| return -EINVAL; |
| } |
| |
| status = hdd_get_tx_nss(adapter, &tx_nss); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("Failed to get nss"); |
| return -EINVAL; |
| } |
| |
| hdd_debug("tx_nss %d", tx_nss); |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS, tx_nss)) { |
| hdd_err("nla_put failure"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_get_rx_nss_config() - Get the number of rx spatial streams supported by |
| * the adapter |
| * @adapter: Pointer to HDD adapter |
| * @skb: sk buffer to hold nl80211 attributes |
| * @attr: Pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_get_rx_nss_config(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| uint8_t rx_nss; |
| QDF_STATUS status; |
| |
| if (!hdd_is_vdev_in_conn_state(adapter)) { |
| hdd_err("Not in connected state"); |
| return -EINVAL; |
| } |
| |
| status = hdd_get_rx_nss(adapter, &rx_nss); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("Failed to get nss"); |
| return -EINVAL; |
| } |
| |
| hdd_debug("rx_nss %d", rx_nss); |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS, rx_nss)) { |
| hdd_err("nla_put failure"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_get_optimized_power_config() - Get the number of spatial streams |
| * supported by the adapter |
| * @adapter: Pointer to HDD adapter |
| * @skb: sk buffer to hold nl80211 attributes |
| * @attr: Pointer to struct nlattr |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_get_optimized_power_config(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint8_t optimized_power_cfg; |
| int errno; |
| |
| errno = wlan_hdd_validate_context(hdd_ctx); |
| if (errno) |
| return errno; |
| |
| optimized_power_cfg = ucfg_pmo_get_power_save_mode(hdd_ctx->psoc); |
| |
| if (nla_put_u8(skb, |
| QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT, |
| optimized_power_cfg)) { |
| hdd_err_rl("nla_put failure"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * typedef config_getter_fn - get configuration handler |
| * @adapter: The adapter being configured |
| * @skb: sk buffer to hold nl80211 attributes |
| * @attr: The nl80211 attribute being applied |
| * |
| * Defines the signature of functions in the attribute vtable |
| * |
| * Return: 0 if the attribute was handled successfully, otherwise an errno |
| */ |
| typedef int (*config_getter_fn)(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| const struct nlattr *attr); |
| |
| /** |
| * struct config_getters |
| * @id: vendor attribute which this entry handles |
| * @cb: callback function to invoke to process the attribute when present |
| */ |
| struct config_getters { |
| uint32_t id; |
| size_t max_attr_len; |
| config_getter_fn cb; |
| }; |
| |
| /* vtable for config getters */ |
| static const struct config_getters config_getters[] = { |
| #ifdef WLAN_FEATURE_ELNA |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_ELNA_BYPASS, |
| sizeof(uint8_t), |
| hdd_get_elna_bypass}, |
| #endif |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_ROAM_REASON, |
| sizeof(uint8_t), |
| hdd_get_roam_reason_vsie_status}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION, |
| sizeof(uint8_t), |
| hdd_get_tx_ampdu}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION, |
| sizeof(uint8_t), |
| hdd_get_rx_ampdu}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MSDU_AGGREGATION, |
| sizeof(uint8_t), |
| hdd_get_tx_amsdu}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MSDU_AGGREGATION, |
| sizeof(uint8_t), |
| hdd_get_rx_amsdu}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_LDPC, |
| sizeof(uint8_t), |
| hdd_vendor_attr_ldpc_get}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_TX_STBC, |
| sizeof(uint8_t), |
| hdd_vendor_attr_tx_stbc_get}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_RX_STBC, |
| sizeof(uint8_t), |
| hdd_vendor_attr_rx_stbc_get}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_WIDTH, |
| sizeof(uint8_t), |
| hdd_get_channel_width}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_BW, |
| sizeof(uint8_t), |
| hdd_get_dynamic_bw}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_NSS, |
| sizeof(uint8_t), |
| hdd_get_nss_config}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT, |
| sizeof(uint8_t), |
| hdd_get_optimized_power_config}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS, |
| sizeof(uint8_t), |
| hdd_get_tx_nss_config}, |
| {QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS, |
| sizeof(uint8_t), |
| hdd_get_rx_nss_config}, |
| }; |
| |
| /** |
| * hdd_get_configuration() - Handle get configuration |
| * @adapter: adapter upon which the vendor command was received |
| * @tb: parsed attribute array |
| * |
| * This is a table-driven function which dispatches attributes |
| * in a QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION |
| * vendor command. |
| * |
| * Return: 0 if there were no issues, otherwise errno of the last issue |
| */ |
| static int hdd_get_configuration(struct hdd_adapter *adapter, |
| struct nlattr **tb) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint32_t i, id; |
| unsigned long nl_buf_len = NLMSG_HDRLEN; |
| struct sk_buff *skb; |
| struct nlattr *attr; |
| config_getter_fn cb; |
| int errno = 0; |
| |
| for (i = 0; i < QDF_ARRAY_SIZE(config_getters); i++) { |
| id = config_getters[i].id; |
| attr = tb[id]; |
| if (!attr) |
| continue; |
| |
| nl_buf_len += NLA_HDRLEN + |
| NLA_ALIGN(config_getters[i].max_attr_len); |
| } |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| if (!skb) { |
| hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| return -ENOMEM; |
| } |
| |
| for (i = 0; i < QDF_ARRAY_SIZE(config_getters); i++) { |
| id = config_getters[i].id; |
| attr = tb[id]; |
| if (!attr) |
| continue; |
| |
| hdd_debug("Get wifi configuration %d", id); |
| |
| cb = config_getters[i].cb; |
| errno = cb(adapter, skb, attr); |
| if (errno) |
| break; |
| } |
| |
| if (errno) { |
| hdd_err("Failed to get wifi configuration, errno = %d", errno); |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| cfg80211_vendor_cmd_reply(skb); |
| |
| return errno; |
| } |
| |
| /** |
| * hdd_set_independent_configuration() - Handle independent attributes |
| * @adapter: adapter upon which the vendor command was received |
| * @tb: parsed attribute array |
| * |
| * This is a table-driven function which dispatches independent |
| * attributes in a QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION |
| * vendor command. An attribute is considered independent if it |
| * doesn't depend upon any other attributes |
| * |
| * Return: 0 if there were no issues, otherwise errno of the last issue |
| */ |
| static int hdd_set_independent_configuration(struct hdd_adapter *adapter, |
| struct nlattr **tb) |
| { |
| uint32_t i; |
| uint32_t id; |
| struct nlattr *attr; |
| independent_setter_fn cb; |
| int errno = 0; |
| int ret; |
| |
| for (i = 0; i < QDF_ARRAY_SIZE(independent_setters); i++) { |
| id = independent_setters[i].id; |
| attr = tb[id]; |
| if (!attr) |
| continue; |
| |
| hdd_debug("Set wifi configuration %d", id); |
| |
| cb = independent_setters[i].cb; |
| ret = cb(adapter, attr); |
| if (ret) |
| errno = ret; |
| } |
| |
| return errno; |
| } |
| |
| /** |
| * typedef interdependent_setter_fn - interdependent attribute handler |
| * @adapter: The adapter being configured |
| * @tb: The parsed nl80211 attributes being applied |
| * |
| * Defines the signature of functions in the interdependent attribute vtable |
| * |
| * Return: 0 if attributes were handled successfully, otherwise an errno |
| */ |
| typedef int (*interdependent_setter_fn)(struct hdd_adapter *adapter, |
| struct nlattr **tb); |
| |
| /* vtable for interdependent setters */ |
| static const interdependent_setter_fn interdependent_setters[] = { |
| hdd_config_access_policy, |
| hdd_config_mpdu_aggregation, |
| hdd_config_ant_div_period, |
| hdd_config_ant_div_snr_weight, |
| wlan_hdd_cfg80211_wifi_set_reorder_timeout, |
| wlan_hdd_cfg80211_wifi_set_rx_blocksize, |
| hdd_config_msdu_aggregation, |
| hdd_config_vdev_chains, |
| hdd_config_ani, |
| hdd_config_tx_rx_nss, |
| }; |
| |
| /** |
| * hdd_set_interdependent_configuration() - Handle interdependent attributes |
| * @adapter: adapter upon which the vendor command was received |
| * @tb: parsed attribute array |
| * |
| * This is a table-driven function which handles interdependent |
| * attributes in a QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION |
| * vendor command. A set of attributes is considered interdependent if |
| * they depend upon each other. In the typical case if one of the |
| * attributes is present in the the attribute array, then all of the |
| * attributes must be present. |
| * |
| * Return: 0 if there were no issues, otherwise errno of the last issue |
| */ |
| static int hdd_set_interdependent_configuration(struct hdd_adapter *adapter, |
| struct nlattr **tb) |
| { |
| uint32_t i; |
| interdependent_setter_fn cb; |
| int errno = 0; |
| int ret; |
| |
| for (i = 0; i < QDF_ARRAY_SIZE(interdependent_setters); i++) { |
| cb = interdependent_setters[i]; |
| ret = cb(adapter, tb); |
| if (ret) |
| errno = ret; |
| } |
| |
| return errno; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_wifi_configuration_set() - Wifi configuration |
| * vendor command |
| * |
| * @wiphy: wiphy device pointer |
| * @wdev: wireless device pointer |
| * @data: Vendor command data buffer |
| * @data_len: Buffer length |
| * |
| * Handles QCA_WLAN_VENDOR_ATTR_CONFIG_MAX. |
| * |
| * Return: Error code. |
| */ |
| static int |
| __wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1]; |
| int errno; |
| int ret; |
| |
| hdd_enter_dev(dev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| errno = wlan_hdd_validate_context(hdd_ctx); |
| if (errno) |
| return errno; |
| |
| if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX, data, |
| data_len, wlan_hdd_wifi_config_policy)) { |
| hdd_err("invalid attr"); |
| return -EINVAL; |
| } |
| |
| ret = hdd_set_independent_configuration(adapter, tb); |
| if (ret) |
| errno = ret; |
| |
| ret = hdd_set_interdependent_configuration(adapter, tb); |
| if (ret) |
| errno = ret; |
| |
| return errno; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_wifi_configuration_set() - Wifi configuration |
| * vendor command |
| * |
| * @wiphy: wiphy device pointer |
| * @wdev: wireless device pointer |
| * @data: Vendor command data buffer |
| * @data_len: Buffer length |
| * |
| * Handles QCA_WLAN_VENDOR_ATTR_CONFIG_MAX. |
| * |
| * Return: EOK or other error codes. |
| */ |
| static int wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_wifi_configuration_set(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_wifi_configuration_get() - Wifi configuration |
| * vendor command |
| * @wiphy: wiphy device pointer |
| * @wdev: wireless device pointer |
| * @data: Vendor command data buffer |
| * @data_len: Buffer length |
| * |
| * Handles QCA_WLAN_VENDOR_ATTR_CONFIG_MAX. |
| * |
| * Return: Error code. |
| */ |
| static int |
| __wlan_hdd_cfg80211_wifi_configuration_get(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1]; |
| int errno; |
| int ret; |
| |
| hdd_enter_dev(dev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| errno = wlan_hdd_validate_context(hdd_ctx); |
| if (errno) |
| return errno; |
| |
| if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX, data, |
| data_len, wlan_hdd_wifi_config_policy)) { |
| hdd_err("invalid attr"); |
| return -EINVAL; |
| } |
| |
| ret = hdd_get_configuration(adapter, tb); |
| if (ret) |
| errno = ret; |
| |
| return errno; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_wifi_configuration_get() - Wifi configuration |
| * vendor command |
| * |
| * @wiphy: wiphy device pointer |
| * @wdev: wireless device pointer |
| * @data: Vendor command data buffer |
| * @data_len: Buffer length |
| * |
| * Handles QCA_WLAN_VENDOR_ATTR_CONFIG_MAX. |
| * |
| * Return: EOK or other error codes. |
| */ |
| static int wlan_hdd_cfg80211_wifi_configuration_get(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_wifi_configuration_get(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| static void hdd_disable_runtime_pm_for_user(struct hdd_context *hdd_ctx) |
| { |
| struct hdd_runtime_pm_context *ctx = &hdd_ctx->runtime_context; |
| |
| if (!ctx) |
| return; |
| |
| if (ctx->is_user_wakelock_acquired) |
| return; |
| |
| ctx->is_user_wakelock_acquired = true; |
| qdf_runtime_pm_prevent_suspend(&ctx->user); |
| } |
| |
| static int hdd_test_config_6ghz_security_test_mode(struct hdd_context *hdd_ctx, |
| struct nlattr *attr) |
| |
| { |
| uint8_t cfg_val; |
| bool rf_test_mode = false; |
| QDF_STATUS status; |
| |
| status = ucfg_mlme_is_rf_test_mode_enabled(hdd_ctx->psoc, |
| &rf_test_mode); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("Get rf test mode failed"); |
| return -EINVAL; |
| } |
| if (rf_test_mode) { |
| hdd_err("rf test mode is enabled, ignore setting"); |
| return 0; |
| } |
| |
| cfg_val = nla_get_u8(attr); |
| hdd_debug("safe mode setting %d", cfg_val); |
| wlan_mlme_set_safe_mode_enable(hdd_ctx->psoc, cfg_val); |
| if (cfg_val) { |
| wlan_cm_set_check_6ghz_security(hdd_ctx->psoc, false); |
| wlan_cm_set_6ghz_key_mgmt_mask(hdd_ctx->psoc, |
| DEFAULT_KEYMGMT_6G_MASK); |
| } else { |
| wlan_cm_set_check_6ghz_security(hdd_ctx->psoc, true); |
| wlan_cm_set_6ghz_key_mgmt_mask(hdd_ctx->psoc, |
| ALLOWED_KEYMGMT_6G_MASK); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_set_wifi_test_config() - Wifi test configuration |
| * vendor command |
| * |
| * @wiphy: wiphy device pointer |
| * @wdev: wireless device pointer |
| * @data: Vendor command data buffer |
| * @data_len: Buffer length |
| * |
| * Handles QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX |
| * |
| * Return: Error code. |
| */ |
| static int |
| __wlan_hdd_cfg80211_set_wifi_test_config(struct wiphy *wiphy, |
| struct wireless_dev *wdev, const void *data, int data_len) |
| { |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX + 1]; |
| int ret_val = 0; |
| uint8_t cfg_val = 0; |
| uint8_t ini_val = 0; |
| uint8_t set_val = 0; |
| struct sme_config_params *sme_config; |
| bool update_sme_cfg = false; |
| uint8_t tid = 0, ac; |
| uint16_t buff_size = 0; |
| mac_handle_t mac_handle; |
| QDF_STATUS status; |
| bool bval = false; |
| uint8_t value = 0; |
| uint8_t wmm_mode = 0; |
| uint32_t bss_max_idle_period = 0; |
| uint32_t cmd_id; |
| struct keep_alive_req keep_alive_req = {0}; |
| struct set_wfatest_params wfa_param = {0}; |
| struct hdd_station_ctx *hdd_sta_ctx = |
| WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| |
| hdd_enter_dev(dev); |
| |
| sme_config = qdf_mem_malloc(sizeof(*sme_config)); |
| if (!sme_config) |
| return -ENOMEM; |
| |
| mac_handle = hdd_ctx->mac_handle; |
| sme_get_config_param(mac_handle, sme_config); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| ret_val = -EPERM; |
| goto send_err; |
| } |
| |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| goto send_err; |
| |
| if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { |
| hdd_err("Driver Modules are closed, can not start logger"); |
| ret_val = -EINVAL; |
| goto send_err; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb, |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX, |
| data, data_len, wlan_hdd_wifi_test_config_policy)) { |
| hdd_err("invalid attr"); |
| ret_val = -EINVAL; |
| goto send_err; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ]) { |
| cfg_val = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ] |
| ); |
| hdd_debug("set addba accept req from peer value %d", cfg_val); |
| ret_val = sme_set_addba_accept(mac_handle, adapter->vdev_id, |
| cfg_val); |
| if (ret_val) |
| goto send_err; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS]) { |
| cfg_val = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS]); |
| hdd_debug("set HE MCS value 0x%0X", cfg_val); |
| ret_val = sme_update_he_mcs(mac_handle, adapter->vdev_id, |
| cfg_val); |
| if (ret_val) |
| goto send_err; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE]) { |
| cfg_val = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE]); |
| if (!cfg_val) { |
| sme_config->csr_config.WMMSupportMode = |
| hdd_to_csr_wmm_mode(HDD_WMM_USER_MODE_NO_QOS); |
| hdd_debug("wmm is disabled"); |
| } else { |
| status = ucfg_mlme_get_wmm_mode(hdd_ctx->psoc, |
| &wmm_mode); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("Get wmm_mode failed"); |
| ret_val = -EINVAL; |
| goto send_err; |
| } |
| sme_config->csr_config.WMMSupportMode = |
| hdd_to_csr_wmm_mode(wmm_mode); |
| hdd_debug("using wmm default value"); |
| } |
| update_sme_cfg = true; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ]) { |
| cfg_val = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ]); |
| if (cfg_val) { |
| /*Auto BA mode*/ |
| set_val = 0; |
| hdd_debug("BA operating mode is set to auto"); |
| } else { |
| /*Manual BA mode*/ |
| set_val = 1; |
| hdd_debug("BA operating mode is set to Manual"); |
| } |
| |
| ret_val = wma_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_BA_MODE, set_val, VDEV_CMD); |
| if (ret_val) { |
| hdd_err("Set BA operating mode failed"); |
| goto send_err; |
| } |
| if (!cfg_val) { |
| ret_val = wma_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_AMSDU_AGGREGATION_SIZE_OPTIMIZATION, |
| 0, VDEV_CMD); |
| } |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION]) { |
| cfg_val = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION] |
| ); |
| if (cfg_val > HE_FRAG_LEVEL1) |
| set_val = HE_FRAG_LEVEL1; |
| else |
| set_val = cfg_val; |
| |
| hdd_debug("set HE fragmention to %d", set_val); |
| ret_val = sme_update_he_frag_supp(mac_handle, |
| adapter->vdev_id, set_val); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE]) { |
| cfg_val = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE]); |
| sme_config->csr_config.wep_tkip_in_he = cfg_val; |
| hdd_debug("Set WEP/TKIP allow in HE %d", cfg_val); |
| |
| update_sme_cfg = true; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION]) { |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID]) { |
| tid = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID]); |
| } else { |
| hdd_err("TID is not set for ADD/DEL BA cfg"); |
| ret_val = -EINVAL; |
| goto send_err; |
| } |
| cfg_val = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION]); |
| if (cfg_val == QCA_WLAN_ADD_BA) { |
| if (tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE]) |
| buff_size = nla_get_u16(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE]); |
| ret_val = sme_send_addba_req(mac_handle, |
| adapter->vdev_id, |
| tid, buff_size); |
| } else if (cfg_val == QCA_WLAN_DELETE_BA) { |
| } else { |
| hdd_err("Invalid BA session cfg"); |
| ret_val = -EINVAL; |
| goto send_err; |
| } |
| } else if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE]) { |
| buff_size = nla_get_u16(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE]); |
| hdd_debug("set buff size to %d for all tids", buff_size); |
| ret_val = sme_set_ba_buff_size(mac_handle, |
| adapter->vdev_id, buff_size); |
| if (ret_val) |
| goto send_err; |
| if (buff_size > 64) |
| /* Configure ADDBA req buffer size to 256 */ |
| set_val = 3; |
| else |
| /* Configure ADDBA req buffer size to 64 */ |
| set_val = 2; |
| ret_val = wma_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_BA_MODE, set_val, VDEV_CMD); |
| if (ret_val) |
| hdd_err("Failed to set BA operating mode %d", set_val); |
| ret_val = wma_cli_set_command(adapter->vdev_id, |
| GEN_VDEV_PARAM_AMPDU, |
| buff_size, GEN_CMD); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK]) { |
| int he_mcs_val; |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC]) { |
| ac = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC]); |
| } else { |
| hdd_err("AC is not set for NO ACK policy config"); |
| ret_val = -EINVAL; |
| goto send_err; |
| } |
| cfg_val = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK]); |
| hdd_debug("Set NO_ACK to %d for ac %d", cfg_val, ac); |
| ret_val = sme_set_no_ack_policy(mac_handle, |
| adapter->vdev_id, |
| cfg_val, ac); |
| if (cfg_val) { |
| status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, |
| &bval); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("unable to get vht_enable2x2"); |
| if (bval) |
| /*2x2 MCS 5 value*/ |
| he_mcs_val = 0x45; |
| else |
| /*1x1 MCS 5 value*/ |
| he_mcs_val = 0x25; |
| |
| if (hdd_set_11ax_rate(adapter, he_mcs_val, NULL)) |
| hdd_err("HE MCS set failed, MCS val %0x", |
| he_mcs_val); |
| } else { |
| if (hdd_set_11ax_rate(adapter, 0xFF, NULL)) |
| hdd_err("disable fixed rate failed"); |
| } |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_KEEP_ALIVE_FRAME_TYPE; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| hdd_debug("Keep alive data type %d", cfg_val); |
| if (cfg_val == QCA_WLAN_KEEP_ALIVE_DATA) { |
| ret_val = hdd_set_grat_arp_keepalive(adapter); |
| if (ret_val) { |
| hdd_err("Keep alive data type set failed"); |
| goto send_err; |
| } |
| } else { |
| if (cfg_val == QCA_WLAN_KEEP_ALIVE_MGMT) |
| keep_alive_req.packetType = |
| SIR_KEEP_ALIVE_MGMT_FRAME; |
| else |
| keep_alive_req.packetType = |
| SIR_KEEP_ALIVE_NULL_PKT; |
| ucfg_mlme_get_sta_keep_alive_period( |
| hdd_ctx->psoc, |
| &keep_alive_req.timePeriod); |
| keep_alive_req.sessionId = adapter->vdev_id; |
| status = sme_set_keep_alive(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| &keep_alive_req); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("Failed to set keepalive"); |
| ret_val = qdf_status_to_os_return(status); |
| goto send_err; |
| } |
| } |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF]) { |
| cfg_val = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF]); |
| hdd_debug("Set HE LTF to %d", cfg_val); |
| ret_val = sme_set_auto_rate_he_ltf(mac_handle, |
| adapter->vdev_id, |
| cfg_val); |
| if (ret_val) |
| sme_err("Failed to set auto rate HE LTF"); |
| |
| ret_val = wma_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_HE_LTF, |
| cfg_val, VDEV_CMD); |
| if (ret_val) |
| goto send_err; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE]) { |
| cfg_val = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE]); |
| hdd_debug("Set Tx beamformee to %d", cfg_val); |
| ret_val = sme_update_tx_bfee_supp(mac_handle, |
| adapter->vdev_id, |
| cfg_val); |
| if (ret_val) |
| sme_err("Failed to set Tx beamformee cap"); |
| |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_RX_CTRL_FRAME_TO_MBSS; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| ret_val = sme_update_he_capabilities(mac_handle, |
| adapter->vdev_id, |
| cfg_val, cmd_id); |
| if (ret_val) |
| sme_err("Failed to update HE cap"); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BCAST_TWT_SUPPORT; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| ret_val = sme_update_he_capabilities(mac_handle, |
| adapter->vdev_id, |
| cfg_val, cmd_id); |
| if (ret_val) |
| sme_err("Failed to update HE cap"); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS]) { |
| cfg_val = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS]); |
| status = ucfg_mlme_cfg_get_vht_tx_bfee_ant_supp(hdd_ctx->psoc, |
| &value); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("unable to get tx_bfee_ant_supp"); |
| |
| if (!cfg_in_range(CFG_VHT_BEAMFORMEE_ANT_SUPP, cfg_val)) { |
| hdd_err("NSTS %d not supported, supp_val %d", cfg_val, |
| value); |
| ret_val = -ENOTSUPP; |
| goto send_err; |
| } |
| hdd_debug("Set Tx beamformee NSTS to %d", cfg_val); |
| ret_val = sme_update_tx_bfee_nsts(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| cfg_val, |
| value); |
| if (ret_val) |
| sme_err("Failed to set Tx beamformee cap"); |
| |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR]) { |
| cfg_val = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR]); |
| if (cfg_val) { |
| hdd_debug("Set HE mac padding dur to %d", cfg_val); |
| ret_val = sme_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_MU_EDCA_FW_UPDATE_EN, |
| 0, VDEV_CMD); |
| if (ret_val) |
| hdd_err("MU_EDCA update disable failed"); |
| sme_set_usr_cfg_mu_edca(hdd_ctx->mac_handle, true); |
| sme_set_he_mu_edca_def_cfg(hdd_ctx->mac_handle); |
| if (sme_update_mu_edca_params(hdd_ctx->mac_handle, |
| adapter->vdev_id)) |
| hdd_err("Failed to send mu edca params"); |
| } else { |
| ret_val = sme_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_MU_EDCA_FW_UPDATE_EN, |
| 1, VDEV_CMD); |
| sme_set_usr_cfg_mu_edca(hdd_ctx->mac_handle, false); |
| } |
| ret_val = sme_update_he_trigger_frm_mac_pad(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| cfg_val); |
| if (ret_val) |
| hdd_err("Failed to set Trig frame mac padding cap"); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA]) { |
| cfg_val = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA]); |
| if (cfg_val) { |
| ret_val = sme_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_MU_EDCA_FW_UPDATE_EN, |
| 0, VDEV_CMD); |
| if (ret_val) |
| hdd_err("MU_EDCA update disable failed"); |
| sme_set_usr_cfg_mu_edca(hdd_ctx->mac_handle, true); |
| sme_set_he_mu_edca_def_cfg(hdd_ctx->mac_handle); |
| if (sme_update_mu_edca_params(hdd_ctx->mac_handle, |
| adapter->vdev_id)) |
| hdd_err("Failed to send mu edca params"); |
| } else { |
| ret_val = sme_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_MU_EDCA_FW_UPDATE_EN, |
| 1, VDEV_CMD); |
| sme_set_usr_cfg_mu_edca(hdd_ctx->mac_handle, false); |
| } |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP]) { |
| cfg_val = nla_get_u8(tb[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP]); |
| ret_val = sme_update_he_om_ctrl_supp(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| cfg_val); |
| } |
| |
| cmd_id = |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_USE_BSSID_IN_PROBE_REQ_RA; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| if (cfg_val) |
| status = ucfg_mlme_set_scan_probe_unicast_ra( |
| hdd_ctx->psoc, true); |
| else |
| status = ucfg_mlme_set_scan_probe_unicast_ra( |
| hdd_ctx->psoc, false); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("unable to set unicat probe ra cfg"); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX; |
| if (tb[cmd_id]) { |
| struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX + 1]; |
| struct nlattr *curr_attr; |
| int tmp, rc; |
| nla_for_each_nested(curr_attr, tb[cmd_id], tmp) { |
| rc = wlan_cfg80211_nla_parse( |
| tb2, QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX, |
| nla_data(curr_attr), |
| nla_len(curr_attr), |
| qca_wlan_vendor_attr_he_omi_tx_policy); |
| if (rc) { |
| hdd_err("Invalid ATTR"); |
| goto send_err; |
| } |
| cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW; |
| if (tb2[cmd_id]) { |
| cfg_val = nla_get_u8(tb2[cmd_id]); |
| ret_val = sme_set_he_om_ctrl_param( |
| hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| cmd_id, cfg_val); |
| } |
| cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS; |
| if (tb2[cmd_id]) { |
| cfg_val = nla_get_u8(tb2[cmd_id]); |
| ret_val = sme_set_he_om_ctrl_param( |
| hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| cmd_id, cfg_val); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE; |
| if (tb2[cmd_id]) { |
| cfg_val = nla_get_u8(tb2[cmd_id]); |
| ret_val = sme_set_he_om_ctrl_param( |
| hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| cmd_id, cfg_val); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS; |
| if (tb2[cmd_id]) { |
| cfg_val = nla_get_u8(tb2[cmd_id]); |
| ret_val = sme_set_he_om_ctrl_param( |
| hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| cmd_id, cfg_val); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE; |
| if (tb2[cmd_id]) { |
| cfg_val = nla_get_u8(tb2[cmd_id]); |
| ret_val = sme_set_he_om_ctrl_param( |
| hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| cmd_id, cfg_val); |
| } |
| |
| } |
| if (ret_val) { |
| sme_reset_he_om_ctrl(hdd_ctx->mac_handle); |
| goto send_err; |
| } |
| ret_val = sme_send_he_om_ctrl_update(hdd_ctx->mac_handle, |
| adapter->vdev_id); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG]) |
| sme_reset_he_om_ctrl(hdd_ctx->mac_handle); |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_SUPPDU; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| hdd_debug("Configure Tx SU PPDU enable %d", cfg_val); |
| if (cfg_val) |
| sme_config_su_ppdu_queue(adapter->vdev_id, true); |
| else |
| sme_config_su_ppdu_queue(adapter->vdev_id, false); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| hdd_debug("Configure 2G VHT support %d", cfg_val); |
| ucfg_mlme_set_vht_for_24ghz(hdd_ctx->psoc, |
| (cfg_val ? true : false)); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| hdd_debug("Configure +HTC_HE support %d", cfg_val); |
| sme_update_he_htc_he_supp(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| (cfg_val ? true : false)); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_PUNCTURED_PREAMBLE_RX; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| hdd_debug("Configure Punctured preamble Rx %d", cfg_val); |
| ret_val = sme_update_he_capabilities(mac_handle, |
| adapter->vdev_id, |
| cfg_val, cmd_id); |
| if (ret_val) |
| sme_err("Failed to update HE cap"); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS; |
| if (tb[cmd_id]) { |
| hdd_disable_runtime_pm_for_user(hdd_ctx); |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| hdd_debug("Configure HE testbed defaults %d", cfg_val); |
| if (!cfg_val) |
| sme_reset_he_caps(hdd_ctx->mac_handle, |
| adapter->vdev_id); |
| else |
| sme_set_he_testbed_def(hdd_ctx->mac_handle, |
| adapter->vdev_id); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| hdd_debug("Configure Action frame Tx in TB PPDU %d", cfg_val); |
| sme_config_action_tx_in_tb_ppdu(hdd_ctx->mac_handle, |
| adapter->vdev_id, cfg_val); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP]) { |
| ret_val = hdd_test_config_twt_setup_session(adapter, tb); |
| if (ret_val) |
| goto send_err; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_TERMINATE]) { |
| ret_val = hdd_test_config_twt_terminate_session(adapter, tb); |
| if (ret_val) |
| goto send_err; |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| hdd_debug("twt_request: val %d", cfg_val); |
| ret_val = sme_update_he_twt_req_support(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| cfg_val); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FULL_BW_UL_MU_MIMO; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| ini_val = cfg_get(hdd_ctx->psoc, CFG_HE_UL_MUMIMO); |
| hdd_debug("fullbw_ulmumimo: cfg %d, ini %d", cfg_val, ini_val); |
| if (cfg_val) { |
| switch (ini_val) { |
| case CFG_NO_SUPPORT_UL_MUMIMO: |
| case CFG_FULL_BW_SUPPORT_UL_MUMIMO: |
| cfg_val = CFG_FULL_BW_SUPPORT_UL_MUMIMO; |
| break; |
| case CFG_PARTIAL_BW_SUPPORT_UL_MUMIMO: |
| case CFG_FULL_PARTIAL_BW_SUPPORT_UL_MUMIMO: |
| cfg_val = CFG_FULL_PARTIAL_BW_SUPPORT_UL_MUMIMO; |
| break; |
| } |
| } else { |
| switch (ini_val) { |
| case CFG_NO_SUPPORT_UL_MUMIMO: |
| case CFG_FULL_BW_SUPPORT_UL_MUMIMO: |
| cfg_val = CFG_NO_SUPPORT_UL_MUMIMO; |
| break; |
| case CFG_PARTIAL_BW_SUPPORT_UL_MUMIMO: |
| case CFG_FULL_PARTIAL_BW_SUPPORT_UL_MUMIMO: |
| cfg_val = CFG_PARTIAL_BW_SUPPORT_UL_MUMIMO; |
| break; |
| } |
| } |
| ret_val = sme_update_he_full_ul_mumimo(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| cfg_val); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_DISABLE_DATA_MGMT_RSP_TX; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| hdd_debug("disable Tx cfg: val %d", cfg_val); |
| sme_set_cfg_disable_tx(hdd_ctx->mac_handle, adapter->vdev_id, |
| cfg_val); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_PMF_PROTECTION; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| hdd_debug("pmf cfg: val %d", cfg_val); |
| sme_set_pmf_wep_cfg(hdd_ctx->mac_handle, cfg_val); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BSS_MAX_IDLE_PERIOD; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u16(tb[cmd_id]); |
| hdd_debug("bss max idle period %d", cfg_val); |
| sme_set_bss_max_idle_period(hdd_ctx->mac_handle, cfg_val); |
| } |
| |
| cmd_id = |
| QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BSS_MAX_IDLE_PERIOD_ENABLE; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| if (cfg_val) |
| ucfg_mlme_get_sta_keep_alive_period( |
| hdd_ctx->psoc, |
| &bss_max_idle_period); |
| hdd_debug("bss max idle period %d", bss_max_idle_period); |
| sme_set_bss_max_idle_period(hdd_ctx->mac_handle, |
| bss_max_idle_period); |
| } |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_DISASSOC_TX; |
| if (tb[cmd_id]) { |
| hdd_info("Send disassoc mgmt frame"); |
| sme_send_disassoc_req_frame(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| hdd_sta_ctx->conn_info.bssid.bytes, |
| 1, false); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_RU_242_TONE_TX; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| hdd_info("RU 242 tone Tx enable: %d", cfg_val); |
| sme_set_ru_242_tone_tx_cfg(hdd_ctx->mac_handle, cfg_val); |
| if (cfg_val) |
| hdd_update_channel_width( |
| adapter, eHT_CHANNEL_WIDTH_20MHZ, |
| WNI_CFG_CHANNEL_BONDING_MODE_DISABLE); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ER_SU_PPDU_TYPE; |
| if (tb[cmd_id]) { |
| cfg_val = nla_get_u8(tb[cmd_id]); |
| hdd_debug("EU SU PPDU type Tx enable: %d", cfg_val); |
| if (cfg_val) { |
| hdd_update_channel_width( |
| adapter, eHT_CHANNEL_WIDTH_20MHZ, |
| WNI_CFG_CHANNEL_BONDING_MODE_DISABLE); |
| hdd_set_tx_stbc(adapter, 0); |
| hdd_set_11ax_rate(adapter, 0x400, NULL); |
| status = wma_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_HE_RANGE_EXT, |
| 1, VDEV_CMD); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("failed to set HE_RANGE_EXT, %d", |
| status); |
| status = wma_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_NON_DATA_HE_RANGE_EXT, |
| 1, VDEV_CMD); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("fail to set NON_DATA_HE_RANGE_EXT %d", |
| status); |
| } else { |
| hdd_update_channel_width( |
| adapter, eHT_CHANNEL_WIDTH_80MHZ, |
| WNI_CFG_CHANNEL_BONDING_MODE_ENABLE); |
| hdd_set_tx_stbc(adapter, 1); |
| hdd_set_11ax_rate(adapter, 0xFFFF, NULL); |
| status = wma_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_HE_RANGE_EXT, |
| 0, VDEV_CMD); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("failed to set HE_RANGE_EXT, %d", |
| status); |
| status = wma_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_NON_DATA_HE_RANGE_EXT, |
| 0, VDEV_CMD); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("fail to set NON_DATA_HE_RANGE_EXT %d", |
| status); |
| } |
| |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FT_REASSOCREQ_RSNXE_USED; |
| if (tb[cmd_id]) { |
| wfa_param.vdev_id = adapter->vdev_id; |
| wfa_param.value = nla_get_u8(tb[cmd_id]); |
| |
| if (wfa_param.value < RSNXE_DEFAULT || |
| wfa_param.value > RSNXE_OVERRIDE_2) { |
| hdd_debug("Invalid RSNXE override %d", wfa_param.value); |
| goto send_err; |
| } |
| wfa_param.cmd = WFA_CONFIG_RXNE; |
| hdd_info("send wfa test config RXNE used %d", wfa_param.value); |
| |
| ret_val = ucfg_send_wfatest_cmd(adapter->vdev, &wfa_param); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_CSA; |
| if (tb[cmd_id]) { |
| wfa_param.vdev_id = adapter->vdev_id; |
| wfa_param.value = nla_get_u8(tb[cmd_id]); |
| |
| if (wfa_param.value != CSA_DEFAULT && |
| wfa_param.value != CSA_IGNORE) { |
| hdd_debug("Invalid CSA config %d", wfa_param.value); |
| goto send_err; |
| } |
| wfa_param.cmd = WFA_CONFIG_CSA; |
| hdd_info("send wfa test config CSA used %d", wfa_param.value); |
| |
| ret_val = ucfg_send_wfatest_cmd(adapter->vdev, &wfa_param); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_6GHZ_SECURITY_TEST_MODE; |
| if (tb[cmd_id]) { |
| ret_val = hdd_test_config_6ghz_security_test_mode(hdd_ctx, |
| tb[cmd_id]); |
| if (ret_val) |
| goto send_err; |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OCI_OVERRIDE; |
| if (tb[cmd_id]) { |
| struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_MAX + 1]; |
| |
| wfa_param.vdev_id = adapter->vdev_id; |
| wfa_param.cmd = WFA_CONFIG_OCV; |
| if (wlan_cfg80211_nla_parse_nested( |
| tb2, QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_MAX, |
| tb[cmd_id], wlan_oci_override_policy)) { |
| hdd_debug("Failed to parse OCI override"); |
| goto send_err; |
| } |
| |
| if (!(tb2[QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FRAME_TYPE] && |
| tb2[QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FREQUENCY])) { |
| hdd_debug("Invalid ATTR FRAME_TYPE/FREQUENCY"); |
| goto send_err; |
| } |
| |
| wfa_param.ocv_param = qdf_mem_malloc( |
| sizeof(struct ocv_wfatest_params)); |
| if (!wfa_param.ocv_param) { |
| hdd_err("Failed to alloc memory for ocv param"); |
| goto send_err; |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FRAME_TYPE; |
| switch (nla_get_u8(tb2[cmd_id])) { |
| case QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_REQ: |
| wfa_param.ocv_param->frame_type = |
| WMI_HOST_WFA_CONFIG_OCV_FRMTYPE_SAQUERY_REQ; |
| break; |
| |
| case QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_RESP: |
| wfa_param.ocv_param->frame_type = |
| WMI_HOST_WFA_CONFIG_OCV_FRMTYPE_SAQUERY_RSP; |
| break; |
| |
| case QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FT_REASSOC_REQ: |
| wfa_param.ocv_param->frame_type = |
| WMI_HOST_WFA_CONFIG_OCV_FRMTYPE_FT_REASSOC_REQ; |
| break; |
| |
| case QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FILS_REASSOC_REQ: |
| wfa_param.ocv_param->frame_type = |
| WMI_HOST_WFA_CONFIG_OCV_FRMTYPE_FILS_REASSOC_REQ; |
| break; |
| |
| default: |
| hdd_debug("Invalid frame type for ocv test config %d", |
| nla_get_u8(tb2[cmd_id])); |
| qdf_mem_free(wfa_param.ocv_param); |
| goto send_err; |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FREQUENCY; |
| wfa_param.ocv_param->freq = nla_get_u32(tb2[cmd_id]); |
| |
| if (!WLAN_REG_IS_24GHZ_CH_FREQ(wfa_param.ocv_param->freq) && |
| !WLAN_REG_IS_5GHZ_CH_FREQ(wfa_param.ocv_param->freq) && |
| !WLAN_REG_IS_6GHZ_CHAN_FREQ(wfa_param.ocv_param->freq)) { |
| hdd_debug("Invalid Freq %d", wfa_param.ocv_param->freq); |
| qdf_mem_free(wfa_param.ocv_param); |
| goto send_err; |
| } |
| |
| hdd_info("send wfa test config OCV frame type %d freq %d", |
| wfa_param.ocv_param->frame_type, |
| wfa_param.ocv_param->freq); |
| ret_val = ucfg_send_wfatest_cmd(adapter->vdev, &wfa_param); |
| qdf_mem_free(wfa_param.ocv_param); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_SA_QUERY_TIMEOUT; |
| if (tb[cmd_id]) { |
| wfa_param.vdev_id = adapter->vdev_id; |
| wfa_param.value = nla_get_u8(tb[cmd_id]); |
| |
| if (wfa_param.value != SA_QUERY_TIMEOUT_DEFAULT && |
| wfa_param.value != SA_QUERY_TIMEOUT_IGNORE) { |
| hdd_debug("Invalid SA query timeout config %d", |
| wfa_param.value); |
| goto send_err; |
| } |
| wfa_param.cmd = WFA_CONFIG_SA_QUERY; |
| hdd_info("send wfa test config SAquery %d", wfa_param.value); |
| |
| ret_val = ucfg_send_wfatest_cmd(adapter->vdev, &wfa_param); |
| } |
| |
| cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FILS_DISCOVERY_FRAMES_TX; |
| if (tb[cmd_id] && adapter->device_mode == QDF_SAP_MODE) { |
| wfa_param.vdev_id = adapter->vdev_id; |
| wfa_param.value = nla_get_u8(tb[cmd_id]); |
| |
| if (!(wfa_param.value == FILS_DISCV_FRAMES_DISABLE || |
| wfa_param.value == FILS_DISCV_FRAMES_ENABLE)) { |
| hdd_debug("Invalid FILS_DISCV_FRAMES config %d", |
| wfa_param.value); |
| goto send_err; |
| } |
| wfa_param.cmd = WFA_FILS_DISCV_FRAMES; |
| hdd_info("send wfa FILS_DISCV_FRAMES TX config %d", |
| wfa_param.value); |
| ret_val = ucfg_send_wfatest_cmd(adapter->vdev, &wfa_param); |
| } |
| |
| if (update_sme_cfg) |
| sme_update_config(mac_handle, sme_config); |
| |
| send_err: |
| qdf_mem_free(sme_config); |
| |
| return ret_val; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_wifi_test_config() - Wifi test configuration |
| * vendor command |
| * |
| * @wiphy: wiphy device pointer |
| * @wdev: wireless device pointer |
| * @data: Vendor command data buffer |
| * @data_len: Buffer length |
| * |
| * Handles QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX |
| * |
| * Return: EOK or other error codes. |
| */ |
| static int wlan_hdd_cfg80211_set_wifi_test_config(struct wiphy *wiphy, |
| struct wireless_dev *wdev, const void *data, int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_wifi_test_config(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| const struct nla_policy qca_wlan_vendor_wifi_logger_start_policy[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID] |
| = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL] |
| = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS] |
| = {.type = NLA_U32 }, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_wifi_logger_start() - This function is used to enable |
| * or disable the collection of packet statistics from the firmware |
| * @wiphy: WIPHY structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of the data received |
| * |
| * This function enables or disables the collection of packet statistics from |
| * the firmware |
| * |
| * Return: 0 on success and errno on failure |
| */ |
| static int __wlan_hdd_cfg80211_wifi_logger_start(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| QDF_STATUS status; |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX + 1]; |
| struct sir_wifi_start_log start_log = { 0 }; |
| mac_handle_t mac_handle; |
| |
| hdd_enter_dev(wdev->netdev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (status) |
| return status; |
| |
| if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { |
| hdd_err("Driver Modules are closed, can not start logger"); |
| return -EINVAL; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb, |
| QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX, |
| data, data_len, |
| qca_wlan_vendor_wifi_logger_start_policy)) { |
| hdd_err("Invalid attribute"); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch ring id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]) { |
| hdd_err("attr ATTR failed"); |
| return -EINVAL; |
| } |
| start_log.ring_id = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]); |
| hdd_debug("Ring ID=%d", start_log.ring_id); |
| |
| /* Parse and fetch verbose level */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]) { |
| hdd_err("attr verbose_level failed"); |
| return -EINVAL; |
| } |
| start_log.verbose_level = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]); |
| hdd_debug("verbose_level=%d", start_log.verbose_level); |
| |
| /* Parse and fetch flag */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]) { |
| hdd_err("attr flag failed"); |
| return -EINVAL; |
| } |
| start_log.is_iwpriv_command = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]); |
| |
| start_log.user_triggered = 1; |
| |
| /* size is buff size which can be set using iwpriv command*/ |
| start_log.size = 0; |
| start_log.is_pktlog_buff_clear = false; |
| |
| cds_set_ring_log_level(start_log.ring_id, start_log.verbose_level); |
| |
| if (start_log.ring_id == RING_ID_WAKELOCK) { |
| /* Start/stop wakelock events */ |
| if (start_log.verbose_level > WLAN_LOG_LEVEL_OFF) |
| cds_set_wakelock_logging(true); |
| else |
| cds_set_wakelock_logging(false); |
| return 0; |
| } |
| |
| if (start_log.ring_id == RING_ID_PER_PACKET_STATS) { |
| if (hdd_ctx->is_pktlog_enabled && |
| (start_log.verbose_level == WLAN_LOG_LEVEL_ACTIVE)) |
| return 0; |
| |
| if ((!hdd_ctx->is_pktlog_enabled) && |
| (start_log.verbose_level != WLAN_LOG_LEVEL_ACTIVE)) |
| return 0; |
| } |
| |
| mac_handle = hdd_ctx->mac_handle; |
| status = sme_wifi_start_logger(mac_handle, start_log); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_wifi_start_logger failed(err=%d)", |
| status); |
| return -EINVAL; |
| } |
| |
| if (start_log.ring_id == RING_ID_PER_PACKET_STATS) { |
| if (start_log.verbose_level == WLAN_LOG_LEVEL_ACTIVE) |
| hdd_ctx->is_pktlog_enabled = true; |
| else |
| hdd_ctx->is_pktlog_enabled = false; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_wifi_logger_start() - Wrapper function used to enable |
| * or disable the collection of packet statistics from the firmware |
| * @wiphy: WIPHY structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of the data received |
| * |
| * This function is used to enable or disable the collection of packet |
| * statistics from the firmware |
| * |
| * Return: 0 on success and errno on failure |
| */ |
| static int wlan_hdd_cfg80211_wifi_logger_start(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_wifi_logger_start(wiphy, wdev, |
| data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| const struct nla_policy qca_wlan_vendor_wifi_logger_get_ring_data_policy[ |
| QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_ID] |
| = {.type = NLA_U32 }, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_wifi_logger_get_ring_data() - Flush per packet stats |
| * @wiphy: WIPHY structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of the data received |
| * |
| * This function is used to flush or retrieve the per packet statistics from |
| * the driver |
| * |
| * Return: 0 on success and errno on failure |
| */ |
| static int __wlan_hdd_cfg80211_wifi_logger_get_ring_data(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| QDF_STATUS status; |
| uint32_t ring_id; |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb |
| [QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX + 1]; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (status) |
| return status; |
| |
| if (wlan_cfg80211_nla_parse(tb, |
| QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX, |
| data, data_len, |
| qca_wlan_vendor_wifi_logger_get_ring_data_policy)) { |
| hdd_err("Invalid attribute"); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch ring id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_ID]) { |
| hdd_err("attr ATTR failed"); |
| return -EINVAL; |
| } |
| |
| ring_id = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_ID]); |
| |
| if (ring_id == RING_ID_PER_PACKET_STATS) { |
| wlan_logging_set_per_pkt_stats(); |
| hdd_debug("Flushing/Retrieving packet stats"); |
| } else if (ring_id == RING_ID_DRIVER_DEBUG) { |
| /* |
| * As part of DRIVER ring ID, flush both driver and fw logs. |
| * For other Ring ID's driver doesn't have any rings to flush |
| */ |
| hdd_debug("Bug report triggered by framework"); |
| |
| status = cds_flush_logs(WLAN_LOG_TYPE_NON_FATAL, |
| WLAN_LOG_INDICATOR_FRAMEWORK, |
| WLAN_LOG_REASON_CODE_UNUSED, |
| false, false); |
| if (QDF_STATUS_SUCCESS != status) { |
| hdd_err("Failed to trigger bug report"); |
| return -EINVAL; |
| } |
| status = wlan_logging_wait_for_flush_log_completion(); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("wait for flush log timed out"); |
| return qdf_status_to_os_return(status); |
| } |
| } else { |
| wlan_report_log_completion(WLAN_LOG_TYPE_NON_FATAL, |
| WLAN_LOG_INDICATOR_FRAMEWORK, |
| WLAN_LOG_REASON_CODE_UNUSED, |
| ring_id); |
| } |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_wifi_logger_get_ring_data() - Wrapper to flush packet stats |
| * @wiphy: WIPHY structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of the data received |
| * |
| * This function is used to flush or retrieve the per packet statistics from |
| * the driver |
| * |
| * Return: 0 on success and errno on failure |
| */ |
| static int wlan_hdd_cfg80211_wifi_logger_get_ring_data(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_wifi_logger_get_ring_data(wiphy, wdev, |
| data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| #ifdef WLAN_FEATURE_OFFLOAD_PACKETS |
| /** |
| * hdd_map_req_id_to_pattern_id() - map request id to pattern id |
| * @hdd_ctx: HDD context |
| * @request_id: [input] request id |
| * @pattern_id: [output] pattern id |
| * |
| * This function loops through request id to pattern id array |
| * if the slot is available, store the request id and return pattern id |
| * if entry exists, return the pattern id |
| * |
| * Return: 0 on success and errno on failure |
| */ |
| static int hdd_map_req_id_to_pattern_id(struct hdd_context *hdd_ctx, |
| uint32_t request_id, |
| uint8_t *pattern_id) |
| { |
| uint32_t i; |
| |
| mutex_lock(&hdd_ctx->op_ctx.op_lock); |
| for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++) { |
| if (hdd_ctx->op_ctx.op_table[i].request_id == MAX_REQUEST_ID) { |
| hdd_ctx->op_ctx.op_table[i].request_id = request_id; |
| *pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id; |
| mutex_unlock(&hdd_ctx->op_ctx.op_lock); |
| return 0; |
| } else if (hdd_ctx->op_ctx.op_table[i].request_id == |
| request_id) { |
| *pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id; |
| mutex_unlock(&hdd_ctx->op_ctx.op_lock); |
| return 0; |
| } |
| } |
| mutex_unlock(&hdd_ctx->op_ctx.op_lock); |
| return -ENOBUFS; |
| } |
| |
| /** |
| * hdd_unmap_req_id_to_pattern_id() - unmap request id to pattern id |
| * @hdd_ctx: HDD context |
| * @request_id: [input] request id |
| * @pattern_id: [output] pattern id |
| * |
| * This function loops through request id to pattern id array |
| * reset request id to 0 (slot available again) and |
| * return pattern id |
| * |
| * Return: 0 on success and errno on failure |
| */ |
| static int hdd_unmap_req_id_to_pattern_id(struct hdd_context *hdd_ctx, |
| uint32_t request_id, |
| uint8_t *pattern_id) |
| { |
| uint32_t i; |
| |
| mutex_lock(&hdd_ctx->op_ctx.op_lock); |
| for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++) { |
| if (hdd_ctx->op_ctx.op_table[i].request_id == request_id) { |
| hdd_ctx->op_ctx.op_table[i].request_id = MAX_REQUEST_ID; |
| *pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id; |
| mutex_unlock(&hdd_ctx->op_ctx.op_lock); |
| return 0; |
| } |
| } |
| mutex_unlock(&hdd_ctx->op_ctx.op_lock); |
| return -EINVAL; |
| } |
| |
| |
| /* |
| * define short names for the global vendor params |
| * used by __wlan_hdd_cfg80211_offloaded_packets() |
| */ |
| #define PARAM_MAX QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX |
| #define PARAM_REQUEST_ID \ |
| QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID |
| #define PARAM_CONTROL \ |
| QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL |
| #define PARAM_IP_PACKET \ |
| QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA |
| #define PARAM_SRC_MAC_ADDR \ |
| QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR |
| #define PARAM_DST_MAC_ADDR \ |
| QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR |
| #define PARAM_PERIOD QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD |
| #define PARAM_PROTO_TYPE \ |
| QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_ETHER_PROTO_TYPE |
| |
| const struct nla_policy offloaded_packet_policy[ |
| QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA] = { |
| .type = NLA_BINARY}, |
| [QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR] = { |
| .type = NLA_BINARY, |
| .len = QDF_MAC_ADDR_SIZE }, |
| [QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR] = { |
| .type = NLA_BINARY, |
| .len = QDF_MAC_ADDR_SIZE }, |
| [QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_ETHER_PROTO_TYPE] = { |
| .type = NLA_U16}, |
| }; |
| |
| /** |
| * wlan_hdd_add_tx_ptrn() - add tx pattern |
| * @adapter: adapter pointer |
| * @hdd_ctx: hdd context |
| * @tb: nl attributes |
| * |
| * This function reads the NL attributes and forms a AddTxPtrn message |
| * posts it to SME. |
| * |
| */ |
| static int |
| wlan_hdd_add_tx_ptrn(struct hdd_adapter *adapter, struct hdd_context *hdd_ctx, |
| struct nlattr **tb) |
| { |
| struct sSirAddPeriodicTxPtrn *add_req; |
| QDF_STATUS status; |
| uint32_t request_id, len; |
| int32_t ret; |
| uint8_t pattern_id = 0; |
| struct qdf_mac_addr dst_addr; |
| uint16_t eth_type = htons(ETH_P_IP); |
| mac_handle_t mac_handle; |
| |
| if (!hdd_cm_is_vdev_associated(adapter)) { |
| hdd_err("Not in Connected state!"); |
| return -ENOTSUPP; |
| } |
| |
| add_req = qdf_mem_malloc(sizeof(*add_req)); |
| if (!add_req) |
| return -ENOMEM; |
| |
| /* Parse and fetch request Id */ |
| if (!tb[PARAM_REQUEST_ID]) { |
| hdd_err("attr request id failed"); |
| ret = -EINVAL; |
| goto fail; |
| } |
| |
| request_id = nla_get_u32(tb[PARAM_REQUEST_ID]); |
| if (request_id == MAX_REQUEST_ID) { |
| hdd_err("request_id cannot be MAX"); |
| ret = -EINVAL; |
| goto fail; |
| } |
| hdd_debug("Request Id: %u", request_id); |
| |
| if (!tb[PARAM_PERIOD]) { |
| hdd_err("attr period failed"); |
| ret = -EINVAL; |
| goto fail; |
| } |
| |
| add_req->usPtrnIntervalMs = nla_get_u32(tb[PARAM_PERIOD]); |
| hdd_debug("Period: %u ms", add_req->usPtrnIntervalMs); |
| if (add_req->usPtrnIntervalMs == 0) { |
| hdd_err("Invalid interval zero, return failure"); |
| ret = -EINVAL; |
| goto fail; |
| } |
| |
| if (!tb[PARAM_SRC_MAC_ADDR]) { |
| hdd_err("attr source mac address failed"); |
| ret = -EINVAL; |
| goto fail; |
| } |
| nla_memcpy(add_req->mac_address.bytes, tb[PARAM_SRC_MAC_ADDR], |
| QDF_MAC_ADDR_SIZE); |
| hdd_debug("input src mac address: "QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(add_req->mac_address.bytes)); |
| |
| if (!qdf_is_macaddr_equal(&add_req->mac_address, |
| &adapter->mac_addr)) { |
| hdd_err("input src mac address and connected ap bssid are different"); |
| ret = -EINVAL; |
| goto fail; |
| } |
| |
| if (!tb[PARAM_DST_MAC_ADDR]) { |
| hdd_err("attr dst mac address failed"); |
| ret = -EINVAL; |
| goto fail; |
| } |
| nla_memcpy(dst_addr.bytes, tb[PARAM_DST_MAC_ADDR], QDF_MAC_ADDR_SIZE); |
| hdd_debug("input dst mac address: "QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(dst_addr.bytes)); |
| |
| if (!tb[PARAM_IP_PACKET]) { |
| hdd_err("attr ip packet failed"); |
| ret = -EINVAL; |
| goto fail; |
| } |
| add_req->ucPtrnSize = nla_len(tb[PARAM_IP_PACKET]); |
| hdd_debug("IP packet len: %u", add_req->ucPtrnSize); |
| |
| if (add_req->ucPtrnSize < 0 || |
| add_req->ucPtrnSize > (PERIODIC_TX_PTRN_MAX_SIZE - |
| ETH_HLEN)) { |
| hdd_err("Invalid IP packet len: %d", |
| add_req->ucPtrnSize); |
| ret = -EINVAL; |
| goto fail; |
| } |
| |
| if (!tb[PARAM_PROTO_TYPE]) |
| eth_type = htons(ETH_P_IP); |
| else |
| eth_type = htons(nla_get_u16(tb[PARAM_PROTO_TYPE])); |
| |
| hdd_debug("packet proto type: %u", eth_type); |
| |
| len = 0; |
| qdf_mem_copy(&add_req->ucPattern[0], dst_addr.bytes, QDF_MAC_ADDR_SIZE); |
| len += QDF_MAC_ADDR_SIZE; |
| qdf_mem_copy(&add_req->ucPattern[len], add_req->mac_address.bytes, |
| QDF_MAC_ADDR_SIZE); |
| len += QDF_MAC_ADDR_SIZE; |
| qdf_mem_copy(&add_req->ucPattern[len], ð_type, 2); |
| len += 2; |
| |
| /* |
| * This is the IP packet, add 14 bytes Ethernet (802.3) header |
| * ------------------------------------------------------------ |
| * | 14 bytes Ethernet (802.3) header | IP header and payload | |
| * ------------------------------------------------------------ |
| */ |
| qdf_mem_copy(&add_req->ucPattern[len], |
| nla_data(tb[PARAM_IP_PACKET]), |
| add_req->ucPtrnSize); |
| add_req->ucPtrnSize += len; |
| |
| ret = hdd_map_req_id_to_pattern_id(hdd_ctx, request_id, &pattern_id); |
| if (ret) { |
| hdd_err("req id to pattern id failed (ret=%d)", ret); |
| goto fail; |
| } |
| add_req->ucPtrnId = pattern_id; |
| hdd_debug("pattern id: %d", add_req->ucPtrnId); |
| |
| mac_handle = hdd_ctx->mac_handle; |
| status = sme_add_periodic_tx_ptrn(mac_handle, add_req); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_add_periodic_tx_ptrn failed (err=%d)", status); |
| ret = qdf_status_to_os_return(status); |
| goto fail; |
| } |
| |
| hdd_exit(); |
| |
| fail: |
| qdf_mem_free(add_req); |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_del_tx_ptrn() - delete tx pattern |
| * @adapter: adapter pointer |
| * @hdd_ctx: hdd context |
| * @tb: nl attributes |
| * |
| * This function reads the NL attributes and forms a DelTxPtrn message |
| * posts it to SME. |
| * |
| */ |
| static int |
| wlan_hdd_del_tx_ptrn(struct hdd_adapter *adapter, struct hdd_context *hdd_ctx, |
| struct nlattr **tb) |
| { |
| struct sSirDelPeriodicTxPtrn *del_req; |
| QDF_STATUS status; |
| uint32_t request_id, ret; |
| uint8_t pattern_id = 0; |
| mac_handle_t mac_handle; |
| |
| /* Parse and fetch request Id */ |
| if (!tb[PARAM_REQUEST_ID]) { |
| hdd_err("attr request id failed"); |
| return -EINVAL; |
| } |
| request_id = nla_get_u32(tb[PARAM_REQUEST_ID]); |
| if (request_id == MAX_REQUEST_ID) { |
| hdd_err("request_id cannot be MAX"); |
| return -EINVAL; |
| } |
| |
| ret = hdd_unmap_req_id_to_pattern_id(hdd_ctx, request_id, &pattern_id); |
| if (ret) { |
| hdd_err("req id to pattern id failed (ret=%d)", ret); |
| return -EINVAL; |
| } |
| |
| del_req = qdf_mem_malloc(sizeof(*del_req)); |
| if (!del_req) |
| return -ENOMEM; |
| |
| qdf_copy_macaddr(&del_req->mac_address, &adapter->mac_addr); |
| hdd_debug(QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(del_req->mac_address.bytes)); |
| del_req->ucPtrnId = pattern_id; |
| hdd_debug("Request Id: %u Pattern id: %d", |
| request_id, del_req->ucPtrnId); |
| |
| mac_handle = hdd_ctx->mac_handle; |
| status = sme_del_periodic_tx_ptrn(mac_handle, del_req); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_del_periodic_tx_ptrn failed (err=%d)", status); |
| goto fail; |
| } |
| |
| hdd_exit(); |
| qdf_mem_free(del_req); |
| return 0; |
| |
| fail: |
| qdf_mem_free(del_req); |
| return -EINVAL; |
| } |
| |
| |
| /** |
| * __wlan_hdd_cfg80211_offloaded_packets() - send offloaded packets |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_offloaded_packets(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[PARAM_MAX + 1]; |
| uint8_t control; |
| int ret; |
| |
| hdd_enter_dev(dev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| if (!sme_is_feature_supported_by_fw(WLAN_PERIODIC_TX_PTRN)) { |
| hdd_err("Periodic Tx Pattern Offload feature is not supported in FW!"); |
| return -ENOTSUPP; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb, PARAM_MAX, data, data_len, |
| offloaded_packet_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| if (!tb[PARAM_CONTROL]) { |
| hdd_err("attr control failed"); |
| return -EINVAL; |
| } |
| control = nla_get_u32(tb[PARAM_CONTROL]); |
| hdd_debug("Control: %d", control); |
| |
| if (control == WLAN_START_OFFLOADED_PACKETS) |
| return wlan_hdd_add_tx_ptrn(adapter, hdd_ctx, tb); |
| if (control == WLAN_STOP_OFFLOADED_PACKETS) |
| return wlan_hdd_del_tx_ptrn(adapter, hdd_ctx, tb); |
| |
| hdd_err("Invalid control: %d", control); |
| return -EINVAL; |
| } |
| |
| /* |
| * done with short names for the global vendor params |
| * used by __wlan_hdd_cfg80211_offloaded_packets() |
| */ |
| #undef PARAM_MAX |
| #undef PARAM_REQUEST_ID |
| #undef PARAM_CONTROL |
| #undef PARAM_IP_PACKET |
| #undef PARAM_SRC_MAC_ADDR |
| #undef PARAM_DST_MAC_ADDR |
| #undef PARAM_PERIOD |
| #undef PARAM_PROTO_TYPE |
| |
| /** |
| * wlan_hdd_cfg80211_offloaded_packets() - Wrapper to offload packets |
| * @wiphy: wiphy structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of @data |
| * |
| * Return: 0 on success; errno on failure |
| */ |
| static int wlan_hdd_cfg80211_offloaded_packets(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_offloaded_packets(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| |
| #ifdef WLAN_NS_OFFLOAD |
| const struct nla_policy ns_offload_set_policy[ |
| QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG] = {.type = NLA_U8}, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_set_ns_offload() - enable/disable NS offload |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Length of @data |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_set_ns_offload(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int status; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX + 1]; |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| |
| hdd_enter_dev(wdev->netdev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return status; |
| |
| if (!ucfg_pmo_is_ns_offloaded(hdd_ctx->psoc)) { |
| hdd_err("ND Offload not supported"); |
| return -EINVAL; |
| } |
| |
| if (!ucfg_pmo_is_active_mode_offloaded(hdd_ctx->psoc)) { |
| hdd_warn("Active mode offload is disabled"); |
| return -EINVAL; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX, |
| (struct nlattr *)data, data_len, |
| ns_offload_set_policy)) { |
| hdd_err("nla_parse failed"); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG]) { |
| hdd_err("ND Offload flag attribute not present"); |
| return -EINVAL; |
| } |
| |
| hdd_ctx->ns_offload_enable = |
| nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG]); |
| |
| /* update ns offload in case it is already enabled/disabled */ |
| if (hdd_ctx->ns_offload_enable) |
| hdd_enable_ns_offload(adapter, pmo_ns_offload_dynamic_update); |
| else |
| hdd_disable_ns_offload(adapter, pmo_ns_offload_dynamic_update); |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_ns_offload() - enable/disable NS offload |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: Pointer to the data to be passed via vendor interface |
| * @data_len:Length of the data to be passed |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int wlan_hdd_cfg80211_set_ns_offload(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_ns_offload(wiphy, wdev, data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif /* WLAN_NS_OFFLOAD */ |
| |
| /** |
| * struct weighed_pcl: Preferred channel info |
| * @freq: Channel frequency |
| * @weight: Weightage of the channel |
| * @flag: Validity of the channel in p2p negotiation |
| */ |
| struct weighed_pcl { |
| u32 freq; |
| u32 weight; |
| u32 flag; |
| }; |
| |
| const struct nla_policy get_preferred_freq_list_policy[ |
| QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE] = { |
| .type = NLA_U32}, |
| }; |
| |
| static uint32_t wlan_hdd_populate_weigh_pcl( |
| struct wlan_objmgr_psoc *psoc, |
| struct policy_mgr_pcl_chan_weights * |
| chan_weights, |
| struct weighed_pcl *w_pcl, |
| enum policy_mgr_con_mode intf_mode) |
| { |
| u32 i, j, valid_weight; |
| u32 chan_idx = 0; |
| u32 pcl_len = chan_weights->pcl_len; |
| u32 conn_count = policy_mgr_get_connection_count(psoc); |
| |
| /* convert channel number to frequency */ |
| for (i = 0; i < chan_weights->pcl_len; i++) { |
| w_pcl[i].freq = chan_weights->pcl_list[i]; |
| w_pcl[i].weight = chan_weights->weight_list[i]; |
| |
| if (intf_mode == PM_SAP_MODE || intf_mode == PM_P2P_GO_MODE) |
| w_pcl[i].flag = PCL_CHANNEL_SUPPORT_GO; |
| else |
| w_pcl[i].flag = PCL_CHANNEL_SUPPORT_CLI; |
| } |
| chan_idx = pcl_len; |
| if (!conn_count || policy_mgr_is_hw_dbs_capable(psoc) || |
| policy_mgr_is_interband_mcc_supported(psoc)) { |
| if (pcl_len && chan_weights->weight_list[pcl_len - 1] > |
| PCL_GROUPS_WEIGHT_DIFFERENCE) |
| /* |
| * Set non-pcl channels weight 20 point less than the |
| * last PCL entry |
| */ |
| valid_weight = chan_weights->weight_list[pcl_len - 1] - |
| PCL_GROUPS_WEIGHT_DIFFERENCE; |
| else |
| valid_weight = 1; |
| |
| /* Include rest of the valid channels */ |
| for (i = 0; i < chan_weights->saved_num_chan; i++) { |
| for (j = 0; j < chan_weights->pcl_len; j++) { |
| if (chan_weights->saved_chan_list[i] == |
| chan_weights->pcl_list[j]) |
| break; |
| } |
| if (j == chan_weights->pcl_len) { |
| w_pcl[chan_idx].freq = |
| chan_weights->saved_chan_list[i]; |
| |
| if (!chan_weights->weighed_valid_list[i]) { |
| w_pcl[chan_idx].flag = |
| PCL_CHANNEL_EXCLUDE_IN_GO_NEG; |
| w_pcl[chan_idx].weight = 0; |
| } else { |
| if (intf_mode == PM_SAP_MODE || |
| intf_mode == PM_P2P_GO_MODE) |
| w_pcl[chan_idx].flag = |
| PCL_CHANNEL_SUPPORT_GO; |
| else |
| w_pcl[chan_idx].flag = |
| PCL_CHANNEL_SUPPORT_CLI; |
| w_pcl[chan_idx].weight = valid_weight; |
| } |
| chan_idx++; |
| } |
| } |
| } |
| return chan_idx; |
| } |
| |
| /** __wlan_hdd_cfg80211_get_preferred_freq_list() - get preferred frequency list |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * This function return the preferred frequency list generated by the policy |
| * manager. |
| * |
| * Return: success or failure code |
| */ |
| static int __wlan_hdd_cfg80211_get_preferred_freq_list(struct wiphy *wiphy, |
| struct wireless_dev |
| *wdev, const void *data, |
| int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| int i, ret = 0; |
| QDF_STATUS status; |
| uint32_t pcl_len = 0; |
| uint32_t pcl_len_legacy = 0; |
| uint32_t freq_list[NUM_CHANNELS]; |
| uint32_t freq_list_legacy[NUM_CHANNELS]; |
| enum policy_mgr_con_mode intf_mode; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX + 1]; |
| struct sk_buff *reply_skb; |
| struct weighed_pcl *w_pcl; |
| struct nlattr *nla_attr, *channel; |
| struct policy_mgr_pcl_chan_weights *chan_weights; |
| struct net_device *ndev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); |
| |
| hdd_enter_dev(wdev->netdev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return -EINVAL; |
| |
| if (wlan_cfg80211_nla_parse(tb, |
| QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX, |
| data, data_len, |
| get_preferred_freq_list_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE]) { |
| hdd_err("attr interface type failed"); |
| return -EINVAL; |
| } |
| |
| intf_mode = nla_get_u32(tb |
| [QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE]); |
| |
| if (intf_mode < PM_STA_MODE || intf_mode >= PM_MAX_NUM_OF_MODE) { |
| hdd_err("Invalid interface type"); |
| return -EINVAL; |
| } |
| |
| hdd_debug("Userspace requested pref freq list"); |
| |
| chan_weights = |
| qdf_mem_malloc(sizeof(struct policy_mgr_pcl_chan_weights)); |
| if (!chan_weights) |
| return -ENOMEM; |
| |
| status = policy_mgr_get_pcl( |
| hdd_ctx->psoc, intf_mode, chan_weights->pcl_list, |
| &chan_weights->pcl_len, chan_weights->weight_list, |
| QDF_ARRAY_SIZE(chan_weights->weight_list)); |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_err("Get pcl failed"); |
| qdf_mem_free(chan_weights); |
| return -EINVAL; |
| } |
| /* |
| * save the pcl in freq_list_legacy to be sent up with |
| * QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST. |
| * freq_list will carry the extended pcl in |
| * QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL. |
| */ |
| pcl_len_legacy = chan_weights->pcl_len; |
| for (i = 0; i < pcl_len_legacy; i++) |
| freq_list_legacy[i] = chan_weights->pcl_list[i]; |
| chan_weights->saved_num_chan = NUM_CHANNELS; |
| sme_get_valid_channels(chan_weights->saved_chan_list, |
| &chan_weights->saved_num_chan); |
| policy_mgr_get_valid_chan_weights(hdd_ctx->psoc, chan_weights, |
| intf_mode, |
| adapter->vdev); |
| w_pcl = qdf_mem_malloc(sizeof(struct weighed_pcl) * NUM_CHANNELS); |
| if (!w_pcl) { |
| qdf_mem_free(chan_weights); |
| return -ENOMEM; |
| } |
| pcl_len = wlan_hdd_populate_weigh_pcl(hdd_ctx->psoc, chan_weights, |
| w_pcl, intf_mode); |
| qdf_mem_free(chan_weights); |
| |
| for (i = 0; i < pcl_len; i++) |
| freq_list[i] = w_pcl[i].freq; |
| |
| /* send the freq_list back to supplicant */ |
| reply_skb = cfg80211_vendor_cmd_alloc_reply_skb( |
| wiphy, |
| (sizeof(u32) + NLA_HDRLEN) + |
| (sizeof(u32) * pcl_len_legacy + NLA_HDRLEN) + |
| NLA_HDRLEN + |
| (NLA_HDRLEN * 4 + sizeof(u32) * 3) * pcl_len + |
| NLMSG_HDRLEN); |
| |
| if (!reply_skb) { |
| hdd_err("Allocate reply_skb failed"); |
| qdf_mem_free(w_pcl); |
| return -EINVAL; |
| } |
| |
| if (nla_put_u32(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE, |
| intf_mode) || |
| nla_put(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST, |
| sizeof(uint32_t) * pcl_len_legacy, |
| freq_list_legacy)) { |
| hdd_err("nla put fail"); |
| kfree_skb(reply_skb); |
| qdf_mem_free(w_pcl); |
| return -EINVAL; |
| } |
| |
| i = QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL; |
| nla_attr = nla_nest_start(reply_skb, i); |
| |
| if (!nla_attr) { |
| hdd_err("nla nest start fail"); |
| kfree_skb(reply_skb); |
| qdf_mem_free(w_pcl); |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < pcl_len; i++) { |
| channel = nla_nest_start(reply_skb, i); |
| if (!channel) { |
| hdd_err("updating pcl list failed"); |
| kfree_skb(reply_skb); |
| qdf_mem_free(w_pcl); |
| return -EINVAL; |
| } |
| if (nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_PCL_FREQ, |
| w_pcl[i].freq) || |
| nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT, |
| w_pcl[i].weight) || |
| nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_PCL_FLAG, |
| w_pcl[i].flag)) { |
| hdd_err("nla put fail"); |
| kfree_skb(reply_skb); |
| qdf_mem_free(w_pcl); |
| return -EINVAL; |
| } |
| nla_nest_end(reply_skb, channel); |
| } |
| nla_nest_end(reply_skb, nla_attr); |
| qdf_mem_free(w_pcl); |
| |
| return cfg80211_vendor_cmd_reply(reply_skb); |
| } |
| |
| /** wlan_hdd_cfg80211_get_preferred_freq_list () - get preferred frequency list |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * This function return the preferred frequency list generated by the policy |
| * manager. |
| * |
| * Return: success or failure code |
| */ |
| static int wlan_hdd_cfg80211_get_preferred_freq_list(struct wiphy *wiphy, |
| struct wireless_dev |
| *wdev, const void *data, |
| int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_preferred_freq_list(wiphy, wdev, |
| data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| const struct nla_policy set_probable_oper_channel_policy[ |
| QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ] = { |
| .type = NLA_U32}, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_set_probable_oper_channel () - set probable channel |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int __wlan_hdd_cfg80211_set_probable_oper_channel(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct net_device *ndev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| int ret = 0; |
| enum policy_mgr_con_mode intf_mode; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX + 1]; |
| uint32_t ch_freq, conc_ext_flags; |
| |
| hdd_enter_dev(ndev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| if (wlan_cfg80211_nla_parse(tb, |
| QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX, |
| data, data_len, |
| set_probable_oper_channel_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE]) { |
| hdd_err("attr interface type failed"); |
| return -EINVAL; |
| } |
| |
| intf_mode = nla_get_u32(tb |
| [QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE]); |
| |
| if (intf_mode < PM_STA_MODE || intf_mode >= PM_MAX_NUM_OF_MODE) { |
| hdd_err("Invalid interface type"); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ]) { |
| hdd_err("attr probable freq failed"); |
| return -EINVAL; |
| } |
| |
| ch_freq = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ]); |
| conc_ext_flags = policy_mgr_get_conc_ext_flags(adapter->vdev, false); |
| |
| /* check pcl table */ |
| if (!policy_mgr_allow_concurrency(hdd_ctx->psoc, intf_mode, |
| ch_freq, HW_MODE_20_MHZ, |
| conc_ext_flags)) { |
| hdd_err("Set channel hint failed due to concurrency check"); |
| return -EINVAL; |
| } |
| |
| if (0 != wlan_hdd_check_remain_on_channel(adapter)) |
| hdd_warn("Remain On Channel Pending"); |
| |
| if (wlan_hdd_change_hw_mode_for_given_chnl(adapter, ch_freq, |
| POLICY_MGR_UPDATE_REASON_SET_OPER_CHAN)) { |
| hdd_err("Failed to change hw mode"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_probable_oper_channel () - set probable channel |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int wlan_hdd_cfg80211_set_probable_oper_channel(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_probable_oper_channel(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| static const struct |
| nla_policy |
| wlan_hdd_get_link_properties_policy[QCA_WLAN_VENDOR_ATTR_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_MAC_ADDR] = { |
| .type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE }, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_get_link_properties() - Get link properties |
| * @wiphy: WIPHY structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of the data received |
| * |
| * This function is used to get link properties like nss, rate flags and |
| * operating frequency for the active connection with the given peer. |
| * |
| * Return: 0 on success and errno on failure |
| */ |
| static int __wlan_hdd_cfg80211_get_link_properties(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_station_ctx *hdd_sta_ctx; |
| struct hdd_station_info *sta_info; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX+1]; |
| uint8_t peer_mac[QDF_MAC_ADDR_SIZE]; |
| struct sk_buff *reply_skb; |
| uint32_t rate_flags = 0; |
| uint8_t nss; |
| uint8_t final_rate_flags = 0; |
| uint32_t freq; |
| |
| hdd_enter_dev(dev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| if (0 != wlan_hdd_validate_context(hdd_ctx)) |
| return -EINVAL; |
| |
| if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, |
| data_len, |
| wlan_hdd_get_link_properties_policy)) { |
| hdd_err("Invalid attribute"); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) { |
| hdd_err("Attribute peerMac not provided for mode=%d", |
| adapter->device_mode); |
| return -EINVAL; |
| } |
| |
| if (nla_len(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) < QDF_MAC_ADDR_SIZE) { |
| hdd_err("Attribute peerMac is invalid for mode=%d", |
| adapter->device_mode); |
| return -EINVAL; |
| } |
| |
| qdf_mem_copy(peer_mac, nla_data(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]), |
| QDF_MAC_ADDR_SIZE); |
| hdd_debug("peerMac="QDF_MAC_ADDR_FMT" for device_mode:%d", |
| QDF_MAC_ADDR_REF(peer_mac), adapter->device_mode); |
| |
| if (adapter->device_mode == QDF_STA_MODE || |
| adapter->device_mode == QDF_P2P_CLIENT_MODE) { |
| hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| if (!hdd_cm_is_vdev_associated(adapter) || |
| qdf_mem_cmp(hdd_sta_ctx->conn_info.bssid.bytes, |
| peer_mac, QDF_MAC_ADDR_SIZE)) { |
| hdd_err("Not Associated to mac "QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(peer_mac)); |
| return -EINVAL; |
| } |
| |
| nss = hdd_sta_ctx->conn_info.nss; |
| freq = hdd_sta_ctx->conn_info.chan_freq; |
| rate_flags = hdd_sta_ctx->conn_info.rate_flags; |
| } else if (adapter->device_mode == QDF_P2P_GO_MODE || |
| adapter->device_mode == QDF_SAP_MODE) { |
| |
| if (QDF_IS_ADDR_BROADCAST(peer_mac)) { |
| hdd_err("Ignore bcast/self sta"); |
| return -EINVAL; |
| } |
| |
| sta_info = hdd_get_sta_info_by_mac( |
| &adapter->sta_info_list, peer_mac, |
| STA_INFO_CFG80211_GET_LINK_PROPERTIES); |
| |
| if (!sta_info) { |
| hdd_err("No active peer with mac = " QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(peer_mac)); |
| return -EINVAL; |
| } |
| |
| nss = sta_info->nss; |
| freq = (WLAN_HDD_GET_AP_CTX_PTR(adapter))->operating_chan_freq; |
| rate_flags = sta_info->rate_flags; |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, |
| STA_INFO_CFG80211_GET_LINK_PROPERTIES); |
| } else { |
| hdd_err("Not Associated! with mac "QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(peer_mac)); |
| return -EINVAL; |
| } |
| |
| if (!(rate_flags & TX_RATE_LEGACY)) { |
| if (rate_flags & TX_RATE_VHT80) { |
| final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS) |
| final_rate_flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; |
| #endif |
| } else if (rate_flags & TX_RATE_VHT40) { |
| final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS) |
| final_rate_flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; |
| #endif |
| } else if (rate_flags & TX_RATE_VHT20) { |
| final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS; |
| } else if (rate_flags & |
| (TX_RATE_HT20 | TX_RATE_HT40)) { |
| final_rate_flags |= RATE_INFO_FLAGS_MCS; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS) |
| if (rate_flags & TX_RATE_HT40) |
| final_rate_flags |= |
| RATE_INFO_FLAGS_40_MHZ_WIDTH; |
| #endif |
| } |
| |
| if (rate_flags & TX_RATE_SGI) { |
| if (!(final_rate_flags & RATE_INFO_FLAGS_VHT_MCS)) |
| final_rate_flags |= RATE_INFO_FLAGS_MCS; |
| final_rate_flags |= RATE_INFO_FLAGS_SHORT_GI; |
| } |
| } |
| |
| reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, |
| sizeof(u8) + sizeof(u8) + sizeof(u32) + NLMSG_HDRLEN); |
| |
| if (!reply_skb) { |
| hdd_err("getLinkProperties: skb alloc failed"); |
| return -EINVAL; |
| } |
| |
| if (nla_put_u8(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_NSS, |
| nss) || |
| nla_put_u8(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_RATE_FLAGS, |
| final_rate_flags) || |
| nla_put_u32(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_FREQ, |
| freq)) { |
| hdd_err("nla_put failed"); |
| kfree_skb(reply_skb); |
| return -EINVAL; |
| } |
| |
| return cfg80211_vendor_cmd_reply(reply_skb); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_get_link_properties() - Wrapper function to get link |
| * properties. |
| * @wiphy: WIPHY structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of the data received |
| * |
| * This function is used to get link properties like nss, rate flags and |
| * operating frequency for the active connection with the given peer. |
| * |
| * Return: 0 on success and errno on failure |
| */ |
| static int wlan_hdd_cfg80211_get_link_properties(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_link_properties(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| const struct nla_policy |
| wlan_hdd_sap_config_policy[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL] = {.type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST] = { |
| .type = NLA_BINARY}, |
| }; |
| |
| const struct nla_policy |
| wlan_hdd_set_acs_dfs_config_policy[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE] = {.type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT] = {.type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT] = {.type = NLA_U32}, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_acs_dfs_mode() - set ACS DFS mode and channel |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Length of @data |
| * |
| * This function parses the incoming NL vendor command data attributes and |
| * updates the SAP context about channel_hint and DFS mode. |
| * If channel_hint is set, SAP will choose that channel |
| * as operating channel. |
| * |
| * If DFS mode is enabled, driver will include DFS channels |
| * in ACS else driver will skip DFS channels. |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_acs_dfs_mode(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX + 1]; |
| int ret; |
| struct acs_dfs_policy *acs_policy; |
| int mode = DFS_MODE_NONE; |
| uint32_t freq_hint = 0; |
| |
| hdd_enter_dev(wdev->netdev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX, |
| data, data_len, |
| wlan_hdd_set_acs_dfs_config_policy)) { |
| hdd_err("invalid attr"); |
| return -EINVAL; |
| } |
| |
| acs_policy = &hdd_ctx->acs_policy; |
| /* |
| * SCM sends this attribute to restrict SAP from choosing |
| * DFS channels from ACS. |
| */ |
| if (tb[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE]) |
| mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE]); |
| |
| if (!IS_DFS_MODE_VALID(mode)) { |
| hdd_err("attr acs dfs mode is not valid"); |
| return -EINVAL; |
| } |
| acs_policy->acs_dfs_mode = mode; |
| |
| /* |
| * SCM sends this attribute to provide an active channel, |
| * to skip redundant ACS between drivers, and save driver start up time |
| */ |
| if (tb[QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT]) { |
| freq_hint = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT]); |
| } else if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT]) { |
| uint32_t channel_hint = nla_get_u8( |
| tb[QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT]); |
| |
| freq_hint = wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, |
| channel_hint); |
| } |
| |
| if (freq_hint && !WLAN_REG_IS_24GHZ_CH_FREQ(freq_hint) && |
| !WLAN_REG_IS_5GHZ_CH_FREQ(freq_hint) && |
| !WLAN_REG_IS_6GHZ_CHAN_FREQ(freq_hint)) { |
| hdd_err("acs channel frequency is not valid"); |
| return -EINVAL; |
| } |
| |
| acs_policy->acs_chan_freq = freq_hint; |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_acs_dfs_mode() - Wrapper to set ACS DFS mode |
| * @wiphy: wiphy structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of @data |
| * |
| * This function parses the incoming NL vendor command data attributes and |
| * updates the SAP context about channel_hint and DFS mode. |
| * |
| * Return: 0 on success; errno on failure |
| */ |
| static int wlan_hdd_cfg80211_acs_dfs_mode(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_acs_dfs_mode(wiphy, wdev, data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * wlan_hdd_get_sta_roam_dfs_mode() - get sta roam dfs mode policy |
| * @mode : cfg80211 dfs mode |
| * |
| * Return: return csr sta roam dfs mode else return NONE |
| */ |
| static enum sta_roam_policy_dfs_mode wlan_hdd_get_sta_roam_dfs_mode( |
| enum dfs_mode mode) |
| { |
| switch (mode) { |
| case DFS_MODE_ENABLE: |
| return STA_ROAM_POLICY_DFS_ENABLED; |
| case DFS_MODE_DISABLE: |
| return STA_ROAM_POLICY_DFS_DISABLED; |
| case DFS_MODE_DEPRIORITIZE: |
| return STA_ROAM_POLICY_DFS_DEPRIORITIZE; |
| default: |
| hdd_err("STA Roam policy dfs mode is NONE"); |
| return STA_ROAM_POLICY_NONE; |
| } |
| } |
| |
| /* |
| * hdd_get_sap_operating_band_by_adapter: Get current adapter operating band |
| * for sap. |
| * @adapter: Pointer to adapter |
| * |
| * Return : Corresponding band for SAP operating channel |
| */ |
| uint8_t hdd_get_sap_operating_band_by_adapter(struct hdd_adapter *adapter) |
| { |
| uint32_t operating_chan_freq; |
| uint8_t sap_operating_band = 0; |
| |
| if (adapter->device_mode != QDF_SAP_MODE && |
| adapter->device_mode != QDF_P2P_GO_MODE) |
| return BAND_UNKNOWN; |
| |
| operating_chan_freq = adapter->session.ap.operating_chan_freq; |
| if (WLAN_REG_IS_24GHZ_CH_FREQ(operating_chan_freq)) |
| sap_operating_band = BAND_2G; |
| else if (WLAN_REG_IS_5GHZ_CH_FREQ(operating_chan_freq) || |
| WLAN_REG_IS_6GHZ_CHAN_FREQ(operating_chan_freq)) |
| sap_operating_band = BAND_5G; |
| else |
| sap_operating_band = BAND_UNKNOWN; |
| |
| return sap_operating_band; |
| } |
| |
| /* |
| * hdd_get_sap_operating_band: Get current operating channel |
| * for sap. |
| * @hdd_ctx: hdd context |
| * |
| * Return : Corresponding band for SAP operating channel |
| */ |
| uint8_t hdd_get_sap_operating_band(struct hdd_context *hdd_ctx) |
| { |
| struct hdd_adapter *adapter, *next_adapter = NULL; |
| uint8_t operating_band = 0; |
| wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_GET_SAP_OPERATING_BAND; |
| |
| hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, |
| dbgid) { |
| if (adapter->device_mode != QDF_SAP_MODE) { |
| hdd_adapter_dev_put_debug(adapter, dbgid); |
| continue; |
| } |
| |
| operating_band = hdd_get_sap_operating_band_by_adapter(adapter); |
| |
| hdd_adapter_dev_put_debug(adapter, dbgid); |
| } |
| |
| return operating_band; |
| } |
| |
| const struct nla_policy |
| wlan_hdd_set_sta_roam_config_policy[ |
| QCA_WLAN_VENDOR_ATTR_STA_CONNECT_ROAM_POLICY_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_STA_DFS_MODE] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_STA_SKIP_UNSAFE_CHANNEL] = {.type = NLA_U8 }, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_sta_roam_policy() - Set params to restrict scan channels |
| * for station connection or roaming. |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Length of @data |
| * |
| * __wlan_hdd_cfg80211_sta_roam_policy will decide if DFS channels or unsafe |
| * channels needs to be skipped in scanning or not. |
| * If dfs_mode is disabled, driver will not scan DFS channels. |
| * If skip_unsafe_channels is set, driver will skip unsafe channels |
| * in Scanning. |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_sta_roam_policy(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[ |
| QCA_WLAN_VENDOR_ATTR_STA_CONNECT_ROAM_POLICY_MAX + 1]; |
| int ret; |
| enum sta_roam_policy_dfs_mode sta_roam_dfs_mode; |
| enum dfs_mode mode = DFS_MODE_NONE; |
| bool skip_unsafe_channels = false; |
| QDF_STATUS status; |
| uint8_t sap_operating_band; |
| mac_handle_t mac_handle; |
| |
| hdd_enter_dev(dev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| if (wlan_cfg80211_nla_parse(tb, |
| QCA_WLAN_VENDOR_ATTR_STA_CONNECT_ROAM_POLICY_MAX, |
| data, data_len, |
| wlan_hdd_set_sta_roam_config_policy)) { |
| hdd_err("invalid attr"); |
| return -EINVAL; |
| } |
| if (tb[QCA_WLAN_VENDOR_ATTR_STA_DFS_MODE]) |
| mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_STA_DFS_MODE]); |
| if (!IS_DFS_MODE_VALID(mode)) { |
| hdd_err("attr sta roam dfs mode policy is not valid"); |
| return -EINVAL; |
| } |
| |
| sta_roam_dfs_mode = wlan_hdd_get_sta_roam_dfs_mode(mode); |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_STA_SKIP_UNSAFE_CHANNEL]) |
| skip_unsafe_channels = nla_get_u8( |
| tb[QCA_WLAN_VENDOR_ATTR_STA_SKIP_UNSAFE_CHANNEL]); |
| sap_operating_band = hdd_get_sap_operating_band(hdd_ctx); |
| mac_handle = hdd_ctx->mac_handle; |
| status = sme_update_sta_roam_policy(mac_handle, sta_roam_dfs_mode, |
| skip_unsafe_channels, |
| adapter->vdev_id, |
| sap_operating_band); |
| |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_update_sta_roam_policy (err=%d)", status); |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_sta_roam_policy() - Wrapper to restrict scan channels, |
| * connection and roaming for station. |
| * @wiphy: wiphy structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of @data |
| * |
| * __wlan_hdd_cfg80211_sta_roam_policy will decide if DFS channels or unsafe |
| * channels needs to be skipped in scanning or not. |
| * If dfs_mode is disabled, driver will not scan DFS channels. |
| * If skip_unsafe_channels is set, driver will skip unsafe channels |
| * in Scanning. |
| * Return: 0 on success; errno on failure |
| */ |
| static int |
| wlan_hdd_cfg80211_sta_roam_policy(struct wiphy *wiphy, |
| struct wireless_dev *wdev, const void *data, |
| int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_sta_roam_policy(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| const struct nla_policy |
| wlan_hdd_set_dual_sta_policy[ |
| QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_CONFIG] = {.type = NLA_U8 }, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_dual_sta_policy() - Wrapper to configure the concurrent |
| * session policies |
| * @wiphy: wiphy structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of @data |
| * |
| * Configure the concurrent session policies when multiple STA ifaces are |
| * (getting) active. |
| * Return: 0 on success; errno on failure |
| */ |
| static int __wlan_hdd_cfg80211_dual_sta_policy(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[ |
| QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_MAX + 1]; |
| QDF_STATUS status; |
| uint8_t dual_sta_config = |
| QCA_WLAN_CONCURRENT_STA_POLICY_UNBIASED; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) { |
| hdd_err("Invalid hdd context"); |
| return -EINVAL; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb, |
| QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_MAX, |
| data, data_len, |
| wlan_hdd_set_dual_sta_policy)) { |
| hdd_err("nla_parse failed"); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_CONFIG]) { |
| hdd_err("sta policy config attribute not present"); |
| return -EINVAL; |
| } |
| |
| dual_sta_config = nla_get_u8( |
| tb[QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_CONFIG]); |
| hdd_debug("Concurrent STA policy : %d", dual_sta_config); |
| |
| if (dual_sta_config > QCA_WLAN_CONCURRENT_STA_POLICY_UNBIASED) |
| return -EINVAL; |
| |
| status = ucfg_mlme_set_dual_sta_policy(hdd_ctx->psoc, dual_sta_config); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("failed to set MLME dual sta config"); |
| return -EINVAL; |
| } |
| |
| /* After SSR, the dual sta configuration is lost. As SSR is hidden from |
| * userland, this command will not come from userspace after a SSR. To |
| * restore this configuration, save this in hdd context and restore |
| * after re-init. |
| */ |
| hdd_ctx->dual_sta_policy.dual_sta_policy = dual_sta_config; |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_dual_sta_policy() - Wrapper to configure the concurrent |
| * session policies |
| * @wiphy: wiphy structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of @data |
| * |
| * Configure the concurrent session policies when multiple STA ifaces are |
| * (getting) active. |
| * Return: 0 on success; errno on failure |
| */ |
| static int wlan_hdd_cfg80211_dual_sta_policy(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_dual_sta_policy(wiphy, wdev, data, |
| data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #ifdef FEATURE_WLAN_CH_AVOID |
| /** |
| * __wlan_hdd_cfg80211_avoid_freq() - ask driver to restart SAP if SAP |
| * is on unsafe channel. |
| * @wiphy: wiphy structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of @data |
| * |
| * wlan_hdd_cfg80211_avoid_freq do restart the sap if sap is already |
| * on any of unsafe channels. |
| * If sap is on any of unsafe channel, hdd_unsafe_channel_restart_sap |
| * will send WLAN_SVC_LTE_COEX_IND indication to userspace to restart. |
| * |
| * Return: 0 on success; errno on failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_avoid_freq(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| int ret; |
| qdf_device_t qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); |
| struct ch_avoid_ind_type *channel_list; |
| struct ch_avoid_ind_type avoid_freq_list; |
| enum QDF_GLOBAL_MODE curr_mode; |
| uint8_t num_args = 0; |
| |
| hdd_enter_dev(wdev->netdev); |
| |
| if (!qdf_ctx) |
| return -EINVAL; |
| |
| curr_mode = hdd_get_conparam(); |
| if (QDF_GLOBAL_FTM_MODE == curr_mode || |
| QDF_GLOBAL_MONITOR_MODE == curr_mode) { |
| hdd_err("Command not allowed in FTM/MONITOR mode"); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| qdf_mem_zero(&avoid_freq_list, sizeof(struct ch_avoid_ind_type)); |
| |
| if (!data && data_len == 0) { |
| hdd_debug("Clear avoid frequency list"); |
| goto process_unsafe_channel; |
| } |
| if (!data || data_len < (sizeof(channel_list->ch_avoid_range_cnt) + |
| sizeof(struct ch_avoid_freq_type))) { |
| hdd_err("Avoid frequency channel list empty"); |
| return -EINVAL; |
| } |
| num_args = (data_len - sizeof(channel_list->ch_avoid_range_cnt)) / |
| sizeof(channel_list->avoid_freq_range[0].start_freq); |
| |
| |
| if (num_args < 2 || num_args > CH_AVOID_MAX_RANGE * 2 || |
| num_args % 2 != 0) { |
| hdd_err("Invalid avoid frequency channel list"); |
| return -EINVAL; |
| } |
| |
| channel_list = (struct ch_avoid_ind_type *)data; |
| if (channel_list->ch_avoid_range_cnt == 0 || |
| channel_list->ch_avoid_range_cnt > CH_AVOID_MAX_RANGE || |
| 2 * channel_list->ch_avoid_range_cnt != num_args) { |
| hdd_err("Invalid frequency range count %d", |
| channel_list->ch_avoid_range_cnt); |
| return -EINVAL; |
| } |
| |
| qdf_mem_copy(&avoid_freq_list, channel_list, data_len); |
| |
| process_unsafe_channel: |
| ucfg_reg_ch_avoid(hdd_ctx->psoc, &avoid_freq_list); |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_avoid_freq() - ask driver to restart SAP if SAP |
| * is on unsafe channel. |
| * @wiphy: wiphy structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of @data |
| * |
| * wlan_hdd_cfg80211_avoid_freq do restart the sap if sap is already |
| * on any of unsafe channels. |
| * If sap is on any of unsafe channel, hdd_unsafe_channel_restart_sap |
| * will send WLAN_SVC_LTE_COEX_IND indication to userspace to restart. |
| * |
| * Return: 0 on success; errno on failure |
| */ |
| static int wlan_hdd_cfg80211_avoid_freq(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_avoid_freq(wiphy, wdev, data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| #endif |
| /** |
| * __wlan_hdd_cfg80211_sap_configuration_set() - ask driver to restart SAP if |
| * SAP is on unsafe channel. |
| * @wiphy: wiphy structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of @data |
| * |
| * __wlan_hdd_cfg80211_sap_configuration_set function set SAP params to |
| * driver. |
| * QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHAN will set sap config channel and |
| * will initiate restart of sap. |
| * |
| * Return: 0 on success; errno on failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_sap_configuration_set(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct net_device *ndev = wdev->netdev; |
| struct hdd_adapter *hostapd_adapter = WLAN_HDD_GET_PRIV_PTR(ndev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX + 1]; |
| struct hdd_ap_ctx *ap_ctx; |
| int ret; |
| uint32_t chan_freq = 0; |
| bool chan_freq_present = false; |
| QDF_STATUS status; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return -EINVAL; |
| |
| if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX, |
| data, data_len, |
| wlan_hdd_sap_config_policy)) { |
| hdd_err("invalid attr"); |
| return -EINVAL; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY]) { |
| chan_freq = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY]); |
| chan_freq_present = true; |
| } else if (tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL]) { |
| uint32_t config_channel = |
| nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL]); |
| |
| chan_freq = wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, |
| config_channel); |
| chan_freq_present = true; |
| } |
| |
| if (chan_freq_present) { |
| if (!test_bit(SOFTAP_BSS_STARTED, |
| &hostapd_adapter->event_flags)) { |
| hdd_err("SAP is not started yet. Restart sap will be invalid"); |
| return -EINVAL; |
| } |
| |
| if (!WLAN_REG_IS_24GHZ_CH_FREQ(chan_freq) && |
| !WLAN_REG_IS_5GHZ_CH_FREQ(chan_freq) && |
| !WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_freq)) { |
| hdd_err("Channel frequency %u is invalid to restart SAP", |
| chan_freq); |
| return -ENOTSUPP; |
| } |
| |
| ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(hostapd_adapter); |
| ap_ctx->sap_config.chan_freq = chan_freq; |
| ap_ctx->sap_config.ch_params.ch_width = |
| ap_ctx->sap_config.ch_width_orig; |
| ap_ctx->bss_stop_reason = BSS_STOP_DUE_TO_VENDOR_CONFIG_CHAN; |
| |
| wlan_reg_set_channel_params_for_freq( |
| hdd_ctx->pdev, chan_freq, |
| ap_ctx->sap_config.sec_ch_freq, |
| &ap_ctx->sap_config.ch_params); |
| |
| hdd_restart_sap(hostapd_adapter); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST]) { |
| uint32_t freq_len, i; |
| uint32_t *freq; |
| |
| hdd_debug("setting mandatory freq/chan list"); |
| |
| freq_len = nla_len( |
| tb[QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST])/ |
| sizeof(uint32_t); |
| |
| if (freq_len > NUM_CHANNELS) { |
| hdd_err("insufficient space to hold channels"); |
| return -ENOMEM; |
| } |
| |
| freq = nla_data( |
| tb[QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST]); |
| |
| hdd_debug("freq_len=%d", freq_len); |
| |
| for (i = 0; i < freq_len; i++) { |
| hdd_debug("freq[%d]=%d", i, freq[i]); |
| } |
| |
| status = policy_mgr_set_sap_mandatory_channels( |
| hdd_ctx->psoc, freq, freq_len); |
| if (QDF_IS_STATUS_ERROR(status)) |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_sap_configuration_set() - sap configuration vendor command |
| * @wiphy: wiphy structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of @data |
| * |
| * __wlan_hdd_cfg80211_sap_configuration_set function set SAP params to |
| * driver. |
| * QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHAN will set sap config channel and |
| * will initiate restart of sap. |
| * |
| * Return: 0 on success; errno on failure |
| */ |
| static int wlan_hdd_cfg80211_sap_configuration_set(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_sap_configuration_set(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * wlan_hdd_process_wake_lock_stats() - wrapper function to absract cp_stats |
| * or legacy get_wake_lock_stats API. |
| * @hdd_ctx: pointer to hdd_ctx |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| static int wlan_hdd_process_wake_lock_stats(struct hdd_context *hdd_ctx) |
| { |
| return wlan_cfg80211_mc_cp_stats_get_wakelock_stats(hdd_ctx->psoc, |
| hdd_ctx->wiphy); |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_get_wakelock_stats() - gets wake lock stats |
| * @wiphy: wiphy pointer |
| * @wdev: pointer to struct wireless_dev |
| * @data: pointer to incoming NL vendor data |
| * @data_len: length of @data |
| * |
| * This function parses the incoming NL vendor command data attributes and |
| * invokes the SME Api and blocks on a completion variable. |
| * WMA copies required data and invokes callback |
| * wlan_hdd_cfg80211_wakelock_stats_rsp_callback to send wake lock stats. |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| static int __wlan_hdd_cfg80211_get_wakelock_stats(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret; |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return -EINVAL; |
| |
| ret = wlan_hdd_process_wake_lock_stats(hdd_ctx); |
| hdd_exit(); |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_get_wakelock_stats() - gets wake lock stats |
| * @wiphy: wiphy pointer |
| * @wdev: pointer to struct wireless_dev |
| * @data: pointer to incoming NL vendor data |
| * @data_len: length of @data |
| * |
| * This function parses the incoming NL vendor command data attributes and |
| * invokes the SME Api and blocks on a completion variable. |
| * WMA copies required data and invokes callback |
| * wlan_hdd_cfg80211_wakelock_stats_rsp_callback to send wake lock stats. |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| static int wlan_hdd_cfg80211_get_wakelock_stats(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_wakelock_stats(wiphy, wdev, |
| data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_get_bus_size() - Get WMI Bus size |
| * @wiphy: wiphy structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of @data |
| * |
| * This function reads wmi max bus size and fill in the skb with |
| * NL attributes and send up the NL event. |
| * Return: 0 on success; errno on failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_get_bus_size(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| int ret_val; |
| struct sk_buff *skb; |
| uint32_t nl_buf_len; |
| |
| hdd_enter(); |
| |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| return ret_val; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| hdd_debug("WMI Max Bus size: %d", hdd_ctx->wmi_max_len); |
| |
| nl_buf_len = NLMSG_HDRLEN; |
| nl_buf_len += (sizeof(hdd_ctx->wmi_max_len) + NLA_HDRLEN); |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| if (!skb) { |
| hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| return -ENOMEM; |
| } |
| |
| if (nla_put_u16(skb, QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE, |
| hdd_ctx->wmi_max_len)) { |
| hdd_err("nla put failure"); |
| goto nla_put_failure; |
| } |
| |
| cfg80211_vendor_cmd_reply(skb); |
| |
| hdd_exit(); |
| |
| return 0; |
| |
| nla_put_failure: |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_get_bus_size() - SSR Wrapper to Get Bus size |
| * @wiphy: wiphy structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of @data |
| * |
| * Return: 0 on success; errno on failure |
| */ |
| static int wlan_hdd_cfg80211_get_bus_size(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_bus_size(wiphy, wdev, data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| const struct nla_policy setband_policy[QCA_WLAN_VENDOR_ATTR_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_SETBAND_MASK] = {.type = NLA_U32}, |
| }; |
| |
| /** |
| *__wlan_hdd_cfg80211_setband() - set band |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Length of @data |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int __wlan_hdd_cfg80211_setband(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; |
| int ret; |
| uint32_t reg_wifi_band_bitmap = 0, band_val, band_mask; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, |
| data, data_len, setband_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_SETBAND_MASK]) { |
| band_mask = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_SETBAND_MASK]); |
| reg_wifi_band_bitmap = |
| wlan_vendor_bitmap_to_reg_wifi_band_bitmap(hdd_ctx->psoc, |
| band_mask); |
| hdd_debug("[SET BAND] set band mask:%d", reg_wifi_band_bitmap); |
| } else if (tb[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE]) { |
| band_val = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE]); |
| reg_wifi_band_bitmap = |
| hdd_reg_legacy_setband_to_reg_wifi_band_bitmap( |
| band_val); |
| } |
| |
| if (!reg_wifi_band_bitmap) { |
| hdd_err("attr SETBAND_VALUE failed"); |
| return -EINVAL; |
| } |
| |
| ret = hdd_reg_set_band(dev, reg_wifi_band_bitmap); |
| |
| hdd_exit(); |
| return ret; |
| } |
| |
| /** |
| *wlan_hdd_validate_acs_channel() - validate channel frequency provided by ACS |
| * @adapter: hdd adapter |
| * @chan_freq: channel frequency in MHz |
| * |
| * return: QDF status based on success or failure |
| */ |
| static QDF_STATUS wlan_hdd_validate_acs_channel(struct hdd_adapter *adapter, |
| uint32_t chan_freq, int chan_bw) |
| { |
| if (QDF_STATUS_SUCCESS != |
| wlan_hdd_validate_operation_channel(adapter, chan_freq)) |
| return QDF_STATUS_E_FAILURE; |
| |
| if ((wlansap_is_channel_in_nol_list(WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| chan_freq, |
| PHY_SINGLE_CHANNEL_CENTERED))) { |
| hdd_info("channel %d is in nol", chan_freq); |
| return -EINVAL; |
| } |
| |
| if ((wlansap_is_channel_leaking_in_nol( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| chan_freq, chan_bw))) { |
| hdd_info("channel freq %d is leaking in nol", chan_freq); |
| return -EINVAL; |
| } |
| |
| return 0; |
| |
| } |
| |
| static void hdd_update_acs_sap_config(struct hdd_context *hdd_ctx, |
| struct sap_config *sap_config, |
| struct hdd_vendor_chan_info *channel_list) |
| { |
| uint8_t ch_width; |
| QDF_STATUS status; |
| uint32_t channel_bonding_mode; |
| |
| sap_config->chan_freq = channel_list->pri_chan_freq; |
| |
| sap_config->ch_params.center_freq_seg0 = |
| wlan_reg_freq_to_chan(hdd_ctx->pdev, |
| channel_list->vht_seg0_center_chan_freq); |
| sap_config->ch_params.center_freq_seg1 = |
| wlan_reg_freq_to_chan(hdd_ctx->pdev, |
| channel_list->vht_seg1_center_chan_freq); |
| |
| sap_config->ch_params.sec_ch_offset = |
| wlan_reg_freq_to_chan(hdd_ctx->pdev, |
| channel_list->ht_sec_chan_freq); |
| |
| sap_config->ch_params.ch_width = channel_list->chan_width; |
| if (!WLAN_REG_IS_24GHZ_CH_FREQ(sap_config->chan_freq)) { |
| status = |
| ucfg_mlme_get_vht_channel_width(hdd_ctx->psoc, |
| &ch_width); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("Failed to set channel_width"); |
| sap_config->ch_width_orig = ch_width; |
| } else { |
| ucfg_mlme_get_channel_bonding_24ghz(hdd_ctx->psoc, |
| &channel_bonding_mode); |
| sap_config->ch_width_orig = channel_bonding_mode ? |
| eHT_CHANNEL_WIDTH_40MHZ : eHT_CHANNEL_WIDTH_20MHZ; |
| } |
| sap_config->acs_cfg.pri_ch_freq = channel_list->pri_chan_freq; |
| sap_config->acs_cfg.ch_width = channel_list->chan_width; |
| sap_config->acs_cfg.vht_seg0_center_ch_freq = |
| channel_list->vht_seg0_center_chan_freq; |
| sap_config->acs_cfg.vht_seg1_center_ch_freq = |
| channel_list->vht_seg1_center_chan_freq; |
| sap_config->acs_cfg.ht_sec_ch_freq = |
| channel_list->ht_sec_chan_freq; |
| } |
| |
| static int hdd_update_acs_channel(struct hdd_adapter *adapter, uint8_t reason, |
| uint8_t channel_cnt, |
| struct hdd_vendor_chan_info *channel_list) |
| { |
| struct sap_config *sap_config; |
| struct hdd_ap_ctx *hdd_ap_ctx; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| mac_handle_t mac_handle; |
| uint32_t ch; |
| |
| if (!channel_list) { |
| hdd_err("channel_list is NULL"); |
| return -EINVAL; |
| } |
| |
| hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| sap_config = &adapter->session.ap.sap_config; |
| |
| if (QDF_TIMER_STATE_RUNNING == |
| qdf_mc_timer_get_current_state(&adapter->session. |
| ap.vendor_acs_timer)) { |
| qdf_mc_timer_stop(&adapter->session.ap.vendor_acs_timer); |
| } |
| |
| if (channel_list->pri_chan_freq == 0) { |
| /* Check mode, set default channel */ |
| channel_list->pri_chan_freq = 2437; |
| /* |
| * sap_select_default_oper_chan(mac_handle, |
| * sap_config->acs_cfg.hw_mode); |
| */ |
| } |
| |
| mac_handle = hdd_ctx->mac_handle; |
| switch (reason) { |
| /* SAP init case */ |
| case QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT: |
| hdd_update_acs_sap_config(hdd_ctx, sap_config, channel_list); |
| /* Update Hostapd */ |
| wlan_hdd_cfg80211_acs_ch_select_evt(adapter); |
| break; |
| |
| /* DFS detected on current channel */ |
| case QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS: |
| ch = wlan_reg_freq_to_chan(hdd_ctx->pdev, |
| channel_list->pri_chan_freq); |
| |
| wlan_sap_update_next_channel( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter), (uint8_t)ch, |
| channel_list->chan_width); |
| status = sme_update_new_channel_event( |
| mac_handle, |
| adapter->vdev_id); |
| break; |
| |
| /* LTE coex event on current channel */ |
| case QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX: |
| ch = wlan_reg_freq_to_chan(hdd_ctx->pdev, |
| channel_list->pri_chan_freq); |
| sap_config->acs_cfg.pri_ch_freq = channel_list->pri_chan_freq; |
| sap_config->acs_cfg.ch_width = channel_list->chan_width; |
| hdd_ap_ctx->sap_config.ch_width_orig = |
| channel_list->chan_width; |
| wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc, adapter->vdev_id, |
| CSA_REASON_LTE_COEX); |
| hdd_switch_sap_channel(adapter, (uint8_t)ch, true); |
| break; |
| |
| default: |
| hdd_info("invalid reason for timer invoke"); |
| } |
| hdd_exit(); |
| return qdf_status_to_os_return(status); |
| } |
| |
| /** |
| * Define short name for vendor channel set config |
| */ |
| #define SET_CHAN_REASON QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON |
| #define SET_CHAN_CHAN_LIST QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST |
| #define SET_CHAN_PRIMARY_CHANNEL \ |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY |
| #define SET_CHAN_SECONDARY_CHANNEL \ |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY |
| #define SET_CHAN_SEG0_CENTER_CHANNEL \ |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0 |
| #define SET_CHAN_SEG1_CENTER_CHANNEL \ |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1 |
| #define SET_CHAN_CHANNEL_WIDTH \ |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH |
| |
| #define SET_CHAN_FREQ_LIST QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_LIST |
| #define SET_CHAN_FREQUENCY_PRIMARY \ |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_PRIMARY |
| #define SET_CHAN_FREQUENCY_SECONDARY \ |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY |
| #define SET_CHAN_SEG0_CENTER_FREQUENCY \ |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0 |
| #define SET_CHAN_SEG1_CENTER_FREQUENCY \ |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1 |
| |
| #define SET_CHAN_MAX QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_MAX |
| #define SET_EXT_ACS_BAND QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND |
| |
| static const struct nla_policy acs_chan_config_policy[SET_CHAN_MAX + 1] = { |
| [SET_CHAN_REASON] = {.type = NLA_U8}, |
| [SET_CHAN_CHAN_LIST] = {.type = NLA_NESTED}, |
| [SET_CHAN_FREQ_LIST] = {.type = NLA_NESTED}, |
| }; |
| |
| static const struct nla_policy acs_chan_list_policy[SET_CHAN_MAX + 1] = { |
| [SET_CHAN_PRIMARY_CHANNEL] = {.type = NLA_U8}, |
| [SET_CHAN_SECONDARY_CHANNEL] = {.type = NLA_U8}, |
| [SET_CHAN_SEG0_CENTER_CHANNEL] = {.type = NLA_U8}, |
| [SET_CHAN_SEG1_CENTER_CHANNEL] = {.type = NLA_U8}, |
| [SET_CHAN_CHANNEL_WIDTH] = {.type = NLA_U8}, |
| [SET_EXT_ACS_BAND] = {.type = NLA_U8}, |
| |
| [SET_CHAN_FREQUENCY_PRIMARY] = {.type = NLA_U32}, |
| [SET_CHAN_FREQUENCY_SECONDARY] = {.type = NLA_U32}, |
| [SET_CHAN_SEG0_CENTER_FREQUENCY] = {.type = NLA_U32}, |
| [SET_CHAN_SEG1_CENTER_FREQUENCY] = {.type = NLA_U32}, |
| }; |
| |
| /** |
| * hdd_extract_external_acs_frequencies() - API to parse and extract vendor acs |
| * channel frequency (in MHz) configuration. |
| * @hdd_ctx: pointer to hdd context |
| * @list_ptr: pointer to hdd_vendor_chan_info |
| * @channel_cnt: channel count |
| * @data: data |
| * @data_len: data len |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int |
| hdd_extract_external_acs_frequencies(struct hdd_context *hdd_ctx, |
| struct hdd_vendor_chan_info **list_ptr, |
| uint8_t *channel_cnt, |
| const void *data, int data_len) |
| { |
| int rem; |
| uint32_t i = 0; |
| struct nlattr *tb[SET_CHAN_MAX + 1]; |
| struct nlattr *tb2[SET_CHAN_MAX + 1]; |
| struct nlattr *curr_attr; |
| struct hdd_vendor_chan_info *channel_list; |
| |
| if (wlan_cfg80211_nla_parse(tb, SET_CHAN_MAX, data, data_len, |
| acs_chan_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| nla_for_each_nested(curr_attr, tb[SET_CHAN_FREQ_LIST], rem) |
| i++; |
| |
| if (!i) { |
| hdd_err_rl("Error: channel count is zero"); |
| return -EINVAL; |
| } |
| |
| if (i > NUM_CHANNELS) { |
| hdd_err_rl("Error: Exceeded max channels: %u", NUM_CHANNELS); |
| return -ENOMEM; |
| } |
| |
| channel_list = qdf_mem_malloc(sizeof(struct hdd_vendor_chan_info) * i); |
| if (!channel_list) |
| return -ENOMEM; |
| |
| *channel_cnt = (uint8_t)i; |
| i = 0; |
| nla_for_each_nested(curr_attr, tb[SET_CHAN_FREQ_LIST], rem) { |
| if (wlan_cfg80211_nla_parse_nested(tb2, SET_CHAN_MAX, |
| curr_attr, |
| acs_chan_list_policy)) { |
| hdd_err_rl("nla_parse failed"); |
| qdf_mem_free(channel_list); |
| *channel_cnt = 0; |
| return -EINVAL; |
| } |
| |
| if (tb2[SET_EXT_ACS_BAND]) |
| channel_list[i].band = |
| nla_get_u8(tb2[SET_EXT_ACS_BAND]); |
| |
| if (tb2[SET_CHAN_FREQUENCY_PRIMARY]) |
| channel_list[i].pri_chan_freq = |
| nla_get_u32(tb2[SET_CHAN_FREQUENCY_PRIMARY]); |
| |
| if (tb2[SET_CHAN_FREQUENCY_SECONDARY]) |
| channel_list[i].ht_sec_chan_freq = |
| nla_get_u32(tb2[SET_CHAN_FREQUENCY_SECONDARY]); |
| |
| if (tb2[SET_CHAN_SEG0_CENTER_FREQUENCY]) |
| channel_list[i].vht_seg0_center_chan_freq = |
| nla_get_u32(tb2[SET_CHAN_SEG0_CENTER_FREQUENCY]); |
| |
| if (tb2[SET_CHAN_SEG1_CENTER_FREQUENCY]) |
| channel_list[i].vht_seg1_center_chan_freq = |
| nla_get_u32(tb2[SET_CHAN_SEG1_CENTER_FREQUENCY]); |
| |
| if (tb2[SET_CHAN_CHANNEL_WIDTH]) |
| channel_list[i].chan_width = |
| nla_get_u8(tb2[SET_CHAN_CHANNEL_WIDTH]); |
| |
| hdd_debug("index %d, pri_chan_freq %u, ht_sec_chan_freq %u seg0_freq %u seg1_freq %u width %u", |
| i, channel_list[i].pri_chan_freq, |
| channel_list[i].ht_sec_chan_freq, |
| channel_list[i].vht_seg0_center_chan_freq, |
| channel_list[i].vht_seg1_center_chan_freq, |
| channel_list[i].chan_width); |
| i++; |
| } |
| *list_ptr = channel_list; |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_extract_external_acs_channels() - API to parse and extract vendor acs |
| * channel configuration. |
| * @hdd_ctx: pointer to hdd context |
| * @list_ptr: pointer to hdd_vendor_chan_info |
| * @channel_cnt: channel count |
| * @data: data |
| * @data_len: data len |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int |
| hdd_extract_external_acs_channels(struct hdd_context *hdd_ctx, |
| struct hdd_vendor_chan_info **list_ptr, |
| uint8_t *channel_cnt, |
| const void *data, int data_len) |
| { |
| int rem; |
| uint32_t i = 0; |
| struct nlattr *tb[SET_CHAN_MAX + 1]; |
| struct nlattr *tb2[SET_CHAN_MAX + 1]; |
| struct nlattr *curr_attr; |
| struct hdd_vendor_chan_info *channel_list; |
| |
| if (wlan_cfg80211_nla_parse(tb, SET_CHAN_MAX, data, data_len, |
| acs_chan_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| nla_for_each_nested(curr_attr, tb[SET_CHAN_CHAN_LIST], rem) |
| i++; |
| |
| if (!i) { |
| hdd_err_rl("Error: channel count is zero"); |
| return -EINVAL; |
| } |
| |
| if (i > NUM_CHANNELS) { |
| hdd_err_rl("Error: Exceeded max channels: %u", NUM_CHANNELS); |
| return -ENOMEM; |
| } |
| |
| channel_list = qdf_mem_malloc(sizeof(struct hdd_vendor_chan_info) * i); |
| if (!channel_list) |
| return -ENOMEM; |
| |
| *channel_cnt = (uint8_t)i; |
| i = 0; |
| nla_for_each_nested(curr_attr, tb[SET_CHAN_CHAN_LIST], rem) { |
| if (wlan_cfg80211_nla_parse_nested(tb2, SET_CHAN_MAX, |
| curr_attr, |
| acs_chan_list_policy)) { |
| hdd_err("nla_parse failed"); |
| qdf_mem_free(channel_list); |
| *channel_cnt = 0; |
| return -EINVAL; |
| } |
| |
| if (tb2[SET_EXT_ACS_BAND]) { |
| channel_list[i].band = |
| nla_get_u8(tb2[SET_EXT_ACS_BAND]); |
| } |
| |
| if (tb2[SET_CHAN_PRIMARY_CHANNEL]) { |
| uint32_t ch = |
| nla_get_u8(tb2[SET_CHAN_PRIMARY_CHANNEL]); |
| |
| channel_list[i].pri_chan_freq = |
| wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, ch); |
| } |
| |
| if (tb2[SET_CHAN_SECONDARY_CHANNEL]) { |
| uint32_t ch = |
| nla_get_u8(tb2[SET_CHAN_SECONDARY_CHANNEL]); |
| |
| channel_list[i].ht_sec_chan_freq = |
| wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, ch); |
| } |
| |
| if (tb2[SET_CHAN_SEG0_CENTER_CHANNEL]) { |
| uint32_t ch = |
| nla_get_u8(tb2[SET_CHAN_SEG0_CENTER_CHANNEL]); |
| |
| channel_list[i].vht_seg0_center_chan_freq = |
| wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, ch); |
| } |
| |
| if (tb2[SET_CHAN_SEG1_CENTER_CHANNEL]) { |
| uint32_t ch = |
| nla_get_u8(tb2[SET_CHAN_SEG1_CENTER_CHANNEL]); |
| |
| channel_list[i].vht_seg1_center_chan_freq = |
| wlan_reg_legacy_chan_to_freq(hdd_ctx->pdev, ch); |
| } |
| |
| if (tb2[SET_CHAN_CHANNEL_WIDTH]) { |
| channel_list[i].chan_width = |
| nla_get_u8(tb2[SET_CHAN_CHANNEL_WIDTH]); |
| } |
| hdd_debug("index %d, pri_chan_freq %u, ht_sec_chan_freq %u seg0_freq %u seg1_freq %u width %u", |
| i, channel_list[i].pri_chan_freq, |
| channel_list[i].ht_sec_chan_freq, |
| channel_list[i].vht_seg0_center_chan_freq, |
| channel_list[i].vht_seg1_center_chan_freq, |
| channel_list[i].chan_width); |
| i++; |
| } |
| *list_ptr = channel_list; |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_parse_vendor_acs_chan_config() - API to parse vendor acs channel config |
| * @hdd_ctx: pointer to hdd context |
| * @channel_list: pointer to hdd_vendor_chan_info |
| * @reason: channel change reason |
| * @channel_cnt: channel count |
| * @data: data |
| * @data_len: data len |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int |
| hdd_parse_vendor_acs_chan_config(struct hdd_context *hdd_ctx, |
| struct hdd_vendor_chan_info **chan_list_ptr, |
| uint8_t *reason, uint8_t *channel_cnt, |
| const void *data, int data_len) |
| { |
| struct nlattr *tb[SET_CHAN_MAX + 1]; |
| int ret; |
| |
| if (wlan_cfg80211_nla_parse(tb, SET_CHAN_MAX, data, data_len, |
| acs_chan_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| if (tb[SET_CHAN_REASON]) |
| *reason = nla_get_u8(tb[SET_CHAN_REASON]); |
| |
| if (!tb[SET_CHAN_FREQ_LIST] && !tb[SET_CHAN_CHAN_LIST]) { |
| hdd_err("Both channel list and frequency list are empty"); |
| return -EINVAL; |
| } |
| |
| if (tb[SET_CHAN_FREQ_LIST]) { |
| ret = hdd_extract_external_acs_frequencies(hdd_ctx, |
| chan_list_ptr, |
| channel_cnt, |
| data, data_len); |
| if (ret) { |
| hdd_err("Failed to extract frequencies"); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| ret = hdd_extract_external_acs_channels(hdd_ctx, chan_list_ptr, |
| channel_cnt, data, data_len); |
| if (ret) |
| hdd_err("Failed to extract channels"); |
| |
| return ret; |
| } |
| |
| /** |
| * Undef short names for vendor set channel configuration |
| */ |
| #undef SET_CHAN_REASON |
| #undef SET_CHAN_CHAN_LIST |
| #undef SET_CHAN_PRIMARY_CHANNEL |
| #undef SET_CHAN_SECONDARY_CHANNEL |
| #undef SET_CHAN_SEG0_CENTER_CHANNEL |
| #undef SET_CHAN_SEG1_CENTER_CHANNEL |
| |
| #undef SET_CHAN_FREQ_LIST |
| #undef SET_CHAN_FREQUENCY_PRIMARY |
| #undef SET_CHAN_FREQUENCY_SECONDARY |
| #undef SET_CHAN_SEG0_CENTER_FREQUENCY |
| #undef SET_CHAN_SEG1_CENTER_FREQUENCY |
| |
| #undef SET_CHAN_CHANNEL_WIDTH |
| #undef SET_CHAN_MAX |
| |
| /** |
| * __wlan_hdd_cfg80211_update_vendor_channel() - update vendor channel |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Length of @data |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int __wlan_hdd_cfg80211_update_vendor_channel(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret_val; |
| QDF_STATUS qdf_status; |
| uint8_t channel_cnt = 0, reason = -1; |
| struct hdd_vendor_chan_info *channel_list = NULL; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct hdd_vendor_chan_info *channel_list_ptr; |
| |
| hdd_enter(); |
| |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| return ret_val; |
| |
| if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (test_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags)) |
| clear_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags); |
| else { |
| hdd_err("already timeout happened for acs"); |
| return -EINVAL; |
| } |
| |
| ret_val = hdd_parse_vendor_acs_chan_config(hdd_ctx, &channel_list, |
| &reason, &channel_cnt, data, |
| data_len); |
| channel_list_ptr = channel_list; |
| if (ret_val) |
| return ret_val; |
| |
| /* Validate channel to be set */ |
| while (channel_cnt && channel_list) { |
| qdf_status = wlan_hdd_validate_acs_channel(adapter, |
| channel_list->pri_chan_freq, |
| channel_list->chan_width); |
| if (qdf_status == QDF_STATUS_SUCCESS) |
| break; |
| else if (channel_cnt == 1) { |
| hdd_err("invalid channel frequ %u received from app", |
| channel_list->pri_chan_freq); |
| channel_list->pri_chan_freq = 0; |
| break; |
| } |
| |
| channel_cnt--; |
| channel_list++; |
| } |
| |
| if ((channel_cnt <= 0) || !channel_list) { |
| hdd_err("no available channel/chanlist %d/%pK", channel_cnt, |
| channel_list); |
| qdf_mem_free(channel_list_ptr); |
| return -EINVAL; |
| } |
| |
| hdd_debug("received primary channel freq as %d", |
| channel_list->pri_chan_freq); |
| |
| ret_val = hdd_update_acs_channel(adapter, reason, |
| channel_cnt, channel_list); |
| qdf_mem_free(channel_list_ptr); |
| return ret_val; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_update_vendor_channel() - update vendor channel |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Length of @data |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int wlan_hdd_cfg80211_update_vendor_channel(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_update_vendor_channel(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_setband() - Wrapper to setband |
| * @wiphy: wiphy structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of @data |
| * |
| * Return: 0 on success; errno on failure |
| */ |
| static int wlan_hdd_cfg80211_setband(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_setband(wiphy, wdev, data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| *__wlan_hdd_cfg80211_getband() - get band |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Length of @data |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int __wlan_hdd_cfg80211_getband(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct sk_buff *skb; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| int ret; |
| uint32_t reg_wifi_band_bitmap, vendor_band_mask; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, |
| sizeof(uint32_t) + |
| NLA_HDRLEN); |
| |
| if (!skb) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return -ENOMEM; |
| } |
| |
| status = ucfg_reg_get_band(hdd_ctx->pdev, ®_wifi_band_bitmap); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("failed to get band"); |
| goto failure; |
| } |
| |
| vendor_band_mask = wlan_reg_wifi_band_bitmap_to_vendor_bitmap( |
| reg_wifi_band_bitmap); |
| |
| if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_SETBAND_MASK, |
| vendor_band_mask)) { |
| hdd_err("nla put failure"); |
| goto failure; |
| } |
| |
| cfg80211_vendor_cmd_reply(skb); |
| |
| hdd_exit(); |
| |
| return 0; |
| |
| failure: |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_getband() - Wrapper to getband |
| * @wiphy: wiphy structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of @data |
| * |
| * Return: 0 on success; errno on failure |
| */ |
| static int wlan_hdd_cfg80211_getband(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_getband(wiphy, wdev, data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| static const struct |
| nla_policy qca_wlan_vendor_attr[QCA_WLAN_VENDOR_ATTR_MAX+1] = { |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_MAC_ADDR] = {.type = NLA_BINARY, |
| .len = QDF_MAC_ADDR_SIZE}, |
| }; |
| |
| void wlan_hdd_rso_cmd_status_cb(hdd_handle_t hdd_handle, |
| struct rso_cmd_status *rso_status) |
| { |
| struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); |
| struct hdd_adapter *adapter; |
| |
| adapter = hdd_get_adapter_by_vdev(hdd_ctx, rso_status->vdev_id); |
| if (!adapter) { |
| hdd_err("adapter NULL"); |
| return; |
| } |
| |
| adapter->lfr_fw_status.is_disabled = rso_status->status; |
| complete(&adapter->lfr_fw_status.disable_lfr_event); |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_set_fast_roaming() - enable/disable roaming |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Length of @data |
| * |
| * This function is used to enable/disable roaming using vendor commands |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int __wlan_hdd_cfg80211_set_fast_roaming(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; |
| uint32_t is_fast_roam_enabled; |
| int ret; |
| QDF_STATUS qdf_status; |
| unsigned long rc; |
| bool roaming_enabled; |
| |
| hdd_enter_dev(dev); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| if (adapter->device_mode != QDF_STA_MODE) { |
| hdd_err_rl("command not allowed in %d mode, vdev_id: %d", |
| adapter->device_mode, adapter->vdev_id); |
| return -EINVAL; |
| } |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| ret = wlan_cfg80211_nla_parse(tb, |
| QCA_WLAN_VENDOR_ATTR_MAX, data, data_len, |
| qca_wlan_vendor_attr); |
| if (ret) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch Enable flag */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]) { |
| hdd_err("attr enable failed"); |
| return -EINVAL; |
| } |
| |
| is_fast_roam_enabled = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]); |
| hdd_debug("ROAM_CONFIG: isFastRoamEnabled %d", is_fast_roam_enabled); |
| |
| if (sme_roaming_in_progress(hdd_ctx->mac_handle, adapter->vdev_id)) { |
| hdd_err_rl("Roaming in progress for vdev %d", adapter->vdev_id); |
| return -EAGAIN; |
| } |
| |
| /* |
| * Get current roaming state and decide whether to wait for RSO_STOP |
| * response or not. |
| */ |
| roaming_enabled = ucfg_is_roaming_enabled(hdd_ctx->pdev, |
| adapter->vdev_id); |
| |
| /* Update roaming */ |
| qdf_status = ucfg_user_space_enable_disable_rso(hdd_ctx->pdev, |
| adapter->vdev_id, |
| is_fast_roam_enabled); |
| if (QDF_IS_STATUS_ERROR(qdf_status)) |
| hdd_err("ROAM_CONFIG: sme_config_fast_roaming failed with status=%d", |
| qdf_status); |
| |
| ret = qdf_status_to_os_return(qdf_status); |
| |
| if (hdd_cm_is_vdev_associated(adapter) && |
| roaming_enabled && |
| QDF_IS_STATUS_SUCCESS(qdf_status) && !is_fast_roam_enabled) { |
| INIT_COMPLETION(adapter->lfr_fw_status.disable_lfr_event); |
| /* |
| * wait only for LFR disable in fw as LFR enable |
| * is always success |
| */ |
| rc = wait_for_completion_timeout( |
| &adapter->lfr_fw_status.disable_lfr_event, |
| msecs_to_jiffies(WAIT_TIME_RSO_CMD_STATUS)); |
| if (!rc) { |
| hdd_err("Timed out waiting for RSO CMD status"); |
| return -ETIMEDOUT; |
| } |
| |
| if (!adapter->lfr_fw_status.is_disabled) { |
| hdd_err("Roam disable attempt in FW fails"); |
| return -EBUSY; |
| } |
| } |
| |
| hdd_exit(); |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_fast_roaming() - enable/disable roaming |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Length of @data |
| * |
| * Wrapper function of __wlan_hdd_cfg80211_set_fast_roaming() |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int wlan_hdd_cfg80211_set_fast_roaming(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_fast_roaming(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /* |
| * define short names for the global vendor params |
| * used by wlan_hdd_cfg80211_setarp_stats_cmd() |
| */ |
| #define STATS_SET_INVALID \ |
| QCA_ATTR_NUD_STATS_SET_INVALID |
| #define STATS_SET_START \ |
| QCA_ATTR_NUD_STATS_SET_START |
| #define STATS_GW_IPV4 \ |
| QCA_ATTR_NUD_STATS_GW_IPV4 |
| #define STATS_SET_DATA_PKT_INFO \ |
| QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO |
| #define STATS_SET_MAX \ |
| QCA_ATTR_NUD_STATS_SET_MAX |
| |
| const struct nla_policy |
| qca_wlan_vendor_set_nud_stats_policy[STATS_SET_MAX + 1] = { |
| [STATS_SET_START] = {.type = NLA_FLAG }, |
| [STATS_GW_IPV4] = {.type = NLA_U32 }, |
| [STATS_SET_DATA_PKT_INFO] = {.type = NLA_NESTED }, |
| }; |
| |
| /* define short names for the global vendor params */ |
| #define CONNECTIVITY_STATS_SET_INVALID \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_SET_INVALID |
| #define STATS_PKT_INFO_TYPE \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_STATS_PKT_INFO_TYPE |
| #define STATS_DNS_DOMAIN_NAME \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_DNS_DOMAIN_NAME |
| #define STATS_SRC_PORT \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_SRC_PORT |
| #define STATS_DEST_PORT \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_DEST_PORT |
| #define STATS_DEST_IPV4 \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_DEST_IPV4 |
| #define STATS_DEST_IPV6 \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_DEST_IPV6 |
| #define CONNECTIVITY_STATS_SET_MAX \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_SET_MAX |
| |
| const struct nla_policy |
| qca_wlan_vendor_set_connectivity_check_stats[CONNECTIVITY_STATS_SET_MAX + 1] = { |
| [STATS_PKT_INFO_TYPE] = {.type = NLA_U32 }, |
| [STATS_DNS_DOMAIN_NAME] = {.type = NLA_NUL_STRING, |
| .len = DNS_DOMAIN_NAME_MAX_LEN }, |
| [STATS_SRC_PORT] = {.type = NLA_U32 }, |
| [STATS_DEST_PORT] = {.type = NLA_U32 }, |
| [STATS_DEST_IPV4] = {.type = NLA_U32 }, |
| [STATS_DEST_IPV6] = {.type = NLA_BINARY, |
| .len = ICMPv6_ADDR_LEN }, |
| }; |
| |
| /** |
| * hdd_dns_unmake_name_query() - Convert an uncompressed DNS name to a |
| * NUL-terminated string |
| * @name: DNS name |
| * |
| * Return: Produce a printable version of a DNS name. |
| */ |
| static inline uint8_t *hdd_dns_unmake_name_query(uint8_t *name) |
| { |
| uint8_t *p; |
| unsigned int len; |
| |
| p = name; |
| while ((len = *p)) { |
| *(p++) = '.'; |
| p += len; |
| } |
| |
| return name + 1; |
| } |
| |
| /** |
| * hdd_dns_make_name_query() - Convert a standard NUL-terminated string |
| * to DNS name |
| * @string: Name as a NUL-terminated string |
| * @buf: Buffer in which to place DNS name |
| * |
| * DNS names consist of "<length>element" pairs. |
| * |
| * Return: Byte following constructed DNS name |
| */ |
| static uint8_t *hdd_dns_make_name_query(const uint8_t *string, |
| uint8_t *buf, uint8_t len) |
| { |
| uint8_t *length_byte = buf++; |
| uint8_t c; |
| |
| if (string[len - 1]) { |
| hdd_debug("DNS name is not null terminated"); |
| return NULL; |
| } |
| |
| while ((c = *(string++))) { |
| if (c == '.') { |
| *length_byte = buf - length_byte - 1; |
| length_byte = buf; |
| } |
| *(buf++) = c; |
| } |
| *length_byte = buf - length_byte - 1; |
| *(buf++) = '\0'; |
| return buf; |
| } |
| |
| /** |
| * hdd_set_clear_connectivity_check_stats_info() - set/clear stats info |
| * @adapter: Pointer to hdd adapter |
| * @arp_stats_params: arp stats structure to be sent to FW |
| * @tb: nl attribute |
| * @is_set_stats: set/clear stats |
| * |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int hdd_set_clear_connectivity_check_stats_info( |
| struct hdd_adapter *adapter, |
| struct set_arp_stats_params *arp_stats_params, |
| struct nlattr **tb, bool is_set_stats) |
| { |
| struct nlattr *tb2[CONNECTIVITY_STATS_SET_MAX + 1]; |
| struct nlattr *curr_attr = NULL; |
| int err = 0; |
| uint32_t pkt_bitmap; |
| int rem; |
| |
| /* Set NUD command for start tracking is received. */ |
| nla_for_each_nested(curr_attr, |
| tb[STATS_SET_DATA_PKT_INFO], |
| rem) { |
| |
| if (wlan_cfg80211_nla_parse(tb2, |
| CONNECTIVITY_STATS_SET_MAX, |
| nla_data(curr_attr), nla_len(curr_attr), |
| qca_wlan_vendor_set_connectivity_check_stats)) { |
| hdd_err("nla_parse failed"); |
| err = -EINVAL; |
| goto end; |
| } |
| |
| if (tb2[STATS_PKT_INFO_TYPE]) { |
| pkt_bitmap = nla_get_u32(tb2[STATS_PKT_INFO_TYPE]); |
| if (!pkt_bitmap) { |
| hdd_err("pkt tracking bitmap is empty"); |
| err = -EINVAL; |
| goto end; |
| } |
| |
| if (is_set_stats) { |
| arp_stats_params->pkt_type_bitmap = pkt_bitmap; |
| arp_stats_params->flag = true; |
| adapter->pkt_type_bitmap |= |
| arp_stats_params->pkt_type_bitmap; |
| |
| if (pkt_bitmap & CONNECTIVITY_CHECK_SET_ARP) { |
| if (!tb[STATS_GW_IPV4]) { |
| hdd_err("GW ipv4 address is not present"); |
| err = -EINVAL; |
| goto end; |
| } |
| arp_stats_params->ip_addr = |
| nla_get_u32(tb[STATS_GW_IPV4]); |
| arp_stats_params->pkt_type = |
| WLAN_NUD_STATS_ARP_PKT_TYPE; |
| adapter->track_arp_ip = |
| arp_stats_params->ip_addr; |
| } |
| |
| if (pkt_bitmap & CONNECTIVITY_CHECK_SET_DNS) { |
| uint8_t *domain_name; |
| |
| if (!tb2[STATS_DNS_DOMAIN_NAME]) { |
| hdd_err("DNS domain id is not present"); |
| err = -EINVAL; |
| goto end; |
| } |
| domain_name = nla_data( |
| tb2[STATS_DNS_DOMAIN_NAME]); |
| adapter->track_dns_domain_len = |
| nla_len(tb2[ |
| STATS_DNS_DOMAIN_NAME]); |
| if (!hdd_dns_make_name_query( |
| domain_name, |
| adapter->dns_payload, |
| adapter->track_dns_domain_len)) |
| adapter->track_dns_domain_len = |
| 0; |
| /* DNStracking isn't supported in FW. */ |
| arp_stats_params->pkt_type_bitmap &= |
| ~CONNECTIVITY_CHECK_SET_DNS; |
| } |
| |
| if (pkt_bitmap & |
| CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE) { |
| if (!tb2[STATS_SRC_PORT] || |
| !tb2[STATS_DEST_PORT]) { |
| hdd_err("Source/Dest port is not present"); |
| err = -EINVAL; |
| goto end; |
| } |
| arp_stats_params->tcp_src_port = |
| nla_get_u32( |
| tb2[STATS_SRC_PORT]); |
| arp_stats_params->tcp_dst_port = |
| nla_get_u32( |
| tb2[STATS_DEST_PORT]); |
| adapter->track_src_port = |
| arp_stats_params->tcp_src_port; |
| adapter->track_dest_port = |
| arp_stats_params->tcp_dst_port; |
| } |
| |
| if (pkt_bitmap & |
| CONNECTIVITY_CHECK_SET_ICMPV4) { |
| if (!tb2[STATS_DEST_IPV4]) { |
| hdd_err("destination ipv4 address to track ping packets is not present"); |
| err = -EINVAL; |
| goto end; |
| } |
| arp_stats_params->icmp_ipv4 = |
| nla_get_u32( |
| tb2[STATS_DEST_IPV4]); |
| adapter->track_dest_ipv4 = |
| arp_stats_params->icmp_ipv4; |
| } |
| } else { |
| /* clear stats command received */ |
| arp_stats_params->pkt_type_bitmap = pkt_bitmap; |
| arp_stats_params->flag = false; |
| adapter->pkt_type_bitmap &= |
| (~arp_stats_params->pkt_type_bitmap); |
| |
| if (pkt_bitmap & CONNECTIVITY_CHECK_SET_ARP) { |
| arp_stats_params->pkt_type = |
| WLAN_NUD_STATS_ARP_PKT_TYPE; |
| qdf_mem_zero(&adapter->hdd_stats. |
| hdd_arp_stats, |
| sizeof(adapter->hdd_stats. |
| hdd_arp_stats)); |
| adapter->track_arp_ip = 0; |
| } |
| |
| if (pkt_bitmap & CONNECTIVITY_CHECK_SET_DNS) { |
| /* DNStracking isn't supported in FW. */ |
| arp_stats_params->pkt_type_bitmap &= |
| ~CONNECTIVITY_CHECK_SET_DNS; |
| qdf_mem_zero(&adapter->hdd_stats. |
| hdd_dns_stats, |
| sizeof(adapter->hdd_stats. |
| hdd_dns_stats)); |
| qdf_mem_zero(adapter->dns_payload, |
| adapter->track_dns_domain_len); |
| adapter->track_dns_domain_len = 0; |
| } |
| |
| if (pkt_bitmap & |
| CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE) { |
| qdf_mem_zero(&adapter->hdd_stats. |
| hdd_tcp_stats, |
| sizeof(adapter->hdd_stats. |
| hdd_tcp_stats)); |
| adapter->track_src_port = 0; |
| adapter->track_dest_port = 0; |
| } |
| |
| if (pkt_bitmap & |
| CONNECTIVITY_CHECK_SET_ICMPV4) { |
| qdf_mem_zero(&adapter->hdd_stats. |
| hdd_icmpv4_stats, |
| sizeof(adapter->hdd_stats. |
| hdd_icmpv4_stats)); |
| adapter->track_dest_ipv4 = 0; |
| } |
| } |
| } else { |
| hdd_err("stats list empty"); |
| err = -EINVAL; |
| goto end; |
| } |
| } |
| |
| end: |
| return err; |
| } |
| |
| const struct nla_policy qca_wlan_vendor_set_trace_level_policy[ |
| QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_PARAM] = |
| VENDOR_NLA_POLICY_NESTED(qca_wlan_vendor_set_trace_level_policy), |
| [QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MODULE_ID] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_TRACE_MASK] = {.type = NLA_U32 }, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_set_trace_level() - Set the trace level |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Length of @data |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_set_trace_level(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb1[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX + 1]; |
| struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX + 1]; |
| struct nlattr *apth; |
| int rem; |
| int ret = 1; |
| int print_idx = -1; |
| int module_id = -1; |
| int bit_mask = -1; |
| int status; |
| |
| hdd_enter(); |
| |
| if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret != 0) |
| return -EINVAL; |
| |
| print_idx = qdf_get_pidx(); |
| if (print_idx < 0 || print_idx >= MAX_PRINT_CONFIG_SUPPORTED) { |
| hdd_err("Invalid print controle object index"); |
| return -EINVAL; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb1, |
| QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX, |
| data, data_len, |
| qca_wlan_vendor_set_trace_level_policy)) { |
| hdd_err("Invalid attr"); |
| return -EINVAL; |
| } |
| |
| if (!tb1[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_PARAM]) { |
| hdd_err("attr trace level param failed"); |
| return -EINVAL; |
| } |
| |
| nla_for_each_nested(apth, |
| tb1[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_PARAM], rem) { |
| if (wlan_cfg80211_nla_parse(tb2, |
| QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX, |
| nla_data(apth), nla_len(apth), |
| qca_wlan_vendor_set_trace_level_policy)) { |
| hdd_err("Invalid attr"); |
| return -EINVAL; |
| } |
| |
| if (!tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MODULE_ID]) { |
| hdd_err("attr Module ID failed"); |
| return -EINVAL; |
| } |
| module_id = nla_get_u32 |
| (tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MODULE_ID]); |
| |
| if (!tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_TRACE_MASK]) { |
| hdd_err("attr Verbose mask failed"); |
| return -EINVAL; |
| } |
| bit_mask = nla_get_u32 |
| (tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_TRACE_MASK]); |
| |
| status = hdd_qdf_trace_enable(module_id, bit_mask); |
| |
| if (status != 0) |
| hdd_err("can not set verbose mask %d for the category %d", |
| bit_mask, module_id); |
| } |
| |
| hdd_exit(); |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_trace_level() - Set the trace level |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Length of @data |
| * |
| * Wrapper function of __wlan_hdd_cfg80211_set_trace_level() |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| |
| static int wlan_hdd_cfg80211_set_trace_level(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_trace_level(wiphy, wdev, |
| data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_set_nud_stats() - set arp stats command to firmware |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: pointer to apfind configuration data. |
| * @data_len: the length in byte of apfind data. |
| * |
| * This is called when wlan driver needs to send arp stats to |
| * firmware. |
| * |
| * Return: An error code or 0 on success. |
| */ |
| static int __wlan_hdd_cfg80211_set_nud_stats(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct nlattr *tb[STATS_SET_MAX + 1]; |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct set_arp_stats_params arp_stats_params = {0}; |
| int err = 0; |
| mac_handle_t mac_handle; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| err = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != err) |
| return err; |
| |
| if (adapter->vdev_id == WLAN_UMAC_VDEV_ID_MAX) { |
| hdd_err("Invalid vdev id"); |
| return -EINVAL; |
| } |
| |
| if (adapter->device_mode != QDF_STA_MODE) { |
| hdd_err("STATS supported in only STA mode!"); |
| return -EINVAL; |
| } |
| |
| if (!hdd_cm_is_vdev_associated(adapter)) { |
| hdd_debug("Not Associated"); |
| return 0; |
| } |
| |
| if (hdd_is_roaming_in_progress(hdd_ctx)) |
| return -EINVAL; |
| |
| err = wlan_cfg80211_nla_parse(tb, STATS_SET_MAX, data, data_len, |
| qca_wlan_vendor_set_nud_stats_policy); |
| if (err) { |
| hdd_err("STATS_SET_START ATTR"); |
| return err; |
| } |
| |
| if (tb[STATS_SET_START]) { |
| /* tracking is enabled for stats other than arp. */ |
| if (tb[STATS_SET_DATA_PKT_INFO]) { |
| err = hdd_set_clear_connectivity_check_stats_info( |
| adapter, |
| &arp_stats_params, tb, true); |
| if (err) |
| return -EINVAL; |
| |
| /* |
| * if only tracking dns, then don't send |
| * wmi command to FW. |
| */ |
| if (!arp_stats_params.pkt_type_bitmap) |
| return err; |
| } else { |
| if (!tb[STATS_GW_IPV4]) { |
| hdd_err("STATS_SET_START CMD"); |
| return -EINVAL; |
| } |
| |
| arp_stats_params.pkt_type_bitmap = |
| CONNECTIVITY_CHECK_SET_ARP; |
| adapter->pkt_type_bitmap |= |
| arp_stats_params.pkt_type_bitmap; |
| arp_stats_params.flag = true; |
| arp_stats_params.ip_addr = |
| nla_get_u32(tb[STATS_GW_IPV4]); |
| adapter->track_arp_ip = arp_stats_params.ip_addr; |
| arp_stats_params.pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE; |
| } |
| } else { |
| /* clear stats command received. */ |
| if (tb[STATS_SET_DATA_PKT_INFO]) { |
| err = hdd_set_clear_connectivity_check_stats_info( |
| adapter, |
| &arp_stats_params, tb, false); |
| if (err) |
| return -EINVAL; |
| |
| /* |
| * if only tracking dns, then don't send |
| * wmi command to FW. |
| */ |
| if (!arp_stats_params.pkt_type_bitmap) |
| return err; |
| } else { |
| arp_stats_params.pkt_type_bitmap = |
| CONNECTIVITY_CHECK_SET_ARP; |
| adapter->pkt_type_bitmap &= |
| (~arp_stats_params.pkt_type_bitmap); |
| arp_stats_params.flag = false; |
| qdf_mem_zero(&adapter->hdd_stats.hdd_arp_stats, |
| sizeof(adapter->hdd_stats.hdd_arp_stats)); |
| arp_stats_params.pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE; |
| } |
| } |
| |
| hdd_debug("STATS_SET_START Received flag %d!", arp_stats_params.flag); |
| |
| arp_stats_params.vdev_id = adapter->vdev_id; |
| |
| mac_handle = hdd_ctx->mac_handle; |
| if (QDF_STATUS_SUCCESS != |
| sme_set_nud_debug_stats(mac_handle, &arp_stats_params)) { |
| hdd_err("STATS_SET_START CMD Failed!"); |
| return -EINVAL; |
| } |
| |
| hdd_exit(); |
| |
| return err; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_nud_stats() - set arp stats command to firmware |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: pointer to apfind configuration data. |
| * @data_len: the length in byte of apfind data. |
| * |
| * This is called when wlan driver needs to send arp stats to |
| * firmware. |
| * |
| * Return: An error code or 0 on success. |
| */ |
| static int wlan_hdd_cfg80211_set_nud_stats(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_nud_stats(wiphy, wdev, data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #undef STATS_SET_INVALID |
| #undef STATS_SET_START |
| #undef STATS_GW_IPV4 |
| #undef STATS_SET_MAX |
| |
| /* |
| * define short names for the global vendor params |
| * used by wlan_hdd_cfg80211_setarp_stats_cmd() |
| */ |
| #define STATS_GET_INVALID \ |
| QCA_ATTR_NUD_STATS_SET_INVALID |
| #define COUNT_FROM_NETDEV \ |
| QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV |
| #define COUNT_TO_LOWER_MAC \ |
| QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC |
| #define RX_COUNT_BY_LOWER_MAC \ |
| QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC |
| #define COUNT_TX_SUCCESS \ |
| QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS |
| #define RSP_RX_COUNT_BY_LOWER_MAC \ |
| QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC |
| #define RSP_RX_COUNT_BY_UPPER_MAC \ |
| QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC |
| #define RSP_COUNT_TO_NETDEV \ |
| QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV |
| #define RSP_COUNT_OUT_OF_ORDER_DROP \ |
| QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP |
| #define AP_LINK_ACTIVE \ |
| QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE |
| #define AP_LINK_DAD \ |
| QCA_ATTR_NUD_STATS_IS_DAD |
| #define DATA_PKT_STATS \ |
| QCA_ATTR_NUD_STATS_DATA_PKT_STATS |
| #define STATS_GET_MAX \ |
| QCA_ATTR_NUD_STATS_GET_MAX |
| |
| #define CHECK_STATS_INVALID \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_INVALID |
| #define CHECK_STATS_PKT_TYPE \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_TYPE |
| #define CHECK_STATS_PKT_DNS_DOMAIN_NAME \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DNS_DOMAIN_NAME |
| #define CHECK_STATS_PKT_SRC_PORT \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_SRC_PORT |
| #define CHECK_STATS_PKT_DEST_PORT \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DEST_PORT |
| #define CHECK_STATS_PKT_DEST_IPV4 \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DEST_IPV4 |
| #define CHECK_STATS_PKT_DEST_IPV6 \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DEST_IPV6 |
| #define CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV |
| #define CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC |
| #define CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC |
| #define CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS |
| #define CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC |
| #define CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC |
| #define CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV |
| #define CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP \ |
| QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP |
| #define CHECK_DATA_STATS_MAX \ |
| QCA_ATTR_CONNECTIVITY_CHECK_DATA_STATS_MAX |
| |
| |
| const struct nla_policy |
| qca_wlan_vendor_get_nud_stats[STATS_GET_MAX + 1] = { |
| [COUNT_FROM_NETDEV] = {.type = NLA_U16 }, |
| [COUNT_TO_LOWER_MAC] = {.type = NLA_U16 }, |
| [RX_COUNT_BY_LOWER_MAC] = {.type = NLA_U16 }, |
| [COUNT_TX_SUCCESS] = {.type = NLA_U16 }, |
| [RSP_RX_COUNT_BY_LOWER_MAC] = {.type = NLA_U16 }, |
| [RSP_RX_COUNT_BY_UPPER_MAC] = {.type = NLA_U16 }, |
| [RSP_COUNT_TO_NETDEV] = {.type = NLA_U16 }, |
| [RSP_COUNT_OUT_OF_ORDER_DROP] = {.type = NLA_U16 }, |
| [AP_LINK_ACTIVE] = {.type = NLA_FLAG }, |
| [AP_LINK_DAD] = {.type = NLA_FLAG }, |
| [DATA_PKT_STATS] = {.type = NLA_U16 }, |
| }; |
| |
| /** |
| * hdd_populate_dns_stats_info() - send dns stats info to network stack |
| * @adapter: pointer to adapter context |
| * @skb: pointer to skb |
| * |
| * |
| * Return: An error code or 0 on success. |
| */ |
| static int hdd_populate_dns_stats_info(struct hdd_adapter *adapter, |
| struct sk_buff *skb) |
| { |
| uint8_t *dns_query; |
| |
| dns_query = qdf_mem_malloc(adapter->track_dns_domain_len + 1); |
| if (!dns_query) |
| return -EINVAL; |
| |
| qdf_mem_copy(dns_query, adapter->dns_payload, |
| adapter->track_dns_domain_len); |
| |
| if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE, |
| CONNECTIVITY_CHECK_SET_DNS) || |
| nla_put(skb, CHECK_STATS_PKT_DNS_DOMAIN_NAME, |
| adapter->track_dns_domain_len, |
| hdd_dns_unmake_name_query(dns_query)) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV, |
| adapter->hdd_stats.hdd_dns_stats.tx_dns_req_count) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC, |
| adapter->hdd_stats.hdd_dns_stats.tx_host_fw_sent) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC, |
| adapter->hdd_stats.hdd_dns_stats.tx_host_fw_sent) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS, |
| adapter->hdd_stats.hdd_dns_stats.tx_ack_cnt) || |
| nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC, |
| adapter->hdd_stats.hdd_dns_stats.rx_dns_rsp_count) || |
| nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV, |
| adapter->hdd_stats.hdd_dns_stats.rx_delivered) || |
| nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP, |
| adapter->hdd_stats.hdd_dns_stats.rx_host_drop)) { |
| hdd_err("nla put fail"); |
| qdf_mem_free(dns_query); |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| qdf_mem_free(dns_query); |
| return 0; |
| } |
| |
| /** |
| * hdd_populate_tcp_stats_info() - send tcp stats info to network stack |
| * @adapter: pointer to adapter context |
| * @skb: pointer to skb |
| * @pkt_type: tcp pkt type |
| * |
| * Return: An error code or 0 on success. |
| */ |
| static int hdd_populate_tcp_stats_info(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| uint8_t pkt_type) |
| { |
| switch (pkt_type) { |
| case CONNECTIVITY_CHECK_SET_TCP_SYN: |
| /* Fill info for tcp syn packets (tx packet) */ |
| if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE, |
| CONNECTIVITY_CHECK_SET_TCP_SYN) || |
| nla_put_u16(skb, CHECK_STATS_PKT_SRC_PORT, |
| adapter->track_src_port) || |
| nla_put_u16(skb, CHECK_STATS_PKT_DEST_PORT, |
| adapter->track_dest_port) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV, |
| adapter->hdd_stats.hdd_tcp_stats.tx_tcp_syn_count) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC, |
| adapter->hdd_stats.hdd_tcp_stats. |
| tx_tcp_syn_host_fw_sent) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC, |
| adapter->hdd_stats.hdd_tcp_stats. |
| tx_tcp_syn_host_fw_sent) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS, |
| adapter->hdd_stats.hdd_tcp_stats.tx_tcp_syn_ack_cnt)) { |
| hdd_err("nla put fail"); |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| break; |
| case CONNECTIVITY_CHECK_SET_TCP_SYN_ACK: |
| /* Fill info for tcp syn-ack packets (rx packet) */ |
| if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE, |
| CONNECTIVITY_CHECK_SET_TCP_SYN_ACK) || |
| nla_put_u16(skb, CHECK_STATS_PKT_SRC_PORT, |
| adapter->track_src_port) || |
| nla_put_u16(skb, CHECK_STATS_PKT_DEST_PORT, |
| adapter->track_dest_port) || |
| nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC, |
| adapter->hdd_stats.hdd_tcp_stats.rx_fw_cnt) || |
| nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC, |
| adapter->hdd_stats.hdd_tcp_stats. |
| rx_tcp_syn_ack_count) || |
| nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV, |
| adapter->hdd_stats.hdd_tcp_stats.rx_delivered) || |
| nla_put_u16(skb, |
| CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP, |
| adapter->hdd_stats.hdd_tcp_stats.rx_host_drop)) { |
| hdd_err("nla put fail"); |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| break; |
| case CONNECTIVITY_CHECK_SET_TCP_ACK: |
| /* Fill info for tcp ack packets (tx packet) */ |
| if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE, |
| CONNECTIVITY_CHECK_SET_TCP_ACK) || |
| nla_put_u16(skb, CHECK_STATS_PKT_SRC_PORT, |
| adapter->track_src_port) || |
| nla_put_u16(skb, CHECK_STATS_PKT_DEST_PORT, |
| adapter->track_dest_port) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV, |
| adapter->hdd_stats.hdd_tcp_stats.tx_tcp_ack_count) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC, |
| adapter->hdd_stats.hdd_tcp_stats. |
| tx_tcp_ack_host_fw_sent) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC, |
| adapter->hdd_stats.hdd_tcp_stats. |
| tx_tcp_ack_host_fw_sent) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS, |
| adapter->hdd_stats.hdd_tcp_stats.tx_tcp_ack_ack_cnt)) { |
| hdd_err("nla put fail"); |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| break; |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| /** |
| * hdd_populate_icmpv4_stats_info() - send icmpv4 stats info to network stack |
| * @adapter: pointer to adapter context |
| * @skb: pointer to skb |
| * |
| * |
| * Return: An error code or 0 on success. |
| */ |
| static int hdd_populate_icmpv4_stats_info(struct hdd_adapter *adapter, |
| struct sk_buff *skb) |
| { |
| if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE, |
| CONNECTIVITY_CHECK_SET_ICMPV4) || |
| nla_put_u32(skb, CHECK_STATS_PKT_DEST_IPV4, |
| adapter->track_dest_ipv4) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV, |
| adapter->hdd_stats.hdd_icmpv4_stats.tx_icmpv4_req_count) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC, |
| adapter->hdd_stats.hdd_icmpv4_stats.tx_host_fw_sent) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC, |
| adapter->hdd_stats.hdd_icmpv4_stats.tx_host_fw_sent) || |
| nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS, |
| adapter->hdd_stats.hdd_icmpv4_stats.tx_ack_cnt) || |
| nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC, |
| adapter->hdd_stats.hdd_icmpv4_stats.rx_fw_cnt) || |
| nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC, |
| adapter->hdd_stats.hdd_icmpv4_stats.rx_icmpv4_rsp_count) || |
| nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV, |
| adapter->hdd_stats.hdd_icmpv4_stats.rx_delivered) || |
| nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP, |
| adapter->hdd_stats.hdd_icmpv4_stats.rx_host_drop)) { |
| hdd_err("nla put fail"); |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| /** |
| * hdd_populate_connectivity_check_stats_info() - send connectivity stats info |
| * to network stack |
| * @adapter: pointer to adapter context |
| * @skb: pointer to skb |
| * |
| * |
| * Return: An error code or 0 on success. |
| */ |
| |
| static int hdd_populate_connectivity_check_stats_info( |
| struct hdd_adapter *adapter, struct sk_buff *skb) |
| { |
| struct nlattr *connect_stats, *connect_info; |
| uint32_t count = 0; |
| |
| connect_stats = nla_nest_start(skb, DATA_PKT_STATS); |
| if (!connect_stats) { |
| hdd_err("nla_nest_start failed"); |
| return -EINVAL; |
| } |
| |
| if (adapter->pkt_type_bitmap & CONNECTIVITY_CHECK_SET_DNS) { |
| connect_info = nla_nest_start(skb, count); |
| if (!connect_info) { |
| hdd_err("nla_nest_start failed count %u", count); |
| return -EINVAL; |
| } |
| |
| if (hdd_populate_dns_stats_info(adapter, skb)) |
| goto put_attr_fail; |
| nla_nest_end(skb, connect_info); |
| count++; |
| } |
| |
| if (adapter->pkt_type_bitmap & CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE) { |
| connect_info = nla_nest_start(skb, count); |
| if (!connect_info) { |
| hdd_err("nla_nest_start failed count %u", count); |
| return -EINVAL; |
| } |
| if (hdd_populate_tcp_stats_info(adapter, skb, |
| CONNECTIVITY_CHECK_SET_TCP_SYN)) |
| goto put_attr_fail; |
| nla_nest_end(skb, connect_info); |
| count++; |
| |
| connect_info = nla_nest_start(skb, count); |
| if (!connect_info) { |
| hdd_err("nla_nest_start failed count %u", count); |
| return -EINVAL; |
| } |
| if (hdd_populate_tcp_stats_info(adapter, skb, |
| CONNECTIVITY_CHECK_SET_TCP_SYN_ACK)) |
| goto put_attr_fail; |
| nla_nest_end(skb, connect_info); |
| count++; |
| |
| connect_info = nla_nest_start(skb, count); |
| if (!connect_info) { |
| hdd_err("nla_nest_start failed count %u", count); |
| return -EINVAL; |
| } |
| if (hdd_populate_tcp_stats_info(adapter, skb, |
| CONNECTIVITY_CHECK_SET_TCP_ACK)) |
| goto put_attr_fail; |
| nla_nest_end(skb, connect_info); |
| count++; |
| } |
| |
| if (adapter->pkt_type_bitmap & CONNECTIVITY_CHECK_SET_ICMPV4) { |
| connect_info = nla_nest_start(skb, count); |
| if (!connect_info) { |
| hdd_err("nla_nest_start failed count %u", count); |
| return -EINVAL; |
| } |
| |
| if (hdd_populate_icmpv4_stats_info(adapter, skb)) |
| goto put_attr_fail; |
| nla_nest_end(skb, connect_info); |
| count++; |
| } |
| |
| nla_nest_end(skb, connect_stats); |
| return 0; |
| |
| put_attr_fail: |
| hdd_err("QCA_WLAN_VENDOR_ATTR put fail. count %u", count); |
| return -EINVAL; |
| } |
| |
| |
| /** |
| * __wlan_hdd_cfg80211_get_nud_stats() - get arp stats command to firmware |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: pointer to apfind configuration data. |
| * @data_len: the length in byte of apfind data. |
| * |
| * This is called when wlan driver needs to get arp stats to |
| * firmware. |
| * |
| * Return: An error code or 0 on success. |
| */ |
| static int __wlan_hdd_cfg80211_get_nud_stats(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int err = 0; |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct get_arp_stats_params arp_stats_params; |
| mac_handle_t mac_handle; |
| void *soc = cds_get_context(QDF_MODULE_ID_SOC); |
| uint32_t pkt_type_bitmap; |
| struct sk_buff *skb; |
| struct osif_request *request = NULL; |
| static const struct osif_request_params params = { |
| .priv_size = 0, |
| .timeout_ms = WLAN_WAIT_TIME_NUD_STATS, |
| }; |
| void *cookie = NULL; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| err = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != err) |
| return err; |
| |
| err = hdd_validate_adapter(adapter); |
| if (err) |
| return err; |
| |
| if (adapter->device_mode != QDF_STA_MODE) { |
| hdd_err("STATS supported in only STA mode!"); |
| return -EINVAL; |
| } |
| |
| request = osif_request_alloc(¶ms); |
| if (!request) { |
| hdd_err("Request allocation failure"); |
| return -ENOMEM; |
| } |
| |
| cookie = osif_request_cookie(request); |
| |
| arp_stats_params.pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE; |
| arp_stats_params.vdev_id = adapter->vdev_id; |
| |
| pkt_type_bitmap = adapter->pkt_type_bitmap; |
| |
| /* send NUD failure event only when ARP tracking is enabled. */ |
| if (cdp_cfg_get(soc, cfg_dp_enable_data_stall) && |
| !hdd_ctx->config->enable_nud_tracking && |
| (pkt_type_bitmap & CONNECTIVITY_CHECK_SET_ARP)) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, |
| "Data stall due to NUD failure"); |
| cdp_post_data_stall_event(soc, |
| DATA_STALL_LOG_INDICATOR_FRAMEWORK, |
| DATA_STALL_LOG_NUD_FAILURE, |
| OL_TXRX_PDEV_ID, 0XFF, |
| DATA_STALL_LOG_RECOVERY_TRIGGER_PDR); |
| } |
| |
| mac_handle = hdd_ctx->mac_handle; |
| if (sme_set_nud_debug_stats_cb(mac_handle, hdd_get_nud_stats_cb, |
| cookie) != QDF_STATUS_SUCCESS) { |
| hdd_err("Setting NUD debug stats callback failure"); |
| err = -EINVAL; |
| goto exit; |
| } |
| |
| if (QDF_STATUS_SUCCESS != |
| sme_get_nud_debug_stats(mac_handle, &arp_stats_params)) { |
| hdd_err("STATS_SET_START CMD Failed!"); |
| err = -EINVAL; |
| goto exit; |
| } |
| |
| err = osif_request_wait_for_response(request); |
| if (err) { |
| hdd_err("SME timedout while retrieving NUD stats"); |
| err = -ETIMEDOUT; |
| goto exit; |
| } |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, |
| WLAN_NUD_STATS_LEN); |
| if (!skb) { |
| hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| err = -ENOMEM; |
| goto exit; |
| } |
| |
| if (nla_put_u16(skb, COUNT_FROM_NETDEV, |
| adapter->hdd_stats.hdd_arp_stats.tx_arp_req_count) || |
| nla_put_u16(skb, COUNT_TO_LOWER_MAC, |
| adapter->hdd_stats.hdd_arp_stats.tx_host_fw_sent) || |
| nla_put_u16(skb, RX_COUNT_BY_LOWER_MAC, |
| adapter->hdd_stats.hdd_arp_stats.tx_host_fw_sent) || |
| nla_put_u16(skb, COUNT_TX_SUCCESS, |
| adapter->hdd_stats.hdd_arp_stats.tx_ack_cnt) || |
| nla_put_u16(skb, RSP_RX_COUNT_BY_LOWER_MAC, |
| adapter->hdd_stats.hdd_arp_stats.rx_fw_cnt) || |
| nla_put_u16(skb, RSP_RX_COUNT_BY_UPPER_MAC, |
| adapter->hdd_stats.hdd_arp_stats.rx_arp_rsp_count) || |
| nla_put_u16(skb, RSP_COUNT_TO_NETDEV, |
| adapter->hdd_stats.hdd_arp_stats.rx_delivered) || |
| nla_put_u16(skb, RSP_COUNT_OUT_OF_ORDER_DROP, |
| adapter->hdd_stats.hdd_arp_stats. |
| rx_host_drop_reorder)) { |
| hdd_err("nla put fail"); |
| kfree_skb(skb); |
| err = -EINVAL; |
| goto exit; |
| } |
| if (adapter->con_status) |
| nla_put_flag(skb, AP_LINK_ACTIVE); |
| if (adapter->dad) |
| nla_put_flag(skb, AP_LINK_DAD); |
| |
| /* ARP tracking is done above. */ |
| pkt_type_bitmap &= ~CONNECTIVITY_CHECK_SET_ARP; |
| |
| if (pkt_type_bitmap) { |
| if (hdd_populate_connectivity_check_stats_info(adapter, skb)) { |
| err = -EINVAL; |
| goto exit; |
| } |
| } |
| |
| cfg80211_vendor_cmd_reply(skb); |
| exit: |
| osif_request_put(request); |
| return err; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_get_nud_stats() - get arp stats command to firmware |
| * @wiphy: pointer to wireless wiphy structure. |
| * @wdev: pointer to wireless_dev structure. |
| * @data: pointer to apfind configuration data. |
| * @data_len: the length in byte of apfind data. |
| * |
| * This is called when wlan driver needs to get arp stats to |
| * firmware. |
| * |
| * Return: An error code or 0 on success. |
| */ |
| static int wlan_hdd_cfg80211_get_nud_stats(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_nud_stats(wiphy, wdev, data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #undef QCA_ATTR_NUD_STATS_SET_INVALID |
| #undef QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV |
| #undef QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC |
| #undef QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC |
| #undef QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS |
| #undef QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC |
| #undef QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC |
| #undef QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV |
| #undef QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP |
| #undef QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE |
| #undef QCA_ATTR_NUD_STATS_GET_MAX |
| |
| void hdd_bt_activity_cb(hdd_handle_t hdd_handle, uint32_t bt_activity) |
| { |
| struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); |
| int status; |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return; |
| |
| if (bt_activity == WLAN_COEX_EVENT_BT_A2DP_PROFILE_ADD) |
| hdd_ctx->bt_a2dp_active = 1; |
| else if (bt_activity == WLAN_COEX_EVENT_BT_A2DP_PROFILE_REMOVE) |
| hdd_ctx->bt_a2dp_active = 0; |
| else if (bt_activity == WLAN_COEX_EVENT_BT_VOICE_PROFILE_ADD) |
| hdd_ctx->bt_vo_active = 1; |
| else if (bt_activity == WLAN_COEX_EVENT_BT_VOICE_PROFILE_REMOVE) |
| hdd_ctx->bt_vo_active = 0; |
| else if (bt_activity == WLAN_COEX_EVENT_BT_PROFILE_CONNECTED) |
| hdd_ctx->bt_profile_con = 1; |
| else if (bt_activity == WLAN_COEX_EVENT_BT_PROFILE_DISCONNECTED) |
| hdd_ctx->bt_profile_con = 0; |
| else |
| return; |
| |
| ucfg_scan_set_bt_activity(hdd_ctx->psoc, hdd_ctx->bt_a2dp_active); |
| hdd_debug("a2dp_active: %d vo_active: %d connected:%d", |
| hdd_ctx->bt_a2dp_active, |
| hdd_ctx->bt_vo_active, hdd_ctx->bt_profile_con); |
| } |
| |
| struct chain_rssi_priv { |
| struct chain_rssi_result chain_rssi; |
| }; |
| |
| /** |
| * hdd_get_chain_rssi_cb() - Callback function to get chain rssi |
| * @context: opaque context originally passed to SME. HDD always passes |
| * a cookie for the request context |
| * @data: struct for get chain rssi |
| * |
| * This function receives the response/data from the lower layer and |
| * checks to see if the thread is still waiting then post the results to |
| * upper layer, if the request has timed out then ignore. |
| * |
| * Return: None |
| */ |
| static void hdd_get_chain_rssi_cb(void *context, |
| struct chain_rssi_result *data) |
| { |
| struct osif_request *request; |
| struct chain_rssi_priv *priv; |
| |
| hdd_enter(); |
| |
| request = osif_request_get(context); |
| if (!request) { |
| hdd_err("Obsolete request"); |
| return; |
| } |
| |
| priv = osif_request_priv(request); |
| priv->chain_rssi = *data; |
| osif_request_complete(request); |
| osif_request_put(request); |
| } |
| |
| /** |
| * hdd_post_get_chain_rssi_rsp - send rsp to user space |
| * @hdd_ctx: pointer to hdd context |
| * @result: chain rssi result |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int hdd_post_get_chain_rssi_rsp(struct hdd_context *hdd_ctx, |
| struct chain_rssi_result *result) |
| { |
| struct sk_buff *skb; |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, |
| (sizeof(result->chain_rssi) + NLA_HDRLEN) + |
| (sizeof(result->chain_evm) + NLA_HDRLEN) + |
| (sizeof(result->ant_id) + NLA_HDRLEN) + |
| NLMSG_HDRLEN); |
| |
| if (!skb) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return -ENOMEM; |
| } |
| |
| if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI, |
| sizeof(result->chain_rssi), |
| result->chain_rssi)) { |
| hdd_err("put fail"); |
| goto nla_put_failure; |
| } |
| |
| if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_CHAIN_EVM, |
| sizeof(result->chain_evm), |
| result->chain_evm)) { |
| hdd_err("put fail"); |
| goto nla_put_failure; |
| } |
| |
| if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO, |
| sizeof(result->ant_id), |
| result->ant_id)) { |
| hdd_err("put fail"); |
| goto nla_put_failure; |
| } |
| |
| cfg80211_vendor_cmd_reply(skb); |
| return 0; |
| |
| nla_put_failure: |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| static const struct |
| nla_policy get_chain_rssi_policy[QCA_WLAN_VENDOR_ATTR_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_MAC_ADDR] = {.type = NLA_BINARY, |
| .len = QDF_MAC_ADDR_SIZE}, |
| }; |
| |
| static const struct nla_policy |
| get_chan_info[QCA_WLAN_VENDOR_ATTR_CHAN_INFO_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_CHAN_INFO_INVALID] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CHAN_INFO_PRIMARY_FREQ] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CHAN_INFO_SEG0_FREQ] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CHAN_INFO_SEG1_FREQ] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CHAN_INFO_BANDWIDTH] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CHAN_INFO_IFACE_MODE_MASK] = {.type = NLA_U32}, |
| }; |
| |
| static const struct nla_policy |
| get_usable_channel_policy[QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_INVALID] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_BAND_MASK] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_IFACE_MODE_MASK] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_FILTER_MASK] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_CHAN_INFO] = { |
| .type = NLA_NESTED |
| }, |
| }; |
| |
| #ifdef WLAN_FEATURE_GET_USABLE_CHAN_LIST |
| static enum nl80211_chan_width |
| hdd_convert_phy_bw_to_nl_bw(enum phy_ch_width bw) |
| { |
| switch (bw) { |
| case CH_WIDTH_20MHZ: |
| return NL80211_CHAN_WIDTH_20; |
| case CH_WIDTH_40MHZ: |
| return NL80211_CHAN_WIDTH_40; |
| case CH_WIDTH_160MHZ: |
| return NL80211_CHAN_WIDTH_160; |
| case CH_WIDTH_80MHZ: |
| return NL80211_CHAN_WIDTH_80; |
| case CH_WIDTH_80P80MHZ: |
| return NL80211_CHAN_WIDTH_80P80; |
| case CH_WIDTH_5MHZ: |
| return NL80211_CHAN_WIDTH_5; |
| case CH_WIDTH_10MHZ: |
| return NL80211_CHAN_WIDTH_10; |
| #if defined(WLAN_FEATURE_11BE) |
| #if defined(CFG80211_11BE_BASIC) |
| case CH_WIDTH_320MHZ: |
| return NL80211_CHAN_WIDTH_320; |
| #else |
| case CH_WIDTH_320MHZ: |
| return NL80211_CHAN_WIDTH_20; |
| #endif |
| #endif |
| case CH_WIDTH_INVALID: |
| case CH_WIDTH_MAX: |
| return NL80211_CHAN_WIDTH_20; |
| } |
| |
| return NL80211_CHAN_WIDTH_20; |
| } |
| |
| /** |
| * hdd_fill_usable_channels_data() - Fill the data requested by userspace |
| * @skb: SK buffer |
| * @tb: List of attributes |
| * @res_msg: structure of usable channel info |
| * @count: no of usable channels |
| * |
| * Get the data corresponding to the attribute list specified in tb and |
| * update the same to skb by populating the same attributes. |
| * |
| * Return: 0 on success; error number on failure |
| */ |
| static int |
| hdd_fill_usable_channels_data(struct sk_buff *skb, struct nlattr **tb, |
| struct get_usable_chan_res_params *res_msg, |
| int count) |
| { |
| struct nlattr *config, *chan_params; |
| uint8_t i, bw, j = 0; |
| |
| config = nla_nest_start(skb, |
| QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_CHAN_INFO); |
| if (!config) { |
| hdd_err("nla nest start failure"); |
| return -EINVAL; |
| } |
| for (i = 0; i < count ; i++) { |
| if (!res_msg[i].freq) |
| continue; |
| chan_params = nla_nest_start(skb, j); |
| if (!chan_params) |
| return -EINVAL; |
| j++; |
| bw = hdd_convert_phy_bw_to_nl_bw(res_msg[i].bw); |
| hdd_debug("populating chan_params freq %d bw %d iface mode %d, seg0 %d", |
| res_msg[i].freq, bw, res_msg[i].iface_mode_mask, |
| res_msg[i].seg0_freq); |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_CHAN_INFO_PRIMARY_FREQ, |
| res_msg[i].freq) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_CHAN_INFO_SEG0_FREQ, |
| res_msg[i].seg0_freq) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_CHAN_INFO_SEG1_FREQ, |
| res_msg[i].seg1_freq) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_CHAN_INFO_BANDWIDTH, |
| bw) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_CHAN_INFO_IFACE_MODE_MASK, |
| res_msg[i].iface_mode_mask)) { |
| hdd_err("nla put failre"); |
| return -EINVAL; |
| } |
| |
| nla_nest_end(skb, chan_params); |
| } |
| nla_nest_end(skb, config); |
| return 0; |
| } |
| |
| /** |
| * hdd_get_usable_cahnnel_len() - calculate the length required by skb |
| * @count: number of usable channels |
| * |
| * Find the required length to send usable channel data to upper layer |
| * |
| * Return: required len |
| */ |
| static uint32_t |
| hdd_get_usable_cahnnel_len(uint32_t count) |
| { |
| uint32_t len = 0; |
| struct get_usable_chan_res_params res_msg; |
| |
| len = nla_total_size(sizeof(res_msg.freq)) + |
| nla_total_size(sizeof(res_msg.seg0_freq)) + |
| nla_total_size(sizeof(res_msg.seg1_freq)) + |
| nla_total_size(sizeof(res_msg.bw)) + |
| nla_total_size(sizeof(res_msg.iface_mode_mask)); |
| |
| return len * count; |
| } |
| |
| /** |
| * hdd_send_usable_channel() - Send usable channels as vendor cmd reply |
| * @mac_handle: Opaque handle to the MAC context |
| * @vdev_id: vdev id |
| * @tb: List of attributes |
| * |
| * Parse the attributes list tb and get the data corresponding to the |
| * attributes specified in tb. Send them as a vendor response. |
| * |
| * Return: 0 on success; error number on failure |
| */ |
| static int |
| hdd_send_usable_channel(struct hdd_context *hdd_ctx, |
| struct get_usable_chan_res_params *res_msg, |
| uint32_t count, |
| struct nlattr **tb) |
| { |
| struct sk_buff *skb; |
| uint32_t skb_len; |
| int status; |
| |
| skb_len = hdd_get_usable_cahnnel_len(count); |
| if (!skb_len) { |
| hdd_err("No data requested"); |
| return -EINVAL; |
| } |
| |
| skb_len += NLMSG_HDRLEN; |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, skb_len); |
| if (!skb) { |
| hdd_info("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| return -ENOMEM; |
| } |
| |
| status = hdd_fill_usable_channels_data(skb, tb, res_msg, count); |
| if (status) |
| goto fail; |
| |
| return cfg80211_vendor_cmd_reply(skb); |
| |
| fail: |
| hdd_err("nla put fail"); |
| kfree_skb(skb); |
| return status; |
| } |
| |
| /** |
| * hdd_get_all_band_mask() - get supported nl80211 bands |
| * |
| * Return: supported band mask |
| */ |
| static uint32_t |
| hdd_get_all_band_mask(void) |
| { |
| uint32_t band_mask = 0; |
| |
| band_mask = |
| (1 << REG_BAND_2G) | (1 << REG_BAND_5G) | (1 << REG_BAND_6G); |
| |
| return band_mask; |
| } |
| |
| /** |
| * hdd_get_all_iface_mode_mask() - get supported nl80211 iface mode |
| * |
| * Return: supported iface mode mask |
| */ |
| static uint32_t |
| hdd_get_all_iface_mode_mask(void) |
| { |
| uint32_t mode_mask = 0; |
| |
| mode_mask = (1 << NL80211_IFTYPE_STATION) | |
| (1 << NL80211_IFTYPE_AP) | |
| (1 << NL80211_IFTYPE_P2P_GO) | |
| (1 << NL80211_IFTYPE_P2P_CLIENT) | |
| (1 << NL80211_IFTYPE_P2P_DEVICE) | |
| (1 << NL80211_IFTYPE_NAN); |
| |
| return mode_mask; |
| } |
| |
| /** |
| * hdd_convert_nl80211_to_reg_band_mask() - convert n80211 band to reg band |
| * @band: nl80211 band |
| * |
| * Return: reg band value |
| */ |
| |
| static uint32_t |
| hdd_convert_nl80211_to_reg_band_mask(enum nl80211_band band) |
| { |
| uint32_t reg_band = 0; |
| |
| if (band & 1 << NL80211_BAND_2GHZ) |
| reg_band |= 1 << REG_BAND_2G; |
| if (band & 1 << NL80211_BAND_5GHZ) |
| reg_band |= 1 << REG_BAND_5G; |
| if (band & 1 << NL80211_BAND_6GHZ) |
| reg_band |= 1 << REG_BAND_6G; |
| if (band & 1 << NL80211_BAND_60GHZ) |
| hdd_err("band: %d not supported", NL80211_BAND_60GHZ); |
| |
| return reg_band; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_get_usable_channel() - get chain rssi |
| * @wiphy: wiphy pointer |
| * @wdev: pointer to struct wireless_dev |
| * @data: pointer to incoming NL vendor data |
| * @data_len: length of @data |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| static int __wlan_hdd_cfg80211_get_usable_channel(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct get_usable_chan_req_params req_msg = {0}; |
| struct get_usable_chan_res_params *res_msg; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_MAX + 1]; |
| int ret = 0; |
| uint32_t count = 0; |
| QDF_STATUS status; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| res_msg = qdf_mem_malloc(NUM_CHANNELS * |
| sizeof(*res_msg)); |
| |
| if (!res_msg) { |
| hdd_err("res_msg invalid"); |
| return -EINVAL; |
| } |
| |
| if (wlan_cfg80211_nla_parse( |
| tb, QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_MAX, |
| data, data_len, get_usable_channel_policy)) { |
| hdd_err("Invalid ATTR"); |
| ret = -EINVAL; |
| goto err; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_BAND_MASK]) { |
| hdd_err("band mask not present"); |
| req_msg.band_mask = hdd_get_all_band_mask(); |
| } else { |
| req_msg.band_mask = |
| nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_BAND_MASK]); |
| if (!req_msg.band_mask) |
| req_msg.band_mask = hdd_get_all_band_mask(); |
| else |
| req_msg.band_mask = |
| hdd_convert_nl80211_to_reg_band_mask(req_msg.band_mask); |
| } |
| if (!tb[QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_IFACE_MODE_MASK]) { |
| hdd_err("iface mode mask not present"); |
| req_msg.iface_mode_mask = hdd_get_all_iface_mode_mask(); |
| } else { |
| req_msg.iface_mode_mask = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_IFACE_MODE_MASK]); |
| if (!req_msg.iface_mode_mask) |
| req_msg.iface_mode_mask = hdd_get_all_iface_mode_mask(); |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_FILTER_MASK]) { |
| hdd_err("usable channels filter mask not present"); |
| req_msg.filter_mask = 0; |
| } else { |
| req_msg.filter_mask = |
| nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_FILTER_MASK]); |
| } |
| |
| hdd_debug("get usable channel list for band %d mode %d filter %d", |
| req_msg.band_mask, req_msg.iface_mode_mask, |
| req_msg.filter_mask); |
| |
| status = wlan_reg_get_usable_channel(hdd_ctx->pdev, req_msg, |
| res_msg, &count); |
| if (QDF_STATUS_SUCCESS != status) { |
| hdd_err("get usable channel failed %d", status); |
| ret = -EINVAL; |
| goto err; |
| } |
| hdd_debug("usable channel count : %d", count); |
| |
| ret = hdd_send_usable_channel(hdd_ctx, res_msg, count, tb); |
| if (ret) { |
| hdd_err("failed to send usable_channels"); |
| ret = -EINVAL; |
| goto err; |
| } |
| |
| err: |
| qdf_mem_free(res_msg); |
| if (ret) |
| return ret; |
| return qdf_status_to_os_return(status); |
| } |
| #endif |
| |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| /** |
| * enum roam_stats_set_params - Different types of params to set the roam stats |
| * @ROAM_RT_STATS_DISABLED: Roam stats feature disabled |
| * @ROAM_RT_STATS_ENABLED: Roam stats feature enabled |
| * @ROAM_RT_STATS_ENABLED_IN_SUSPEND_MODE: Roam stats enabled in suspend mode |
| */ |
| enum roam_stats_set_params { |
| ROAM_RT_STATS_DISABLED = 0, |
| ROAM_RT_STATS_ENABLED = 1, |
| ROAM_RT_STATS_ENABLED_IN_SUSPEND_MODE = 2, |
| }; |
| |
| #define EVENTS_CONFIGURE QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CONFIGURE |
| #define SUSPEND_STATE QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_SUSPEND_STATE |
| |
| static const struct nla_policy |
| set_roam_events_policy[QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CONFIGURE] = {.type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_SUSPEND_STATE] = {.type = NLA_FLAG}, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_set_roam_events() - set roam stats |
| * @wiphy: wiphy pointer |
| * @wdev: pointer to struct wireless_dev |
| * @data: pointer to incoming NL vendor data |
| * @data_len: length of @data |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| static int __wlan_hdd_cfg80211_set_roam_events(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_MAX + 1]; |
| QDF_STATUS status; |
| int ret; |
| uint8_t config, state, param = 0; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret != 0) { |
| hdd_err("Invalid hdd_ctx"); |
| return ret; |
| } |
| |
| ret = hdd_validate_adapter(adapter); |
| if (ret != 0) { |
| hdd_err("Invalid adapter"); |
| return ret; |
| } |
| |
| if (adapter->device_mode != QDF_STA_MODE) { |
| hdd_err("STATS supported in only STA mode!"); |
| return -EINVAL; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_MAX, |
| data, data_len, set_roam_events_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| if (!tb[EVENTS_CONFIGURE]) { |
| hdd_err("roam events configure not present"); |
| return -EINVAL; |
| } |
| |
| config = nla_get_u8(tb[EVENTS_CONFIGURE]); |
| hdd_debug("roam stats configured: %d", config); |
| |
| if (!tb[SUSPEND_STATE]) { |
| hdd_debug("suspend state not present"); |
| param = config ? ROAM_RT_STATS_ENABLED : ROAM_RT_STATS_DISABLED; |
| } else if (config == ROAM_RT_STATS_ENABLED) { |
| state = nla_get_flag(tb[SUSPEND_STATE]); |
| hdd_debug("Suspend state configured: %d", state); |
| param = ROAM_RT_STATS_ENABLED | |
| ROAM_RT_STATS_ENABLED_IN_SUSPEND_MODE; |
| } |
| |
| hdd_debug("roam events param: %d", param); |
| ucfg_cm_update_roam_rt_stats(hdd_ctx->psoc, |
| param, ROAM_RT_STATS_ENABLE); |
| |
| if (param == (ROAM_RT_STATS_ENABLED | |
| ROAM_RT_STATS_ENABLED_IN_SUSPEND_MODE)) { |
| ucfg_pmo_enable_wakeup_event(hdd_ctx->psoc, adapter->vdev_id, |
| WOW_ROAM_STATS_EVENT); |
| ucfg_cm_update_roam_rt_stats(hdd_ctx->psoc, |
| ROAM_RT_STATS_ENABLED, |
| ROAM_RT_STATS_SUSPEND_MODE_ENABLE); |
| } else if (ucfg_cm_get_roam_rt_stats(hdd_ctx->psoc, |
| ROAM_RT_STATS_SUSPEND_MODE_ENABLE)) { |
| ucfg_pmo_disable_wakeup_event(hdd_ctx->psoc, adapter->vdev_id, |
| WOW_ROAM_STATS_EVENT); |
| ucfg_cm_update_roam_rt_stats(hdd_ctx->psoc, |
| ROAM_RT_STATS_DISABLED, |
| ROAM_RT_STATS_SUSPEND_MODE_ENABLE); |
| } |
| |
| status = ucfg_cm_roam_send_rt_stats_config(hdd_ctx->pdev, |
| adapter->vdev_id, param); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| #undef EVENTS_CONFIGURE |
| #undef SUSPEND_STATE |
| |
| /** |
| * wlan_hdd_cfg80211_set_roam_events() - set roam stats |
| * @wiphy: wiphy pointer |
| * @wdev: pointer to struct wireless_dev |
| * @data: pointer to incoming NL vendor data |
| * @data_len: length of @data |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| static int wlan_hdd_cfg80211_set_roam_events(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_roam_events(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| |
| /** |
| * __wlan_hdd_cfg80211_get_chain_rssi() - get chain rssi |
| * @wiphy: wiphy pointer |
| * @wdev: pointer to struct wireless_dev |
| * @data: pointer to incoming NL vendor data |
| * @data_len: length of @data |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| static int __wlan_hdd_cfg80211_get_chain_rssi(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| mac_handle_t mac_handle; |
| struct get_chain_rssi_req_params req_msg; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; |
| QDF_STATUS status; |
| int retval; |
| void *cookie; |
| struct osif_request *request; |
| struct chain_rssi_priv *priv; |
| static const struct osif_request_params params = { |
| .priv_size = sizeof(*priv), |
| .timeout_ms = WLAN_WAIT_TIME_STATS, |
| }; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| retval = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != retval) |
| return retval; |
| |
| if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, |
| data, data_len, get_chain_rssi_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) { |
| hdd_err("attr mac addr failed"); |
| return -EINVAL; |
| } |
| if (nla_len(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) != |
| QDF_MAC_ADDR_SIZE) { |
| hdd_err("incorrect mac size"); |
| return -EINVAL; |
| } |
| memcpy(&req_msg.peer_macaddr, |
| nla_data(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]), |
| QDF_MAC_ADDR_SIZE); |
| req_msg.session_id = adapter->vdev_id; |
| |
| request = osif_request_alloc(¶ms); |
| if (!request) { |
| hdd_err("Request allocation failure"); |
| return -ENOMEM; |
| } |
| cookie = osif_request_cookie(request); |
| |
| mac_handle = hdd_ctx->mac_handle; |
| status = sme_get_chain_rssi(mac_handle, |
| &req_msg, |
| hdd_get_chain_rssi_cb, |
| cookie); |
| if (QDF_STATUS_SUCCESS != status) { |
| hdd_err("Unable to get chain rssi"); |
| retval = qdf_status_to_os_return(status); |
| } else { |
| retval = osif_request_wait_for_response(request); |
| if (retval) { |
| hdd_err("Target response timed out"); |
| } else { |
| priv = osif_request_priv(request); |
| retval = hdd_post_get_chain_rssi_rsp(hdd_ctx, |
| &priv->chain_rssi); |
| if (retval) |
| hdd_err("Failed to post chain rssi"); |
| } |
| } |
| osif_request_put(request); |
| |
| hdd_exit(); |
| return retval; |
| } |
| |
| #ifdef WLAN_FEATURE_GET_USABLE_CHAN_LIST |
| /** |
| * wlan_hdd_cfg80211_get_usable_channel() - get chain rssi |
| * @wiphy: wiphy pointer |
| * @wdev: pointer to struct wireless_dev |
| * @data: pointer to incoming NL vendor data |
| * @data_len: length of @data |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| static int wlan_hdd_cfg80211_get_usable_channel(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_usable_channel(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #else |
| static int wlan_hdd_cfg80211_get_usable_channel(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| hdd_debug("get usable channel feature not supported"); |
| return -EPERM; |
| } |
| #endif |
| |
| #ifdef WLAN_FEATURE_PKT_CAPTURE |
| |
| /** |
| * __wlan_hdd_cfg80211_set_monitor_mode() - Wifi monitor mode configuration |
| * vendor command |
| * @wiphy: wiphy device pointer |
| * @wdev: wireless device pointer |
| * @data: Vendor command data buffer |
| * @data_len: Buffer length |
| * |
| * Handles . |
| * |
| * Return: 0 for Success and negative value for failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_set_monitor_mode(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| int errno; |
| QDF_STATUS status; |
| |
| if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| if (!ucfg_pkt_capture_get_mode(hdd_ctx->psoc)) |
| return -EPERM; |
| |
| errno = hdd_validate_adapter(adapter); |
| if (errno) |
| return errno; |
| |
| status = os_if_monitor_mode_configure(adapter, data, data_len); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_monitor_mode() - set monitor mode |
| * @wiphy: wiphy pointer |
| * @wdev: pointer to struct wireless_dev |
| * @data: pointer to incoming NL vendor data |
| * @data_len: length of @data |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| static int wlan_hdd_cfg80211_set_monitor_mode(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| hdd_enter_dev(wdev->netdev); |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_monitor_mode(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| hdd_exit(); |
| |
| return errno; |
| } |
| |
| #endif |
| |
| /** |
| * wlan_hdd_cfg80211_get_chain_rssi() - get chain rssi |
| * @wiphy: wiphy pointer |
| * @wdev: pointer to struct wireless_dev |
| * @data: pointer to incoming NL vendor data |
| * @data_len: length of @data |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| static int wlan_hdd_cfg80211_get_chain_rssi(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_chain_rssi(wiphy, wdev, data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * wlan_hdd_fill_intf_info() - Fill skb buffer with interface info |
| * @skb: Pointer to skb |
| * @info: mac mode info |
| * @index: attribute type index for nla_nest_start() |
| * |
| * Return : 0 on success and errno on failure |
| */ |
| static int wlan_hdd_fill_intf_info(struct sk_buff *skb, |
| struct connection_info *info, int index) |
| { |
| struct nlattr *attr; |
| uint32_t freq; |
| struct hdd_context *hdd_ctx; |
| struct hdd_adapter *hdd_adapter; |
| |
| hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); |
| if (!hdd_ctx) |
| goto error; |
| |
| hdd_adapter = hdd_get_adapter_by_vdev(hdd_ctx, info->vdev_id); |
| if (!hdd_adapter) |
| goto error; |
| |
| attr = nla_nest_start(skb, index); |
| if (!attr) |
| goto error; |
| |
| freq = sme_chn_to_freq(info->channel); |
| |
| if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX, |
| hdd_adapter->dev->ifindex) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ, freq)) |
| goto error; |
| |
| nla_nest_end(skb, attr); |
| |
| return 0; |
| error: |
| hdd_err("Fill buffer with interface info failed"); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_fill_mac_info() - Fill skb buffer with mac info |
| * @skb: Pointer to skb |
| * @info: mac mode info |
| * @mac_id: MAC id |
| * @conn_count: number of current connections |
| * |
| * Return : 0 on success and errno on failure |
| */ |
| static int wlan_hdd_fill_mac_info(struct sk_buff *skb, |
| struct connection_info *info, uint32_t mac_id, |
| uint32_t conn_count) |
| { |
| struct nlattr *attr, *intf_attr; |
| uint32_t band = 0, i = 0, j = 0; |
| bool present = false; |
| |
| while (i < conn_count) { |
| if (info[i].mac_id == mac_id) { |
| present = true; |
| if (info[i].channel <= SIR_11B_CHANNEL_END) |
| band |= 1 << NL80211_BAND_2GHZ; |
| else if (info[i].channel <= SIR_11A_CHANNEL_END) |
| band |= 1 << NL80211_BAND_5GHZ; |
| } |
| i++; |
| } |
| |
| if (!present) |
| return 0; |
| |
| i = 0; |
| attr = nla_nest_start(skb, mac_id); |
| if (!attr) |
| goto error; |
| |
| if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID, mac_id) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND, band)) |
| goto error; |
| |
| intf_attr = nla_nest_start(skb, QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO); |
| if (!intf_attr) |
| goto error; |
| |
| while (i < conn_count) { |
| if (info[i].mac_id == mac_id) { |
| if (wlan_hdd_fill_intf_info(skb, &info[i], j)) |
| return -EINVAL; |
| j++; |
| } |
| i++; |
| } |
| |
| nla_nest_end(skb, intf_attr); |
| |
| nla_nest_end(skb, attr); |
| |
| return 0; |
| error: |
| hdd_err("Fill buffer with mac info failed"); |
| return -EINVAL; |
| } |
| |
| |
| int wlan_hdd_send_mode_change_event(void) |
| { |
| int err; |
| struct hdd_context *hdd_ctx; |
| struct sk_buff *skb; |
| struct nlattr *attr; |
| struct connection_info info[MAX_NUMBER_OF_CONC_CONNECTIONS]; |
| uint32_t conn_count, mac_id; |
| |
| hdd_enter(); |
| hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); |
| if (!hdd_ctx) |
| return -EINVAL; |
| |
| err = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != err) |
| return err; |
| |
| conn_count = policy_mgr_get_connection_info(hdd_ctx->psoc, info); |
| if (!conn_count) |
| return -EINVAL; |
| |
| skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL, |
| (sizeof(uint32_t) * 4) * |
| MAX_NUMBER_OF_CONC_CONNECTIONS + NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO_INDEX, |
| GFP_KERNEL); |
| if (!skb) { |
| hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| return -ENOMEM; |
| } |
| |
| attr = nla_nest_start(skb, QCA_WLAN_VENDOR_ATTR_MAC_INFO); |
| if (!attr) { |
| hdd_err("nla_nest_start failed"); |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| for (mac_id = 0; mac_id < MAX_MAC; mac_id++) { |
| if (wlan_hdd_fill_mac_info(skb, info, mac_id, conn_count)) { |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| } |
| |
| nla_nest_end(skb, attr); |
| |
| cfg80211_vendor_event(skb, GFP_KERNEL); |
| hdd_exit(); |
| |
| return err; |
| } |
| |
| |
| /* Short name for QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_VALID_CHANNELS command */ |
| |
| #define EXTSCAN_CONFIG_MAX \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX |
| #define EXTSCAN_CONFIG_REQUEST_ID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID |
| #define EXTSCAN_CONFIG_WIFI_BAND \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND |
| #define EXTSCAN_CONFIG_MAX_CHANNELS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS |
| #define EXTSCAN_RESULTS_NUM_CHANNELS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_CHANNELS |
| #define EXTSCAN_RESULTS_CHANNELS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CHANNELS |
| |
| static const struct nla_policy |
| wlan_hdd_extscan_get_valid_channels_policy[EXTSCAN_CONFIG_MAX + 1] = { |
| [EXTSCAN_CONFIG_REQUEST_ID] = {.type = NLA_U32}, |
| [EXTSCAN_CONFIG_WIFI_BAND] = {.type = NLA_U32}, |
| [EXTSCAN_CONFIG_MAX_CHANNELS] = {.type = NLA_U32}, |
| }; |
| |
| /** |
| * hdd_remove_passive_channels () - remove passive channels |
| * @wiphy: Pointer to wireless phy |
| * @chan_list: channel list |
| * @num_channels: number of channels |
| * |
| * Return: none |
| */ |
| static void hdd_remove_passive_channels(struct wiphy *wiphy, |
| uint32_t *chan_list, |
| uint8_t *num_channels) |
| { |
| uint8_t num_chan_temp = 0; |
| int i, j, k; |
| |
| for (i = 0; i < *num_channels; i++) |
| for (j = 0; j < HDD_NUM_NL80211_BANDS; j++) { |
| if (!wiphy->bands[j]) |
| continue; |
| for (k = 0; k < wiphy->bands[j]->n_channels; k++) { |
| if ((chan_list[i] == |
| wiphy->bands[j]->channels[k].center_freq) |
| && (!(wiphy->bands[j]->channels[k].flags & |
| IEEE80211_CHAN_PASSIVE_SCAN)) |
| ) { |
| chan_list[num_chan_temp] = chan_list[i]; |
| num_chan_temp++; |
| } |
| } |
| } |
| |
| *num_channels = num_chan_temp; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_extscan_get_valid_channels () - get valid channels |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: none |
| */ |
| static int |
| __wlan_hdd_cfg80211_extscan_get_valid_channels(struct wiphy *wiphy, |
| struct wireless_dev |
| *wdev, const void *data, |
| int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| uint32_t chan_list[CFG_VALID_CHANNEL_LIST_LEN] = {0}; |
| uint8_t num_channels = 0, i, buf[256] = {0}; |
| struct nlattr *tb[EXTSCAN_CONFIG_MAX + 1]; |
| uint32_t request_id, max_channels; |
| tWifiBand wifi_band; |
| QDF_STATUS status; |
| struct sk_buff *reply_skb; |
| int ret, len = 0; |
| |
| /* ENTER_DEV() intentionally not used in a frequently invoked API */ |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return -EINVAL; |
| |
| if (wlan_cfg80211_nla_parse(tb, EXTSCAN_CONFIG_MAX, data, data_len, |
| wlan_hdd_extscan_get_valid_channels_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[EXTSCAN_CONFIG_REQUEST_ID]) { |
| hdd_err("attr request id failed"); |
| return -EINVAL; |
| } |
| request_id = nla_get_u32(tb[EXTSCAN_CONFIG_REQUEST_ID]); |
| |
| /* Parse and fetch wifi band */ |
| if (!tb[EXTSCAN_CONFIG_WIFI_BAND]) { |
| hdd_err("attr wifi band failed"); |
| return -EINVAL; |
| } |
| wifi_band = nla_get_u32(tb[EXTSCAN_CONFIG_WIFI_BAND]); |
| if (!tb[EXTSCAN_CONFIG_MAX_CHANNELS]) { |
| hdd_err("attr max channels failed"); |
| return -EINVAL; |
| } |
| max_channels = nla_get_u32(tb[EXTSCAN_CONFIG_MAX_CHANNELS]); |
| |
| if (max_channels > CFG_VALID_CHANNEL_LIST_LEN) { |
| hdd_err("Max channels %d exceeded Valid channel list len %d", |
| max_channels, CFG_VALID_CHANNEL_LIST_LEN); |
| return -EINVAL; |
| } |
| |
| hdd_err("Req Id: %u Wifi band: %d Max channels: %d", request_id, |
| wifi_band, max_channels); |
| status = sme_get_valid_channels_by_band(hdd_ctx->mac_handle, |
| wifi_band, chan_list, |
| &num_channels); |
| if (QDF_STATUS_SUCCESS != status) { |
| hdd_err("sme_get_valid_channels_by_band failed (err=%d)", |
| status); |
| return -EINVAL; |
| } |
| |
| num_channels = QDF_MIN(num_channels, max_channels); |
| |
| if ((QDF_SAP_MODE == adapter->device_mode) || |
| !strncmp(hdd_get_fwpath(), "ap", 2)) |
| hdd_remove_passive_channels(wiphy, chan_list, |
| &num_channels); |
| |
| hdd_debug("Number of channels: %d", num_channels); |
| for (i = 0; i < num_channels; i++) |
| len += scnprintf(buf + len, sizeof(buf) - len, |
| "%u ", chan_list[i]); |
| |
| hdd_debug("Channels: %s", buf); |
| |
| reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) + |
| sizeof(u32) * |
| num_channels + |
| NLMSG_HDRLEN); |
| |
| if (reply_skb) { |
| if (nla_put_u32( |
| reply_skb, |
| EXTSCAN_RESULTS_NUM_CHANNELS, |
| num_channels) || |
| nla_put( |
| reply_skb, |
| EXTSCAN_RESULTS_CHANNELS, |
| sizeof(u32) * num_channels, chan_list)) { |
| hdd_err("nla put fail"); |
| kfree_skb(reply_skb); |
| return -EINVAL; |
| } |
| ret = cfg80211_vendor_cmd_reply(reply_skb); |
| return ret; |
| } |
| |
| hdd_err("valid channels: buffer alloc fail"); |
| return -EINVAL; |
| } |
| |
| #undef EXTSCAN_CONFIG_MAX |
| #undef EXTSCAN_CONFIG_REQUEST_ID |
| #undef EXTSCAN_CONFIG_WIFI_BAND |
| #undef ETCAN_CONFIG_MAX_CHANNELS |
| #undef EXTSCAN_RESULTS_NUM_CHANNELS |
| #undef EXTSCAN_RESULTS_CHANNELS |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_get_valid_channels() - get ext scan valid channels |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int wlan_hdd_cfg80211_extscan_get_valid_channels( |
| struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_extscan_get_valid_channels(wiphy, wdev, |
| data, data_len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| #ifdef FEATURE_RADAR_HISTORY |
| static uint32_t get_radar_history_evt_len(uint32_t count) |
| { |
| uint32_t data_len = NLMSG_HDRLEN; |
| |
| data_len += |
| /* nested attribute hdr QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_ENTRIES */ |
| nla_total_size(count * |
| (nla_total_size( |
| /* channel frequency */ |
| nla_total_size(sizeof(uint32_t)) + |
| /* timestamp */ |
| nla_total_size(sizeof(uint64_t)) + |
| /* radar detected flag */ |
| nla_total_size(0)))); |
| |
| return data_len; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_radar_history () - Get radar history |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_get_radar_history(struct wiphy *wiphy, |
| struct wireless_dev |
| *wdev, const void *data, |
| int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| QDF_STATUS status; |
| struct sk_buff *reply_skb = NULL; |
| int ret, len; |
| struct dfs_radar_history *radar_history = NULL; |
| uint32_t hist_count = 0; |
| int idx; |
| struct nlattr *ch_array, *ch_element; |
| |
| if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return -EINVAL; |
| |
| status = wlansap_query_radar_history(hdd_ctx->mac_handle, |
| &radar_history, &hist_count); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| return -EINVAL; |
| |
| len = get_radar_history_evt_len(hist_count); |
| reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, len); |
| if (!reply_skb) { |
| ret = -ENOMEM; |
| goto err; |
| } |
| |
| ch_array = nla_nest_start( |
| reply_skb, QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_ENTRIES); |
| if (!ch_array) { |
| ret = -ENOMEM; |
| goto err; |
| } |
| |
| for (idx = 0; idx < hist_count; idx++) { |
| ch_element = nla_nest_start(reply_skb, idx); |
| if (!ch_element) { |
| ret = -ENOMEM; |
| goto err; |
| } |
| |
| if (nla_put_u32(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_FREQ, |
| radar_history[idx].ch_freq)) { |
| ret = -ENOMEM; |
| goto err; |
| } |
| |
| if (wlan_cfg80211_nla_put_u64( |
| reply_skb, |
| QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_TIMESTAMP, |
| radar_history[idx].time)) { |
| ret = -ENOMEM; |
| goto err; |
| } |
| |
| if (radar_history[idx].radar_found && |
| nla_put_flag( |
| reply_skb, |
| QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_DETECTED)) { |
| ret = -ENOMEM; |
| goto err; |
| } |
| |
| nla_nest_end(reply_skb, ch_element); |
| } |
| nla_nest_end(reply_skb, ch_array); |
| qdf_mem_free(radar_history); |
| |
| ret = cfg80211_vendor_cmd_reply(reply_skb); |
| hdd_debug("get radar history count %d, ret %d", hist_count, ret); |
| |
| return ret; |
| err: |
| qdf_mem_free(radar_history); |
| if (reply_skb) |
| kfree_skb(reply_skb); |
| hdd_debug("get radar history error %d", ret); |
| |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_get_radar_history() - get radar history |
| * @wiphy: wiphy pointer |
| * @wdev: pointer to struct wireless_dev |
| * @data: pointer to incoming NL vendor data |
| * @data_len: length of @data |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| static int wlan_hdd_cfg80211_get_radar_history(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_radar_history(wiphy, wdev, |
| data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #define FEATURE_RADAR_HISTORY_VENDOR_COMMANDS \ |
| { \ |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, \ |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_RADAR_HISTORY, \ |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ |
| WIPHY_VENDOR_CMD_NEED_NETDEV | \ |
| WIPHY_VENDOR_CMD_NEED_RUNNING, \ |
| .doit = wlan_hdd_cfg80211_get_radar_history, \ |
| vendor_command_policy(VENDOR_CMD_RAW_DATA, 0) \ |
| }, |
| #else |
| #define FEATURE_RADAR_HISTORY_VENDOR_COMMANDS |
| #endif |
| |
| const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = { |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = is_driver_dfs_capable, |
| vendor_command_policy(VENDOR_CMD_RAW_DATA, 0) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_VALID_CHANNELS, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_extscan_get_valid_channels, |
| vendor_command_policy( |
| wlan_hdd_extscan_get_valid_channels_policy, |
| EXTSCAN_PARAM_MAX) |
| }, |
| #ifdef WLAN_FEATURE_STATS_EXT |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_STATS_EXT, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_stats_ext_request, |
| vendor_command_policy(VENDOR_CMD_RAW_DATA, 0) |
| }, |
| #endif |
| FEATURE_EXTSCAN_VENDOR_COMMANDS |
| |
| FEATURE_LL_STATS_VENDOR_COMMANDS |
| |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_FEATURES, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = wlan_hdd_cfg80211_get_supported_features, |
| vendor_command_policy(VENDOR_CMD_RAW_DATA, 0) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_set_scanning_mac_oui, |
| vendor_command_policy(VENDOR_CMD_RAW_DATA, 0) |
| }, |
| |
| FEATURE_CONCURRENCY_MATRIX_VENDOR_COMMANDS |
| |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NO_DFS_FLAG, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_disable_dfs_chan_scan, |
| vendor_command_policy(wlan_hdd_set_no_dfs_flag_config_policy, |
| QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_WISA, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_handle_wisa_cmd, |
| vendor_command_policy(wlan_hdd_wisa_cmd_policy, |
| QCA_WLAN_VENDOR_ATTR_WISA_MAX) |
| }, |
| |
| FEATURE_STATION_INFO_VENDOR_COMMANDS |
| |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DO_ACS, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_do_acs, |
| vendor_command_policy(wlan_hdd_cfg80211_do_acs_policy, |
| QCA_WLAN_VENDOR_ATTR_ACS_MAX) |
| }, |
| |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = wlan_hdd_cfg80211_get_features, |
| vendor_command_policy(VENDOR_CMD_RAW_DATA, 0) |
| }, |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_keymgmt_set_key, |
| vendor_command_policy(VENDOR_CMD_RAW_DATA, 0) |
| }, |
| #endif |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = wlan_hdd_cfg80211_get_wifi_info, |
| vendor_command_policy(qca_wlan_vendor_get_wifi_info_policy, |
| QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_wifi_configuration_set, |
| vendor_command_policy(wlan_hdd_wifi_config_policy, |
| QCA_WLAN_VENDOR_ATTR_CONFIG_MAX) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_wifi_configuration_get, |
| vendor_command_policy(wlan_hdd_wifi_config_policy, |
| QCA_WLAN_VENDOR_ATTR_CONFIG_MAX) |
| }, |
| |
| FEATURE_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION |
| #ifdef WLAN_SUPPORT_TWT |
| FEATURE_VENDOR_SUBCMD_WIFI_CONFIG_TWT |
| #endif |
| |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ROAM, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_set_ext_roam_params, |
| vendor_command_policy(wlan_hdd_set_roam_param_policy, |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_RATEMASK_CONFIG, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_set_ratemask_config, |
| vendor_command_policy(wlan_hdd_set_ratemask_param_policy, |
| QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_MAX) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_wifi_logger_start, |
| vendor_command_policy( |
| qca_wlan_vendor_wifi_logger_start_policy, |
| QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV, |
| .doit = wlan_hdd_cfg80211_wifi_logger_get_ring_data, |
| vendor_command_policy( |
| qca_wlan_vendor_wifi_logger_get_ring_data_policy, |
| QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_get_preferred_freq_list, |
| vendor_command_policy( |
| get_preferred_freq_list_policy, |
| QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_set_probable_oper_channel, |
| vendor_command_policy( |
| set_probable_oper_channel_policy, |
| QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX) |
| }, |
| |
| FEATURE_HANDLE_TSF_VENDOR_COMMANDS |
| |
| #ifdef FEATURE_WLAN_TDLS |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_get_tdls_capabilities, |
| vendor_command_policy(VENDOR_CMD_RAW_DATA, 0) |
| }, |
| #endif |
| #ifdef WLAN_FEATURE_OFFLOAD_PACKETS |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_offloaded_packets, |
| vendor_command_policy(offloaded_packet_policy, |
| QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX) |
| }, |
| #endif |
| FEATURE_RSSI_MONITOR_VENDOR_COMMANDS |
| FEATURE_OEM_DATA_VENDOR_COMMANDS |
| FEATURE_INTEROP_ISSUES_AP_VENDOR_COMMANDS |
| |
| #ifdef WLAN_NS_OFFLOAD |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_set_ns_offload, |
| vendor_command_policy(ns_offload_set_policy, |
| QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX) |
| }, |
| #endif /* WLAN_NS_OFFLOAD */ |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = wlan_hdd_cfg80211_get_logger_supp_feature, |
| vendor_command_policy(get_logger_set_policy, |
| QCA_WLAN_VENDOR_ATTR_LOGGER_MAX) |
| }, |
| |
| FEATURE_TRIGGER_SCAN_VENDOR_COMMANDS |
| |
| /* Vendor abort scan */ |
| FEATURE_ABORT_SCAN_VENDOR_COMMANDS |
| |
| /* OCB commands */ |
| FEATURE_OCB_VENDOR_COMMANDS |
| |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_get_link_properties, |
| vendor_command_policy(wlan_hdd_get_link_properties_policy, |
| QCA_WLAN_VENDOR_ATTR_MAX) |
| }, |
| |
| FEATURE_OTA_TEST_VENDOR_COMMANDS |
| |
| FEATURE_LFR_SUBNET_DETECT_VENDOR_COMMANDS |
| |
| FEATURE_TX_POWER_VENDOR_COMMANDS |
| |
| FEATURE_APF_OFFLOAD_VENDOR_COMMANDS |
| |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_acs_dfs_mode, |
| vendor_command_policy(wlan_hdd_set_acs_dfs_config_policy, |
| QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_STA_CONNECT_ROAM_POLICY, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_sta_roam_policy, |
| vendor_command_policy( |
| wlan_hdd_set_sta_roam_config_policy, |
| QCA_WLAN_VENDOR_ATTR_STA_CONNECT_ROAM_POLICY_MAX) |
| }, |
| |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_MULTI_STA_POLICY, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = wlan_hdd_cfg80211_dual_sta_policy, |
| vendor_command_policy( |
| wlan_hdd_set_dual_sta_policy, |
| QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_MAX) |
| }, |
| |
| #ifdef FEATURE_WLAN_CH_AVOID |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_avoid_freq, |
| vendor_command_policy(VENDOR_CMD_RAW_DATA, 0) |
| }, |
| #endif |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_SAP_CONFIG, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_sap_configuration_set, |
| vendor_command_policy(wlan_hdd_sap_config_policy, |
| QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX) |
| }, |
| |
| FEATURE_P2P_LISTEN_OFFLOAD_VENDOR_COMMANDS |
| |
| FEATURE_SAP_COND_CHAN_SWITCH_VENDOR_COMMANDS |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_get_wakelock_stats, |
| vendor_command_policy(VENDOR_CMD_RAW_DATA, 0) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_get_bus_size, |
| vendor_command_policy(VENDOR_CMD_RAW_DATA, 0) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_update_vendor_channel, |
| vendor_command_policy(acs_chan_config_policy, |
| QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_MAX) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SETBAND, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_setband, |
| vendor_command_policy(setband_policy, QCA_WLAN_VENDOR_ATTR_MAX) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GETBAND, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_getband, |
| vendor_command_policy(VENDOR_CMD_RAW_DATA, 0) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ROAMING, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_set_fast_roaming, |
| vendor_command_policy(qca_wlan_vendor_attr, |
| QCA_WLAN_VENDOR_ATTR_MAX) |
| }, |
| FEATURE_DISA_VENDOR_COMMANDS |
| FEATURE_TDLS_VENDOR_COMMANDS |
| FEATURE_SAR_LIMITS_VENDOR_COMMANDS |
| BCN_RECV_FEATURE_VENDOR_COMMANDS |
| FEATURE_VENDOR_SUBCMD_SET_TRACE_LEVEL |
| #ifdef WLAN_FEATURE_LINK_LAYER_STATS |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_ll_stats_ext_set_param, |
| vendor_command_policy(qca_wlan_vendor_ll_ext_policy, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX) |
| }, |
| #endif |
| FEATURE_VENDOR_SUBCMD_NUD_STATS_SET |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_get_nud_stats, |
| vendor_command_policy(VENDOR_CMD_RAW_DATA, 0) |
| }, |
| |
| FEATURE_BSS_TRANSITION_VENDOR_COMMANDS |
| FEATURE_SPECTRAL_SCAN_VENDOR_COMMANDS |
| FEATURE_CFR_VENDOR_COMMANDS |
| FEATURE_11AX_VENDOR_COMMANDS |
| |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_get_chain_rssi, |
| vendor_command_policy(get_chain_rssi_policy, |
| QCA_WLAN_VENDOR_ATTR_MAX) |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_USABLE_CHANNELS, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_get_usable_channel, |
| vendor_command_policy(get_usable_channel_policy, |
| QCA_WLAN_VENDOR_ATTR_MAX) |
| }, |
| |
| FEATURE_ACTIVE_TOS_VENDOR_COMMANDS |
| FEATURE_NAN_VENDOR_COMMANDS |
| FEATURE_FW_STATE_COMMANDS |
| FEATURE_COEX_CONFIG_COMMANDS |
| FEATURE_MPTA_HELPER_COMMANDS |
| FEATURE_HW_CAPABILITY_COMMANDS |
| FEATURE_THERMAL_VENDOR_COMMANDS |
| FEATURE_BTC_CHAIN_MODE_COMMANDS |
| FEATURE_WMM_COMMANDS |
| FEATURE_GPIO_CFG_VENDOR_COMMANDS |
| FEATURE_MEDIUM_ASSESS_VENDOR_COMMANDS |
| FEATURE_RADAR_HISTORY_VENDOR_COMMANDS |
| FEATURE_AVOID_FREQ_EXT_VENDOR_COMMANDS |
| FEATURE_MDNS_OFFLOAD_VENDOR_COMMANDS |
| |
| #ifdef WLAN_FEATURE_PKT_CAPTURE |
| FEATURE_MONITOR_MODE_VENDOR_COMMANDS |
| #endif |
| |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_set_roam_events, |
| vendor_command_policy(set_roam_events_policy, |
| QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_MAX) |
| }, |
| #endif |
| }; |
| |
| struct hdd_context *hdd_cfg80211_wiphy_alloc(void) |
| { |
| struct wiphy *wiphy; |
| struct hdd_context *hdd_ctx; |
| |
| hdd_enter(); |
| |
| wiphy = wiphy_new(&wlan_hdd_cfg80211_ops, sizeof(*hdd_ctx)); |
| if (!wiphy) { |
| hdd_err("failed to allocate wiphy!"); |
| return NULL; |
| } |
| |
| hdd_ctx = wiphy_priv(wiphy); |
| hdd_ctx->wiphy = wiphy; |
| |
| return hdd_ctx; |
| } |
| |
| int wlan_hdd_cfg80211_update_band(struct hdd_context *hdd_ctx, |
| struct wiphy *wiphy, |
| enum band_info new_band) |
| { |
| int i, j; |
| enum channel_state channel_state; |
| |
| hdd_enter(); |
| |
| for (i = 0; i < HDD_NUM_NL80211_BANDS; i++) { |
| |
| if (!wiphy->bands[i]) |
| continue; |
| |
| for (j = 0; j < wiphy->bands[i]->n_channels; j++) { |
| struct ieee80211_supported_band *band = wiphy->bands[i]; |
| |
| channel_state = wlan_reg_get_channel_state_for_freq( |
| hdd_ctx->pdev, |
| band->channels[j].center_freq); |
| |
| if (HDD_NL80211_BAND_2GHZ == i && |
| BAND_5G == new_band) { |
| /* 5G only */ |
| #ifdef WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY |
| /* Enable Social channels for P2P */ |
| if (WLAN_HDD_IS_SOCIAL_CHANNEL |
| (band->channels[j].center_freq) |
| && CHANNEL_STATE_ENABLE == |
| channel_state) |
| band->channels[j].flags &= |
| ~IEEE80211_CHAN_DISABLED; |
| else |
| #endif |
| band->channels[j].flags |= |
| IEEE80211_CHAN_DISABLED; |
| continue; |
| } else if (HDD_NL80211_BAND_5GHZ == i && |
| BAND_2G == new_band) { |
| /* 2G only */ |
| band->channels[j].flags |= |
| IEEE80211_CHAN_DISABLED; |
| continue; |
| } |
| |
| if (CHANNEL_STATE_DISABLE != channel_state) |
| band->channels[j].flags &= |
| ~IEEE80211_CHAN_DISABLED; |
| } |
| } |
| return 0; |
| } |
| |
| #if defined(CFG80211_SCAN_RANDOM_MAC_ADDR) || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) |
| static void wlan_hdd_cfg80211_scan_randomization_init(struct wiphy *wiphy) |
| { |
| wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; |
| wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR; |
| } |
| #else |
| static void wlan_hdd_cfg80211_scan_randomization_init(struct wiphy *wiphy) |
| { |
| } |
| #endif |
| |
| #define WLAN_HDD_MAX_NUM_CSA_COUNTERS 2 |
| |
| #if defined(CFG80211_RAND_TA_FOR_PUBLIC_ACTION_FRAME) || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)) |
| /** |
| * wlan_hdd_cfg80211_action_frame_randomization_init() - Randomize SA of MA |
| * frames |
| * @wiphy: Pointer to wiphy |
| * |
| * This function is used to indicate the support of source mac address |
| * randomization of management action frames |
| * |
| * Return: None |
| */ |
| static void |
| wlan_hdd_cfg80211_action_frame_randomization_init(struct wiphy *wiphy) |
| { |
| wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA); |
| } |
| #else |
| static void |
| wlan_hdd_cfg80211_action_frame_randomization_init(struct wiphy *wiphy) |
| { |
| } |
| #endif |
| |
| #if defined(WLAN_FEATURE_FILS_SK) && \ |
| (defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))) |
| static void wlan_hdd_cfg80211_set_wiphy_fils_feature(struct wiphy *wiphy) |
| { |
| wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_SK_OFFLOAD); |
| } |
| #else |
| static void wlan_hdd_cfg80211_set_wiphy_fils_feature(struct wiphy *wiphy) |
| { |
| } |
| #endif |
| |
| #if defined (CFG80211_SCAN_DBS_CONTROL_SUPPORT) || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)) |
| static void wlan_hdd_cfg80211_set_wiphy_scan_flags(struct wiphy *wiphy) |
| { |
| wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_LOW_SPAN_SCAN); |
| wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_LOW_POWER_SCAN); |
| wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN); |
| } |
| #else |
| static void wlan_hdd_cfg80211_set_wiphy_scan_flags(struct wiphy *wiphy) |
| { |
| } |
| #endif |
| |
| #if defined(CFG80211_BIGTK_CONFIGURATION_SUPPORT) || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)) |
| static void wlan_hdd_cfg80211_set_bigtk_flags(struct wiphy *wiphy) |
| { |
| wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION); |
| } |
| #else |
| static void wlan_hdd_cfg80211_set_bigtk_flags(struct wiphy *wiphy) |
| { |
| } |
| #endif |
| |
| #if defined(CFG80211_OCV_CONFIGURATION_SUPPORT) || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)) |
| static void wlan_hdd_cfg80211_set_ocv_flags(struct wiphy *wiphy) |
| { |
| wiphy_ext_feature_set(wiphy, |
| NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION); |
| } |
| #else |
| static void wlan_hdd_cfg80211_set_ocv_flags(struct wiphy *wiphy) |
| { |
| } |
| #endif |
| |
| #if defined(CFG80211_SCAN_OCE_CAPABILITY_SUPPORT) || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)) |
| static void wlan_hdd_cfg80211_set_wiphy_oce_scan_flags(struct wiphy *wiphy) |
| { |
| wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME); |
| wiphy_ext_feature_set(wiphy, |
| NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP); |
| wiphy_ext_feature_set(wiphy, |
| NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE); |
| wiphy_ext_feature_set( |
| wiphy, NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION); |
| } |
| #else |
| static void wlan_hdd_cfg80211_set_wiphy_oce_scan_flags(struct wiphy *wiphy) |
| { |
| } |
| #endif |
| |
| #if defined(WLAN_FEATURE_SAE) && \ |
| (defined(CFG80211_EXTERNAL_AUTH_SUPPORT) || \ |
| LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) |
| /** |
| * wlan_hdd_cfg80211_set_wiphy_sae_feature() - Indicates support of SAE feature |
| * @wiphy: Pointer to wiphy |
| * |
| * This function is used to indicate the support of SAE |
| * |
| * Return: None |
| */ |
| static void wlan_hdd_cfg80211_set_wiphy_sae_feature(struct wiphy *wiphy) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| |
| if (ucfg_fwol_get_sae_enable(hdd_ctx->psoc)) |
| wiphy->features |= NL80211_FEATURE_SAE; |
| } |
| #else |
| static void wlan_hdd_cfg80211_set_wiphy_sae_feature(struct wiphy *wiphy) |
| { |
| } |
| #endif |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) || \ |
| defined(CFG80211_DFS_OFFLOAD_BACKPORT) |
| static void wlan_hdd_cfg80211_set_dfs_offload_feature(struct wiphy *wiphy) |
| { |
| wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD); |
| } |
| #else |
| static void wlan_hdd_cfg80211_set_dfs_offload_feature(struct wiphy *wiphy) |
| { |
| wiphy->flags |= WIPHY_FLAG_DFS_OFFLOAD; |
| } |
| #endif |
| |
| #ifdef WLAN_FEATURE_DSRC |
| static void wlan_hdd_get_num_dsrc_ch_and_len(struct hdd_config *hdd_cfg, |
| int *num_ch, int *ch_len) |
| { |
| *num_ch = QDF_ARRAY_SIZE(hdd_channels_dot11p); |
| *ch_len = sizeof(hdd_channels_dot11p); |
| } |
| |
| static void wlan_hdd_copy_dsrc_ch(char *ch_ptr, int ch_arr_len) |
| { |
| if (!ch_arr_len) |
| return; |
| qdf_mem_copy(ch_ptr, &hdd_channels_dot11p[0], ch_arr_len); |
| } |
| |
| static void wlan_hdd_get_num_srd_ch_and_len(struct hdd_config *hdd_cfg, |
| int *num_ch, int *ch_len) |
| { |
| *num_ch = 0; |
| *ch_len = 0; |
| } |
| |
| static void wlan_hdd_copy_srd_ch(char *ch_ptr, int ch_arr_len) |
| { |
| } |
| |
| /** |
| * wlan_hdd_populate_5dot9_chan_info() - Populate 5.9 GHz chan info in hdd |
| * context |
| * @hdd_ctx: pointer to hdd context |
| * @index: 5.9 GHz channel beginning index in chan_info of @hdd_ctx |
| * |
| * Return: Number of 5.9 GHz channels populated |
| */ |
| static uint32_t |
| wlan_hdd_populate_5dot9_chan_info(struct hdd_context *hdd_ctx, uint32_t index) |
| { |
| return 0; |
| } |
| |
| #else |
| |
| static void wlan_hdd_get_num_dsrc_ch_and_len(struct hdd_config *hdd_cfg, |
| int *num_ch, int *ch_len) |
| { |
| *num_ch = 0; |
| *ch_len = 0; |
| } |
| |
| static void wlan_hdd_copy_dsrc_ch(char *ch_ptr, int ch_arr_len) |
| { |
| } |
| |
| static void wlan_hdd_get_num_srd_ch_and_len(struct hdd_config *hdd_cfg, |
| int *num_ch, int *ch_len) |
| { |
| *num_ch = QDF_ARRAY_SIZE(hdd_5dot9_ghz_ch); |
| *ch_len = sizeof(hdd_5dot9_ghz_ch); |
| } |
| |
| static void wlan_hdd_copy_srd_ch(char *ch_ptr, int ch_arr_len) |
| { |
| if (!ch_arr_len) |
| return; |
| qdf_mem_copy(ch_ptr, &hdd_5dot9_ghz_ch[0], ch_arr_len); |
| } |
| |
| /** |
| * wlan_hdd_populate_5dot9_chan_info() - Populate 5.9 GHz chan info in hdd |
| * context |
| * @hdd_ctx: pointer to hdd context |
| * @index: 5.9 GHz channel beginning index in chan_info of @hdd_ctx |
| * |
| * Return: Number of 5.9 GHz channels populated |
| */ |
| static uint32_t |
| wlan_hdd_populate_5dot9_chan_info(struct hdd_context *hdd_ctx, uint32_t index) |
| { |
| uint32_t num_5dot9_ch, i; |
| struct scan_chan_info *chan_info; |
| |
| num_5dot9_ch = QDF_ARRAY_SIZE(hdd_5dot9_ghz_ch); |
| chan_info = hdd_ctx->chan_info; |
| |
| for (i = 0; i < num_5dot9_ch; i++) |
| chan_info[index + i].freq = hdd_5dot9_ghz_ch[i].center_freq; |
| |
| return num_5dot9_ch; |
| } |
| |
| #endif |
| |
| #if defined(WLAN_FEATURE_11AX) && \ |
| (defined(CFG80211_SBAND_IFTYPE_DATA_BACKPORT) || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))) |
| #if defined(CONFIG_BAND_6GHZ) && (defined(CFG80211_6GHZ_BAND_SUPPORTED) || \ |
| (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE)) |
| static QDF_STATUS |
| wlan_hdd_iftype_data_alloc_6ghz(struct hdd_context *hdd_ctx) |
| { |
| hdd_ctx->iftype_data_6g = |
| qdf_mem_malloc(sizeof(*hdd_ctx->iftype_data_6g)); |
| |
| if (!hdd_ctx->iftype_data_6g) |
| return QDF_STATUS_E_NOMEM; |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static void |
| wlan_hdd_iftype_data_mem_free_6ghz(struct hdd_context *hdd_ctx) |
| { |
| qdf_mem_free(hdd_ctx->iftype_data_6g); |
| hdd_ctx->iftype_data_6g = NULL; |
| } |
| #else |
| static inline QDF_STATUS |
| wlan_hdd_iftype_data_alloc_6ghz(struct hdd_context *hdd_ctx) |
| { |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static inline void |
| wlan_hdd_iftype_data_mem_free_6ghz(struct hdd_context *hdd_ctx) |
| { |
| } |
| #endif |
| |
| static QDF_STATUS |
| wlan_hdd_iftype_data_alloc(struct hdd_context *hdd_ctx) |
| { |
| hdd_ctx->iftype_data_2g = |
| qdf_mem_malloc(sizeof(*hdd_ctx->iftype_data_2g)); |
| |
| if (!hdd_ctx->iftype_data_2g) |
| return QDF_STATUS_E_NOMEM; |
| |
| hdd_ctx->iftype_data_5g = |
| qdf_mem_malloc(sizeof(*hdd_ctx->iftype_data_5g)); |
| if (!hdd_ctx->iftype_data_5g) { |
| qdf_mem_free(hdd_ctx->iftype_data_2g); |
| hdd_ctx->iftype_data_2g = NULL; |
| return QDF_STATUS_E_NOMEM; |
| } |
| |
| if (QDF_IS_STATUS_ERROR(wlan_hdd_iftype_data_alloc_6ghz(hdd_ctx))) { |
| qdf_mem_free(hdd_ctx->iftype_data_5g); |
| qdf_mem_free(hdd_ctx->iftype_data_2g); |
| hdd_ctx->iftype_data_2g = NULL; |
| hdd_ctx->iftype_data_5g = NULL; |
| return QDF_STATUS_E_NOMEM; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static void |
| wlan_hdd_iftype_data_mem_free(struct hdd_context *hdd_ctx) |
| { |
| wlan_hdd_iftype_data_mem_free_6ghz(hdd_ctx); |
| qdf_mem_free(hdd_ctx->iftype_data_5g); |
| qdf_mem_free(hdd_ctx->iftype_data_2g); |
| hdd_ctx->iftype_data_5g = NULL; |
| hdd_ctx->iftype_data_2g = NULL; |
| } |
| #else |
| static QDF_STATUS |
| wlan_hdd_iftype_data_alloc(struct hdd_context *hdd_ctx) |
| |
| { |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static inline void |
| wlan_hdd_iftype_data_mem_free(struct hdd_context *hdd_ctx) |
| { |
| } |
| #endif |
| |
| #if defined(WLAN_FEATURE_NAN) && \ |
| (KERNEL_VERSION(4, 9, 0) <= LINUX_VERSION_CODE) |
| static void wlan_hdd_set_nan_if_mode(struct wiphy *wiphy) |
| { |
| wiphy->interface_modes |= BIT(NL80211_IFTYPE_NAN); |
| } |
| #else |
| static void wlan_hdd_set_nan_if_mode(struct wiphy *wiphy) |
| { |
| } |
| #endif |
| |
| #if defined(WLAN_FEATURE_NAN) && \ |
| (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) |
| static void wlan_hdd_set_nan_supported_bands(struct wiphy *wiphy) |
| { |
| wiphy->nan_supported_bands = |
| BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ); |
| } |
| #else |
| static void wlan_hdd_set_nan_supported_bands(struct wiphy *wiphy) |
| { |
| } |
| #endif |
| |
| /** |
| * wlan_hdd_update_akm_suit_info() - Populate akm suits supported by driver |
| * @wiphy: wiphy |
| * |
| * Return: void |
| */ |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)) || \ |
| defined(CFG80211_IFTYPE_AKM_SUITES_SUPPORT) |
| static void |
| wlan_hdd_update_akm_suit_info(struct wiphy *wiphy) |
| { |
| wiphy->iftype_akm_suites = wlan_hdd_akm_suites; |
| wiphy->num_iftype_akm_suites = QDF_ARRAY_SIZE(wlan_hdd_akm_suites); |
| } |
| #else |
| static void |
| wlan_hdd_update_akm_suit_info(struct wiphy *wiphy) |
| { |
| } |
| #endif |
| |
| #ifdef CFG80211_CTRL_FRAME_SRC_ADDR_TA_ADDR |
| static void wlan_hdd_update_eapol_over_nl80211_flags(struct wiphy *wiphy) |
| { |
| wiphy_ext_feature_set(wiphy, |
| NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211); |
| wiphy_ext_feature_set(wiphy, |
| NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH); |
| } |
| #else |
| static void wlan_hdd_update_eapol_over_nl80211_flags(struct wiphy *wiphy) |
| { |
| } |
| #endif |
| |
| #ifdef CFG80211_MULTI_AKM_CONNECT_SUPPORT |
| static void |
| wlan_hdd_update_max_connect_akm(struct wiphy *wiphy) |
| { |
| wiphy->max_num_akms_connect = WLAN_CM_MAX_CONNECT_AKMS; |
| } |
| #else |
| static void |
| wlan_hdd_update_max_connect_akm(struct wiphy *wiphy) |
| { |
| } |
| #endif |
| |
| /* |
| * FUNCTION: wlan_hdd_cfg80211_init |
| * This function is called by hdd_wlan_startup() |
| * during initialization. |
| * This function is used to initialize and register wiphy structure. |
| */ |
| int wlan_hdd_cfg80211_init(struct device *dev, |
| struct wiphy *wiphy, struct hdd_config *config) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| uint32_t *cipher_suites; |
| hdd_enter(); |
| |
| /* Now bind the underlying wlan device with wiphy */ |
| set_wiphy_dev(wiphy, dev); |
| |
| wiphy->mgmt_stypes = wlan_hdd_txrx_stypes; |
| wlan_hdd_update_akm_suit_info(wiphy); |
| wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
| | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
| | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
| #ifdef FEATURE_WLAN_STA_4ADDR_SCHEME |
| | WIPHY_FLAG_4ADDR_STATION |
| #endif |
| | WIPHY_FLAG_OFFCHAN_TX; |
| |
| if (ucfg_pmo_get_suspend_mode(hdd_ctx->psoc) == PMO_SUSPEND_WOW) { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) |
| wiphy->wowlan = &wowlan_support_cfg80211_init; |
| #else |
| wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT; |
| wiphy->wowlan.n_patterns = WOWL_MAX_PTRNS_ALLOWED; |
| wiphy->wowlan.pattern_min_len = 1; |
| wiphy->wowlan.pattern_max_len = WOWL_PTRN_MAX_SIZE; |
| #endif |
| } |
| |
| #ifdef FEATURE_WLAN_TDLS |
| wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
| | WIPHY_FLAG_TDLS_EXTERNAL_SETUP; |
| #endif |
| |
| wlan_hdd_cfg80211_set_wiphy_scan_flags(wiphy); |
| |
| wlan_scan_cfg80211_add_connected_pno_support(wiphy); |
| |
| wiphy->max_scan_ssids = MAX_SCAN_SSID; |
| |
| wiphy->max_scan_ie_len = SIR_MAC_MAX_ADD_IE_LENGTH; |
| |
| wiphy->max_acl_mac_addrs = MAX_ACL_MAC_ADDRESS; |
| |
| wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
| | BIT(NL80211_IFTYPE_P2P_CLIENT) |
| | BIT(NL80211_IFTYPE_P2P_GO) |
| | BIT(NL80211_IFTYPE_AP) |
| | BIT(NL80211_IFTYPE_MONITOR); |
| |
| wlan_hdd_set_nan_if_mode(wiphy); |
| |
| /* |
| * In case of static linked driver at the time of driver unload, |
| * module exit doesn't happens. Module cleanup helps in cleaning |
| * of static memory. |
| * If driver load happens statically, at the time of driver unload, |
| * wiphy flags don't get reset because of static memory. |
| * It's better not to store channel in static memory. |
| * The memory is for channels of struct wiphy and shouldn't be |
| * released during stop modules. So if it's allocated in active |
| * domain, the memory leak detector would catch the leak during |
| * stop modules. To avoid this,alloc in init domain in advance. |
| */ |
| hdd_ctx->channels_2ghz = qdf_mem_malloc(band_2_ghz_channels_size); |
| if (!hdd_ctx->channels_2ghz) |
| return -ENOMEM; |
| |
| hdd_ctx->channels_5ghz = qdf_mem_malloc(band_5_ghz_chanenls_size); |
| if (!hdd_ctx->channels_5ghz) |
| goto mem_fail_5g; |
| |
| if (QDF_IS_STATUS_ERROR(wlan_hdd_iftype_data_alloc(hdd_ctx))) |
| goto mem_fail_iftype_data; |
| |
| /*Initialise the supported cipher suite details */ |
| if (ucfg_fwol_get_gcmp_enable(hdd_ctx->psoc)) { |
| cipher_suites = qdf_mem_malloc(sizeof(hdd_cipher_suites) + |
| sizeof(hdd_gcmp_cipher_suits)); |
| if (!cipher_suites) |
| goto mem_fail_cipher_suites; |
| wiphy->n_cipher_suites = QDF_ARRAY_SIZE(hdd_cipher_suites) + |
| QDF_ARRAY_SIZE(hdd_gcmp_cipher_suits); |
| qdf_mem_copy(cipher_suites, &hdd_cipher_suites, |
| sizeof(hdd_cipher_suites)); |
| qdf_mem_copy(cipher_suites + QDF_ARRAY_SIZE(hdd_cipher_suites), |
| &hdd_gcmp_cipher_suits, |
| sizeof(hdd_gcmp_cipher_suits)); |
| } else { |
| cipher_suites = qdf_mem_malloc(sizeof(hdd_cipher_suites)); |
| if (!cipher_suites) |
| goto mem_fail_cipher_suites; |
| wiphy->n_cipher_suites = QDF_ARRAY_SIZE(hdd_cipher_suites); |
| qdf_mem_copy(cipher_suites, &hdd_cipher_suites, |
| sizeof(hdd_cipher_suites)); |
| } |
| wiphy->cipher_suites = cipher_suites; |
| cipher_suites = NULL; |
| /*signal strength in mBm (100*dBm) */ |
| wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; |
| wiphy->max_remain_on_channel_duration = MAX_REMAIN_ON_CHANNEL_DURATION; |
| |
| wiphy->n_vendor_commands = ARRAY_SIZE(hdd_wiphy_vendor_commands); |
| wiphy->vendor_commands = hdd_wiphy_vendor_commands; |
| |
| wiphy->vendor_events = wlan_hdd_cfg80211_vendor_events; |
| wiphy->n_vendor_events = ARRAY_SIZE(wlan_hdd_cfg80211_vendor_events); |
| |
| #ifdef QCA_HT_2040_COEX |
| wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE; |
| #endif |
| wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER; |
| |
| wiphy->features |= NL80211_FEATURE_VIF_TXPOWER; |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) || \ |
| defined(CFG80211_BEACON_TX_RATE_CUSTOM_BACKPORT) |
| wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); |
| wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HT); |
| wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_VHT); |
| #endif |
| |
| hdd_add_channel_switch_support(&wiphy->flags); |
| wiphy->max_num_csa_counters = WLAN_HDD_MAX_NUM_CSA_COUNTERS; |
| |
| wlan_hdd_update_max_connect_akm(wiphy); |
| |
| wlan_hdd_cfg80211_action_frame_randomization_init(wiphy); |
| |
| wlan_hdd_set_nan_supported_bands(wiphy); |
| |
| wlan_hdd_update_eapol_over_nl80211_flags(wiphy); |
| |
| hdd_exit(); |
| return 0; |
| |
| mem_fail_cipher_suites: |
| wlan_hdd_iftype_data_mem_free(hdd_ctx); |
| mem_fail_iftype_data: |
| qdf_mem_free(hdd_ctx->channels_5ghz); |
| hdd_ctx->channels_5ghz = NULL; |
| mem_fail_5g: |
| hdd_err("Not enough memory to allocate channels"); |
| qdf_mem_free(hdd_ctx->channels_2ghz); |
| hdd_ctx->channels_2ghz = NULL; |
| |
| return -ENOMEM; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_deinit() - Deinit cfg80211 |
| * @wiphy: the wiphy to validate against |
| * |
| * this function deinit cfg80211 and cleanup the |
| * memory allocated in wlan_hdd_cfg80211_init also |
| * reset the global reg params. |
| * |
| * Return: void |
| */ |
| void wlan_hdd_cfg80211_deinit(struct wiphy *wiphy) |
| { |
| int i; |
| const uint32_t *cipher_suites; |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| |
| for (i = 0; i < HDD_NUM_NL80211_BANDS; i++) { |
| if (wiphy->bands[i] && |
| (wiphy->bands[i]->channels)) |
| wiphy->bands[i]->channels = NULL; |
| } |
| wlan_hdd_iftype_data_mem_free(hdd_ctx); |
| qdf_mem_free(hdd_ctx->channels_5ghz); |
| qdf_mem_free(hdd_ctx->channels_2ghz); |
| hdd_ctx->channels_2ghz = NULL; |
| hdd_ctx->channels_5ghz = NULL; |
| |
| cipher_suites = wiphy->cipher_suites; |
| wiphy->cipher_suites = NULL; |
| wiphy->n_cipher_suites = 0; |
| qdf_mem_free((uint32_t *)cipher_suites); |
| cipher_suites = NULL; |
| hdd_reset_global_reg_params(); |
| } |
| |
| /** |
| * wlan_hdd_update_band_cap() - update capabilities for supported bands |
| * @hdd_ctx: HDD context |
| * |
| * this function will update capabilities for supported bands |
| * |
| * Return: void |
| */ |
| static void wlan_hdd_update_ht_cap(struct hdd_context *hdd_ctx) |
| { |
| struct mlme_ht_capabilities_info ht_cap_info = {0}; |
| QDF_STATUS status; |
| uint32_t channel_bonding_mode; |
| struct ieee80211_supported_band *band_2g; |
| struct ieee80211_supported_band *band_5g; |
| uint8_t i; |
| |
| status = ucfg_mlme_get_ht_cap_info(hdd_ctx->psoc, &ht_cap_info); |
| if (QDF_STATUS_SUCCESS != status) |
| hdd_err("could not get HT capability info"); |
| |
| band_2g = hdd_ctx->wiphy->bands[HDD_NL80211_BAND_2GHZ]; |
| band_5g = hdd_ctx->wiphy->bands[HDD_NL80211_BAND_5GHZ]; |
| |
| if (band_2g) { |
| if (ht_cap_info.tx_stbc) |
| band_2g->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; |
| |
| if (!sme_is_feature_supported_by_fw(DOT11AC)) { |
| band_2g->vht_cap.vht_supported = 0; |
| band_2g->vht_cap.cap = 0; |
| } |
| |
| if (!ht_cap_info.short_gi_20_mhz) |
| band_2g->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_20; |
| |
| for (i = 0; i < hdd_ctx->num_rf_chains; i++) |
| band_2g->ht_cap.mcs.rx_mask[i] = 0xff; |
| |
| /* |
| * According to mcs_nss HT MCS parameters highest data rate for |
| * Nss = 1 is 150 Mbps |
| */ |
| band_2g->ht_cap.mcs.rx_highest = |
| cpu_to_le16(150 * hdd_ctx->num_rf_chains); |
| } |
| |
| if (band_5g) { |
| if (ht_cap_info.tx_stbc) |
| band_5g->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; |
| |
| if (!sme_is_feature_supported_by_fw(DOT11AC)) { |
| band_5g->vht_cap.vht_supported = 0; |
| band_5g->vht_cap.cap = 0; |
| } |
| |
| if (!ht_cap_info.short_gi_20_mhz) |
| band_5g->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_20; |
| |
| if (!ht_cap_info.short_gi_40_mhz) |
| band_5g->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; |
| |
| ucfg_mlme_get_channel_bonding_5ghz(hdd_ctx->psoc, |
| &channel_bonding_mode); |
| if (!channel_bonding_mode) |
| band_5g->ht_cap.cap &= |
| ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; |
| |
| for (i = 0; i < hdd_ctx->num_rf_chains; i++) |
| band_5g->ht_cap.mcs.rx_mask[i] = 0xff; |
| /* |
| * According to mcs_nss HT MCS parameters highest data rate for |
| * Nss = 1 is 150 Mbps |
| */ |
| band_5g->ht_cap.mcs.rx_highest = |
| cpu_to_le16(150 * hdd_ctx->num_rf_chains); |
| } |
| } |
| |
| /** |
| * wlan_hdd_update_band_cap_in_wiphy() - update channel flags based on band cap |
| * @hdd_ctx: HDD context |
| * |
| * This function updates the channel flags based on the band capability set |
| * in the MLME CFG |
| * |
| * Return: void |
| */ |
| static void wlan_hdd_update_band_cap_in_wiphy(struct hdd_context *hdd_ctx) |
| { |
| int i, j; |
| uint32_t band_capability; |
| QDF_STATUS status; |
| struct ieee80211_supported_band *band; |
| |
| status = ucfg_mlme_get_band_capability(hdd_ctx->psoc, &band_capability); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("Failed to get MLME Band Capability"); |
| return; |
| } |
| |
| for (i = 0; i < HDD_NUM_NL80211_BANDS; i++) { |
| if (!hdd_ctx->wiphy->bands[i]) |
| continue; |
| |
| for (j = 0; j < hdd_ctx->wiphy->bands[i]->n_channels; j++) { |
| band = hdd_ctx->wiphy->bands[i]; |
| |
| if (HDD_NL80211_BAND_2GHZ == i && |
| BIT(REG_BAND_5G) == band_capability) { |
| /* 5G only */ |
| #ifdef WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY |
| /* Enable social channels for P2P */ |
| if (WLAN_HDD_IS_SOCIAL_CHANNEL |
| (band->channels[j].center_freq)) |
| band->channels[j].flags &= |
| ~IEEE80211_CHAN_DISABLED; |
| else |
| #endif |
| band->channels[j].flags |= |
| IEEE80211_CHAN_DISABLED; |
| continue; |
| } else if (HDD_NL80211_BAND_5GHZ == i && |
| BIT(REG_BAND_2G) == band_capability) { |
| /* 2G only */ |
| band->channels[j].flags |= |
| IEEE80211_CHAN_DISABLED; |
| continue; |
| } |
| } |
| } |
| } |
| |
| #ifdef FEATURE_WLAN_ESE |
| /** |
| * wlan_hdd_update_lfr_wiphy() - update LFR flag based on configures |
| * @hdd_ctx: HDD context |
| * |
| * This function updates the LFR flag based on LFR configures |
| * |
| * Return: void |
| */ |
| static void wlan_hdd_update_lfr_wiphy(struct hdd_context *hdd_ctx) |
| { |
| bool fast_transition_enabled; |
| bool lfr_enabled; |
| bool ese_enabled; |
| bool roam_offload; |
| |
| ucfg_mlme_is_fast_transition_enabled(hdd_ctx->psoc, |
| &fast_transition_enabled); |
| ucfg_mlme_is_lfr_enabled(hdd_ctx->psoc, &lfr_enabled); |
| ucfg_mlme_is_ese_enabled(hdd_ctx->psoc, &ese_enabled); |
| ucfg_mlme_get_roaming_offload(hdd_ctx->psoc, &roam_offload); |
| if (fast_transition_enabled || lfr_enabled || ese_enabled || |
| roam_offload) |
| hdd_ctx->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; |
| } |
| #else |
| static void wlan_hdd_update_lfr_wiphy(struct hdd_context *hdd_ctx) |
| { |
| bool fast_transition_enabled; |
| bool lfr_enabled; |
| bool roam_offload; |
| |
| ucfg_mlme_is_fast_transition_enabled(hdd_ctx->psoc, |
| &fast_transition_enabled); |
| ucfg_mlme_is_lfr_enabled(hdd_ctx->psoc, &lfr_enabled); |
| ucfg_mlme_get_roaming_offload(hdd_ctx->psoc, &roam_offload); |
| if (fast_transition_enabled || lfr_enabled || roam_offload) |
| hdd_ctx->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; |
| } |
| #endif |
| |
| /* |
| * In this function, wiphy structure is updated after QDF |
| * initialization. In wlan_hdd_cfg80211_init, only the |
| * default values will be initialized. The final initialization |
| * of all required members can be done here. |
| */ |
| void wlan_hdd_update_wiphy(struct hdd_context *hdd_ctx) |
| { |
| int value; |
| bool fils_enabled, mac_spoofing_enabled; |
| bool dfs_master_capable = true, is_oce_sta_enabled = false; |
| QDF_STATUS status; |
| struct wiphy *wiphy = hdd_ctx->wiphy; |
| uint8_t allow_mcc_go_diff_bi = 0, enable_mcc = 0; |
| bool is_bigtk_supported; |
| bool is_ocv_supported; |
| |
| if (!wiphy) { |
| hdd_err("Invalid wiphy"); |
| return; |
| } |
| ucfg_mlme_get_sap_max_peers(hdd_ctx->psoc, &value); |
| hdd_ctx->wiphy->max_ap_assoc_sta = value; |
| wlan_hdd_update_ht_cap(hdd_ctx); |
| wlan_hdd_update_band_cap_in_wiphy(hdd_ctx); |
| wlan_hdd_update_lfr_wiphy(hdd_ctx); |
| |
| fils_enabled = 0; |
| status = ucfg_mlme_get_fils_enabled_info(hdd_ctx->psoc, |
| &fils_enabled); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("could not get fils enabled info"); |
| if (fils_enabled) |
| wlan_hdd_cfg80211_set_wiphy_fils_feature(wiphy); |
| |
| status = ucfg_mlme_get_dfs_master_capability(hdd_ctx->psoc, |
| &dfs_master_capable); |
| if (QDF_IS_STATUS_SUCCESS(status) && dfs_master_capable) |
| wlan_hdd_cfg80211_set_dfs_offload_feature(wiphy); |
| |
| |
| status = ucfg_mlme_get_bigtk_support(hdd_ctx->psoc, |
| &is_bigtk_supported); |
| |
| if (QDF_IS_STATUS_SUCCESS(status) && is_bigtk_supported) |
| wlan_hdd_cfg80211_set_bigtk_flags(wiphy); |
| |
| status = ucfg_mlme_get_ocv_support(hdd_ctx->psoc, |
| &is_ocv_supported); |
| if (QDF_IS_STATUS_SUCCESS(status) && is_ocv_supported) |
| wlan_hdd_cfg80211_set_ocv_flags(wiphy); |
| |
| status = ucfg_mlme_get_oce_sta_enabled_info(hdd_ctx->psoc, |
| &is_oce_sta_enabled); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("could not get OCE STA enable info"); |
| if (is_oce_sta_enabled) |
| wlan_hdd_cfg80211_set_wiphy_oce_scan_flags(wiphy); |
| |
| wlan_hdd_cfg80211_set_wiphy_sae_feature(wiphy); |
| |
| if (QDF_STATUS_SUCCESS != |
| ucfg_policy_mgr_get_allow_mcc_go_diff_bi(hdd_ctx->psoc, |
| &allow_mcc_go_diff_bi)) |
| hdd_err("can't get mcc_go_diff_bi value, use default"); |
| |
| if (QDF_STATUS_SUCCESS != |
| ucfg_mlme_get_mcc_feature(hdd_ctx->psoc, &enable_mcc)) |
| hdd_err("can't get enable_mcc value, use default"); |
| |
| if (hdd_ctx->config->advertise_concurrent_operation) { |
| if (enable_mcc) { |
| int i; |
| |
| for (i = 0; |
| i < ARRAY_SIZE(wlan_hdd_iface_combination); |
| i++) { |
| if (!allow_mcc_go_diff_bi) |
| wlan_hdd_iface_combination[i]. |
| beacon_int_infra_match = true; |
| } |
| } |
| wiphy->n_iface_combinations = |
| ARRAY_SIZE(wlan_hdd_iface_combination); |
| wiphy->iface_combinations = wlan_hdd_iface_combination; |
| } |
| |
| mac_spoofing_enabled = ucfg_scan_is_mac_spoofing_enabled(hdd_ctx->psoc); |
| if (mac_spoofing_enabled) |
| wlan_hdd_cfg80211_scan_randomization_init(wiphy); |
| } |
| |
| /** |
| * wlan_hdd_update_11n_mode - update 11n mode in hdd cfg |
| * @cfg: hdd cfg |
| * |
| * this function update 11n mode in hdd cfg |
| * |
| * Return: void |
| */ |
| void wlan_hdd_update_11n_mode(struct hdd_context *hdd_ctx) |
| { |
| struct hdd_config *cfg = hdd_ctx->config; |
| |
| if (sme_is_feature_supported_by_fw(DOT11AC)) { |
| hdd_debug("support 11ac"); |
| } else { |
| hdd_debug("not support 11ac"); |
| if ((cfg->dot11Mode == eHDD_DOT11_MODE_11ac_ONLY) || |
| (cfg->dot11Mode == eHDD_DOT11_MODE_11ac)) { |
| cfg->dot11Mode = eHDD_DOT11_MODE_11n; |
| ucfg_mlme_set_sap_11ac_override(hdd_ctx->psoc, 0); |
| ucfg_mlme_set_go_11ac_override(hdd_ctx->psoc, 0); |
| } |
| } |
| } |
| |
| QDF_STATUS wlan_hdd_update_wiphy_supported_band(struct hdd_context *hdd_ctx) |
| { |
| int len_5g_ch, num_ch; |
| int num_dsrc_ch, len_dsrc_ch, num_srd_ch, len_srd_ch; |
| bool is_vht_for_24ghz = false; |
| QDF_STATUS status; |
| struct hdd_config *cfg = hdd_ctx->config; |
| struct wiphy *wiphy = hdd_ctx->wiphy; |
| |
| if (wiphy->registered) |
| return QDF_STATUS_SUCCESS; |
| |
| if (hdd_is_2g_supported(hdd_ctx)) { |
| if (!hdd_ctx->channels_2ghz) |
| return QDF_STATUS_E_NOMEM; |
| wiphy->bands[HDD_NL80211_BAND_2GHZ] = &wlan_hdd_band_2_4_ghz; |
| wiphy->bands[HDD_NL80211_BAND_2GHZ]->channels = |
| hdd_ctx->channels_2ghz; |
| qdf_mem_copy(wiphy->bands[HDD_NL80211_BAND_2GHZ]->channels, |
| &hdd_channels_2_4_ghz[0], |
| sizeof(hdd_channels_2_4_ghz)); |
| |
| status = ucfg_mlme_get_vht_for_24ghz(hdd_ctx->psoc, |
| &is_vht_for_24ghz); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("could not get VHT capability"); |
| |
| if (is_vht_for_24ghz && |
| sme_is_feature_supported_by_fw(DOT11AC) && |
| (cfg->dot11Mode == eHDD_DOT11_MODE_AUTO || |
| cfg->dot11Mode == eHDD_DOT11_MODE_11ac_ONLY || |
| cfg->dot11Mode == eHDD_DOT11_MODE_11ac || |
| cfg->dot11Mode == eHDD_DOT11_MODE_11ax || |
| cfg->dot11Mode == eHDD_DOT11_MODE_11ax_ONLY)) |
| wlan_hdd_band_2_4_ghz.vht_cap.vht_supported = 1; |
| } |
| if (!hdd_is_5g_supported(hdd_ctx) || |
| (eHDD_DOT11_MODE_11b == cfg->dot11Mode) || |
| (eHDD_DOT11_MODE_11g == cfg->dot11Mode) || |
| (eHDD_DOT11_MODE_11b_ONLY == cfg->dot11Mode) || |
| (eHDD_DOT11_MODE_11g_ONLY == cfg->dot11Mode)) |
| return QDF_STATUS_SUCCESS; |
| |
| if (!hdd_ctx->channels_5ghz) |
| return QDF_STATUS_E_NOMEM; |
| wiphy->bands[HDD_NL80211_BAND_5GHZ] = &wlan_hdd_band_5_ghz; |
| wiphy->bands[HDD_NL80211_BAND_5GHZ]->channels = hdd_ctx->channels_5ghz; |
| wlan_hdd_get_num_dsrc_ch_and_len(cfg, &num_dsrc_ch, &len_dsrc_ch); |
| wlan_hdd_get_num_srd_ch_and_len(cfg, &num_srd_ch, &len_srd_ch); |
| num_ch = QDF_ARRAY_SIZE(hdd_channels_5_ghz) + num_dsrc_ch + num_srd_ch; |
| len_5g_ch = sizeof(hdd_channels_5_ghz); |
| |
| wiphy->bands[HDD_NL80211_BAND_5GHZ]->n_channels = num_ch; |
| qdf_mem_copy(wiphy->bands[HDD_NL80211_BAND_5GHZ]->channels, |
| &hdd_channels_5_ghz[0], len_5g_ch); |
| if (num_dsrc_ch) |
| wlan_hdd_copy_dsrc_ch((char *)wiphy->bands[ |
| HDD_NL80211_BAND_5GHZ]->channels + |
| len_5g_ch, len_dsrc_ch); |
| if (num_srd_ch) |
| wlan_hdd_copy_srd_ch((char *)wiphy->bands[ |
| HDD_NL80211_BAND_5GHZ]->channels + |
| len_5g_ch, len_srd_ch); |
| |
| if (cfg->dot11Mode != eHDD_DOT11_MODE_AUTO && |
| cfg->dot11Mode != eHDD_DOT11_MODE_11ac && |
| cfg->dot11Mode != eHDD_DOT11_MODE_11ac_ONLY && |
| cfg->dot11Mode != eHDD_DOT11_MODE_11ax && |
| cfg->dot11Mode != eHDD_DOT11_MODE_11ax_ONLY) |
| wlan_hdd_band_5_ghz.vht_cap.vht_supported = 0; |
| |
| if (cfg->dot11Mode == eHDD_DOT11_MODE_AUTO || |
| cfg->dot11Mode == eHDD_DOT11_MODE_11ax || |
| cfg->dot11Mode == eHDD_DOT11_MODE_11ax_ONLY) |
| hdd_init_6ghz(hdd_ctx); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /* In this function we are registering wiphy. */ |
| int wlan_hdd_cfg80211_register(struct wiphy *wiphy) |
| { |
| int ret; |
| |
| hdd_enter(); |
| ret = wiphy_register(wiphy); |
| /* Register our wiphy dev with cfg80211 */ |
| if (ret < 0) { |
| hdd_err("wiphy register failed %d", ret); |
| return -EIO; |
| } |
| |
| hdd_exit(); |
| return 0; |
| } |
| |
| /* This function registers for all frame which supplicant is interested in */ |
| int wlan_hdd_cfg80211_register_frames(struct hdd_adapter *adapter) |
| { |
| mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter); |
| /* Register for all P2P action, public action etc frames */ |
| uint16_t type = (SIR_MAC_MGMT_FRAME << 2) | (SIR_MAC_MGMT_ACTION << 4); |
| QDF_STATUS status; |
| |
| hdd_enter(); |
| if (adapter->device_mode == QDF_FTM_MODE) { |
| hdd_info("No need to register frames in FTM mode"); |
| return 0; |
| } |
| |
| /* Register frame indication call back */ |
| status = sme_register_mgmt_frame_ind_callback(mac_handle, |
| hdd_indicate_mgmt_frame); |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_err("Failed to register hdd_indicate_mgmt_frame"); |
| goto ret_status; |
| } |
| |
| /* Right now we are registering these frame when driver is getting |
| * initialized. Once we will move to 2.6.37 kernel, in which we have |
| * frame register ops, we will move this code as a part of that |
| */ |
| |
| /* GAS Initial Request */ |
| status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) GAS_INITIAL_REQ, |
| GAS_INITIAL_REQ_SIZE); |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_err("Failed to register GAS_INITIAL_REQ"); |
| goto ret_status; |
| } |
| |
| /* GAS Initial Response */ |
| status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) GAS_INITIAL_RSP, |
| GAS_INITIAL_RSP_SIZE); |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_err("Failed to register GAS_INITIAL_RSP"); |
| goto dereg_gas_initial_req; |
| } |
| |
| /* GAS Comeback Request */ |
| status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) GAS_COMEBACK_REQ, |
| GAS_COMEBACK_REQ_SIZE); |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_err("Failed to register GAS_COMEBACK_REQ"); |
| goto dereg_gas_initial_rsp; |
| } |
| |
| /* GAS Comeback Response */ |
| status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) GAS_COMEBACK_RSP, |
| GAS_COMEBACK_RSP_SIZE); |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_err("Failed to register GAS_COMEBACK_RSP"); |
| goto dereg_gas_comeback_req; |
| } |
| |
| /* WNM BSS Transition Request frame */ |
| status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) WNM_BSS_ACTION_FRAME, |
| WNM_BSS_ACTION_FRAME_SIZE); |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_err("Failed to register WNM_BSS_ACTION_FRAME"); |
| goto dereg_gas_comeback_rsp; |
| } |
| |
| /* WNM-Notification */ |
| status = sme_register_mgmt_frame(mac_handle, adapter->vdev_id, type, |
| (uint8_t *) WNM_NOTIFICATION_FRAME, |
| WNM_NOTIFICATION_FRAME_SIZE); |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_err("Failed to register WNM_NOTIFICATION_FRAME"); |
| goto dereg_wnm_bss_action_frm; |
| } |
| |
| return 0; |
| |
| dereg_wnm_bss_action_frm: |
| sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) WNM_BSS_ACTION_FRAME, |
| WNM_BSS_ACTION_FRAME_SIZE); |
| dereg_gas_comeback_rsp: |
| sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) GAS_COMEBACK_RSP, |
| GAS_COMEBACK_RSP_SIZE); |
| dereg_gas_comeback_req: |
| sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) GAS_COMEBACK_REQ, |
| GAS_COMEBACK_REQ_SIZE); |
| dereg_gas_initial_rsp: |
| sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) GAS_INITIAL_RSP, |
| GAS_INITIAL_RSP_SIZE); |
| dereg_gas_initial_req: |
| sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) GAS_INITIAL_REQ, |
| GAS_INITIAL_REQ_SIZE); |
| ret_status: |
| return qdf_status_to_os_return(status); |
| } |
| |
| void wlan_hdd_cfg80211_deregister_frames(struct hdd_adapter *adapter) |
| { |
| mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter); |
| /* Deregister for all P2P action, public action etc frames */ |
| uint16_t type = (SIR_MAC_MGMT_FRAME << 2) | (SIR_MAC_MGMT_ACTION << 4); |
| |
| hdd_enter(); |
| |
| /* Right now we are registering these frame when driver is getting |
| * initialized. Once we will move to 2.6.37 kernel, in which we have |
| * frame register ops, we will move this code as a part of that |
| */ |
| |
| /* GAS Initial Request */ |
| |
| sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) GAS_INITIAL_REQ, |
| GAS_INITIAL_REQ_SIZE); |
| |
| /* GAS Initial Response */ |
| sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) GAS_INITIAL_RSP, |
| GAS_INITIAL_RSP_SIZE); |
| |
| /* GAS Comeback Request */ |
| sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) GAS_COMEBACK_REQ, |
| GAS_COMEBACK_REQ_SIZE); |
| |
| /* GAS Comeback Response */ |
| sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) GAS_COMEBACK_RSP, |
| GAS_COMEBACK_RSP_SIZE); |
| |
| /* P2P Public Action */ |
| sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) P2P_PUBLIC_ACTION_FRAME, |
| P2P_PUBLIC_ACTION_FRAME_SIZE); |
| |
| /* P2P Action */ |
| sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type, |
| (uint8_t *) P2P_ACTION_FRAME, |
| P2P_ACTION_FRAME_SIZE); |
| |
| /* WNM-Notification */ |
| sme_deregister_mgmt_frame(mac_handle, adapter->vdev_id, type, |
| (uint8_t *) WNM_NOTIFICATION_FRAME, |
| WNM_NOTIFICATION_FRAME_SIZE); |
| } |
| |
| bool wlan_hdd_is_ap_supports_immediate_power_save(uint8_t *ies, int length) |
| { |
| const uint8_t *vendor_ie; |
| |
| if (length < 2) { |
| hdd_debug("bss size is less than expected"); |
| return true; |
| } |
| if (!ies) { |
| hdd_debug("invalid IE pointer"); |
| return true; |
| } |
| vendor_ie = wlan_get_vendor_ie_ptr_from_oui(VENDOR1_AP_OUI_TYPE, |
| VENDOR1_AP_OUI_TYPE_SIZE, ies, length); |
| if (vendor_ie) { |
| hdd_debug("AP can't support immediate powersave. defer it"); |
| return false; |
| } |
| return true; |
| } |
| |
| /* |
| * FUNCTION: wlan_hdd_validate_operation_channel |
| * called by wlan_hdd_cfg80211_start_bss() and |
| * wlan_hdd_set_channel() |
| * This function validates whether given channel is part of valid |
| * channel list. |
| */ |
| QDF_STATUS wlan_hdd_validate_operation_channel(struct hdd_adapter *adapter, |
| uint32_t ch_freq) |
| { |
| bool value = 0; |
| uint32_t i; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| struct regulatory_channel *cur_chan_list; |
| QDF_STATUS status; |
| |
| status = ucfg_mlme_get_sap_allow_all_channels(hdd_ctx->psoc, &value); |
| if (status != QDF_STATUS_SUCCESS) |
| hdd_err("Unable to fetch sap allow all channels"); |
| status = QDF_STATUS_E_INVAL; |
| if (value) { |
| /* Validate the channel */ |
| for (i = CHAN_ENUM_2412; i < NUM_CHANNELS; i++) { |
| if (ch_freq == WLAN_REG_CH_TO_FREQ(i)) { |
| status = QDF_STATUS_SUCCESS; |
| break; |
| } |
| } |
| } else { |
| cur_chan_list = qdf_mem_malloc(NUM_CHANNELS * |
| sizeof(struct regulatory_channel)); |
| if (!cur_chan_list) |
| return QDF_STATUS_E_NOMEM; |
| |
| if (wlan_reg_get_secondary_current_chan_list( |
| hdd_ctx->pdev, cur_chan_list) != QDF_STATUS_SUCCESS) { |
| qdf_mem_free(cur_chan_list); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| for (i = 0; i < NUM_CHANNELS; i++) { |
| if (ch_freq != cur_chan_list[i].center_freq) |
| continue; |
| if (cur_chan_list[i].state != CHANNEL_STATE_DISABLE && |
| cur_chan_list[i].state != CHANNEL_STATE_INVALID) |
| status = QDF_STATUS_SUCCESS; |
| break; |
| } |
| qdf_mem_free(cur_chan_list); |
| } |
| |
| return status; |
| |
| } |
| |
| static int __wlan_hdd_cfg80211_change_bss(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct bss_parameters *params) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| int ret = 0; |
| QDF_STATUS qdf_ret_status; |
| mac_handle_t mac_handle; |
| void *soc = cds_get_context(QDF_MODULE_ID_SOC); |
| cdp_config_param_type vdev_param; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_CHANGE_BSS, |
| adapter->vdev_id, params->ap_isolate); |
| |
| hdd_debug("Device_mode %s(%d), ap_isolate = %d", |
| qdf_opmode_str(adapter->device_mode), |
| adapter->device_mode, params->ap_isolate); |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| if (!(adapter->device_mode == QDF_SAP_MODE || |
| adapter->device_mode == QDF_P2P_GO_MODE)) { |
| return -EOPNOTSUPP; |
| } |
| |
| /* ap_isolate == -1 means that in change bss, upper layer doesn't |
| * want to update this parameter |
| */ |
| if (-1 != params->ap_isolate) { |
| adapter->session.ap.disable_intrabss_fwd = |
| !!params->ap_isolate; |
| |
| mac_handle = hdd_ctx->mac_handle; |
| qdf_ret_status = sme_ap_disable_intra_bss_fwd(mac_handle, |
| adapter->vdev_id, |
| adapter->session. |
| ap. |
| disable_intrabss_fwd); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_ret_status)) |
| ret = -EINVAL; |
| |
| ucfg_ipa_set_ap_ibss_fwd(hdd_ctx->pdev, |
| adapter->vdev_id, |
| adapter->session.ap. |
| disable_intrabss_fwd); |
| |
| vdev_param.cdp_vdev_param_ap_brdg_en = |
| !adapter->session.ap.disable_intrabss_fwd; |
| cdp_txrx_set_vdev_param(soc, adapter->vdev_id, |
| CDP_ENABLE_AP_BRIDGE, |
| vdev_param); |
| } |
| |
| hdd_exit(); |
| return ret; |
| } |
| |
| /** |
| * hdd_change_adapter_mode() - change @adapter's operating mode to @new_mode |
| * @adapter: the adapter to change modes on |
| * @new_mode: the new operating mode to change to |
| * |
| * Return: Errno |
| */ |
| static int hdd_change_adapter_mode(struct hdd_adapter *adapter, |
| enum QDF_OPMODE new_mode) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| struct net_device *netdev = adapter->dev; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| |
| hdd_enter(); |
| |
| hdd_stop_adapter(hdd_ctx, adapter); |
| hdd_deinit_adapter(hdd_ctx, adapter, true); |
| adapter->device_mode = new_mode; |
| memset(&adapter->session, 0, sizeof(adapter->session)); |
| hdd_set_station_ops(netdev); |
| |
| hdd_exit(); |
| |
| return qdf_status_to_os_return(status); |
| } |
| |
| static int wlan_hdd_cfg80211_change_bss(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct bss_parameters *params) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_change_bss(wiphy, dev, params); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| static bool hdd_is_client_mode(enum QDF_OPMODE mode) |
| { |
| switch (mode) { |
| case QDF_STA_MODE: |
| case QDF_P2P_CLIENT_MODE: |
| case QDF_P2P_DEVICE_MODE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static bool hdd_is_ap_mode(enum QDF_OPMODE mode) |
| { |
| switch (mode) { |
| case QDF_SAP_MODE: |
| case QDF_P2P_GO_MODE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_change_iface() - change interface cfg80211 op |
| * @wiphy: Pointer to the wiphy structure |
| * @ndev: Pointer to the net device |
| * @type: Interface type |
| * @flags: Flags for change interface |
| * @params: Pointer to change interface parameters |
| * |
| * Return: 0 for success, error number on failure. |
| */ |
| static int __wlan_hdd_cfg80211_change_iface(struct wiphy *wiphy, |
| struct net_device *ndev, |
| enum nl80211_iftype type, |
| u32 *flags, |
| struct vif_params *params) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); |
| struct hdd_context *hdd_ctx; |
| bool iff_up = ndev->flags & IFF_UP; |
| enum QDF_OPMODE new_mode; |
| bool ap_random_bssid_enabled; |
| QDF_STATUS status; |
| int errno; |
| |
| hdd_enter(); |
| |
| if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| errno = wlan_hdd_validate_context(hdd_ctx); |
| if (errno) |
| return errno; |
| |
| if (wlan_hdd_is_mon_concurrency()) |
| return -EINVAL; |
| |
| if (policy_mgr_is_sta_mon_concurrency(hdd_ctx->psoc)) |
| return -EINVAL; |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_CHANGE_IFACE, |
| adapter->vdev_id, type); |
| |
| status = hdd_nl_to_qdf_iface_type(type, &new_mode); |
| if (QDF_IS_STATUS_ERROR(status)) |
| return qdf_status_to_os_return(status); |
| |
| /* A userspace issue leads to it sending a 'change to station mode' |
| * request on a "p2p" device, expecting the driver do execute a 'change |
| * to p2p-device mode' request instead. The (unfortunate) work around |
| * here is implemented by overriding the new mode if the net_device name |
| * starts with "p2p" and the requested mode was station. |
| */ |
| if (strnstr(ndev->name, "p2p", 3) && new_mode == QDF_STA_MODE) |
| new_mode = QDF_P2P_DEVICE_MODE; |
| |
| hdd_debug("Changing mode for '%s' from %s to %s", |
| ndev->name, |
| qdf_opmode_str(adapter->device_mode), |
| qdf_opmode_str(new_mode)); |
| |
| errno = hdd_trigger_psoc_idle_restart(hdd_ctx); |
| if (errno) { |
| hdd_err("Failed to restart psoc; errno:%d", errno); |
| return -EINVAL; |
| } |
| |
| /* Reset the current device mode bit mask */ |
| policy_mgr_clear_concurrency_mode(hdd_ctx->psoc, adapter->device_mode); |
| |
| if (hdd_is_client_mode(adapter->device_mode)) { |
| if (adapter->device_mode == QDF_STA_MODE) |
| hdd_cleanup_conn_info(adapter); |
| |
| if (hdd_is_client_mode(new_mode)) { |
| errno = hdd_change_adapter_mode(adapter, new_mode); |
| if (errno) { |
| hdd_err("change intf mode fail %d", errno); |
| goto err; |
| } |
| } else if (hdd_is_ap_mode(new_mode)) { |
| if (new_mode == QDF_P2P_GO_MODE) |
| wlan_hdd_cancel_existing_remain_on_channel |
| (adapter); |
| |
| hdd_stop_adapter(hdd_ctx, adapter); |
| hdd_deinit_adapter(hdd_ctx, adapter, true); |
| memset(&adapter->session, 0, sizeof(adapter->session)); |
| adapter->device_mode = new_mode; |
| |
| status = ucfg_mlme_get_ap_random_bssid_enable( |
| hdd_ctx->psoc, |
| &ap_random_bssid_enabled); |
| if (QDF_IS_STATUS_ERROR(status)) |
| return qdf_status_to_os_return(status); |
| |
| if (adapter->device_mode == QDF_SAP_MODE && |
| ap_random_bssid_enabled) { |
| /* To meet Android requirements create |
| * a randomized MAC address of the |
| * form 02:1A:11:Fx:xx:xx |
| */ |
| get_random_bytes(&ndev->dev_addr[3], 3); |
| ndev->dev_addr[0] = 0x02; |
| ndev->dev_addr[1] = 0x1A; |
| ndev->dev_addr[2] = 0x11; |
| ndev->dev_addr[3] |= 0xF0; |
| memcpy(adapter->mac_addr.bytes, ndev->dev_addr, |
| QDF_MAC_ADDR_SIZE); |
| pr_info("wlan: Generated HotSpot BSSID " |
| QDF_MAC_ADDR_FMT "\n", |
| QDF_MAC_ADDR_REF(ndev->dev_addr)); |
| } |
| |
| hdd_set_ap_ops(adapter->dev); |
| } else { |
| hdd_err("Changing to device mode '%s' is not supported", |
| qdf_opmode_str(new_mode)); |
| errno = -EOPNOTSUPP; |
| goto err; |
| } |
| } else if (hdd_is_ap_mode(adapter->device_mode)) { |
| if (hdd_is_client_mode(new_mode)) { |
| errno = hdd_change_adapter_mode(adapter, new_mode); |
| if (errno) { |
| hdd_err("change mode fail %d", errno); |
| goto err; |
| } |
| } else if (hdd_is_ap_mode(new_mode)) { |
| adapter->device_mode = new_mode; |
| |
| /* avoid starting the adapter, since it never stopped */ |
| iff_up = false; |
| } else { |
| hdd_err("Changing to device mode '%s' is not supported", |
| qdf_opmode_str(new_mode)); |
| errno = -EOPNOTSUPP; |
| goto err; |
| } |
| } else { |
| hdd_err("Changing from device mode '%s' is not supported", |
| qdf_opmode_str(adapter->device_mode)); |
| errno = -EOPNOTSUPP; |
| goto err; |
| } |
| |
| /* restart the adapter if it was up before the change iface request */ |
| if (iff_up) { |
| errno = hdd_start_adapter(adapter); |
| if (errno) { |
| hdd_err("Failed to start adapter"); |
| errno = -EINVAL; |
| goto err; |
| } |
| } |
| |
| ndev->ieee80211_ptr->iftype = type; |
| hdd_lpass_notify_mode_change(adapter); |
| err: |
| /* Set bitmask based on updated value */ |
| policy_mgr_set_concurrency_mode(hdd_ctx->psoc, adapter->device_mode); |
| |
| hdd_exit(); |
| |
| return errno; |
| } |
| |
| static int _wlan_hdd_cfg80211_change_iface(struct wiphy *wiphy, |
| struct net_device *net_dev, |
| enum nl80211_iftype type, |
| u32 *flags, |
| struct vif_params *params) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_trans_start(net_dev, &vdev_sync); |
| if (errno) |
| goto err; |
| |
| errno = __wlan_hdd_cfg80211_change_iface(wiphy, net_dev, type, |
| flags, params); |
| |
| osif_vdev_sync_trans_stop(vdev_sync); |
| |
| return errno; |
| err: |
| /* In the SSR case, errno will be -EINVAL from |
| * __dsc_vdev_can_trans with qdf_is_recovering() |
| * is true, only change -EINVAL to -EBUSY to make |
| * wpa_supplicant has chance to retry mode switch. |
| * Meanwhile do not touch the errno from |
| * __wlan_hdd_cfg80211_change_iface with this |
| * change. |
| */ |
| if (errno && errno != -EAGAIN && errno != -EBUSY) |
| errno = -EBUSY; |
| return errno; |
| } |
| |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) |
| /** |
| * wlan_hdd_cfg80211_change_iface() - change interface cfg80211 op |
| * @wiphy: Pointer to the wiphy structure |
| * @ndev: Pointer to the net device |
| * @type: Interface type |
| * @flags: Flags for change interface |
| * @params: Pointer to change interface parameters |
| * |
| * Return: 0 for success, error number on failure. |
| */ |
| static int wlan_hdd_cfg80211_change_iface(struct wiphy *wiphy, |
| struct net_device *ndev, |
| enum nl80211_iftype type, |
| u32 *flags, |
| struct vif_params *params) |
| { |
| return _wlan_hdd_cfg80211_change_iface(wiphy, ndev, type, |
| flags, params); |
| } |
| #else |
| static int wlan_hdd_cfg80211_change_iface(struct wiphy *wiphy, |
| struct net_device *ndev, |
| enum nl80211_iftype type, |
| struct vif_params *params) |
| { |
| return _wlan_hdd_cfg80211_change_iface(wiphy, ndev, type, |
| ¶ms->flags, params); |
| } |
| #endif /* KERNEL_VERSION(4, 12, 0) */ |
| |
| QDF_STATUS wlan_hdd_send_sta_authorized_event( |
| struct hdd_adapter *adapter, |
| struct hdd_context *hdd_ctx, |
| const struct qdf_mac_addr *mac_addr) |
| { |
| struct sk_buff *vendor_event; |
| QDF_STATUS status; |
| struct nl80211_sta_flag_update sta_flags; |
| |
| hdd_enter(); |
| if (!hdd_ctx) { |
| hdd_err("HDD context is null"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| vendor_event = |
| cfg80211_vendor_event_alloc( |
| hdd_ctx->wiphy, &adapter->wdev, sizeof(sta_flags) + |
| QDF_MAC_ADDR_SIZE + NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES_INDEX, |
| GFP_KERNEL); |
| if (!vendor_event) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| qdf_mem_zero(&sta_flags, sizeof(sta_flags)); |
| |
| sta_flags.mask |= BIT(NL80211_STA_FLAG_AUTHORIZED); |
| sta_flags.set = true; |
| |
| status = nla_put(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS, |
| sizeof(struct nl80211_sta_flag_update), |
| &sta_flags); |
| if (status) { |
| hdd_err("STA flag put fails"); |
| kfree_skb(vendor_event); |
| return QDF_STATUS_E_FAILURE; |
| } |
| status = nla_put(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR, |
| QDF_MAC_ADDR_SIZE, mac_addr->bytes); |
| if (status) { |
| hdd_err("STA MAC put fails"); |
| kfree_skb(vendor_event); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| cfg80211_vendor_event(vendor_event, GFP_KERNEL); |
| |
| hdd_exit(); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * __wlan_hdd_change_station() - change station |
| * @wiphy: Pointer to the wiphy structure |
| * @dev: Pointer to the net device. |
| * @mac: bssid |
| * @params: Pointer to station parameters |
| * |
| * Return: 0 for success, error number on failure. |
| */ |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) |
| static int __wlan_hdd_change_station(struct wiphy *wiphy, |
| struct net_device *dev, |
| const uint8_t *mac, |
| struct station_parameters *params) |
| #else |
| static int __wlan_hdd_change_station(struct wiphy *wiphy, |
| struct net_device *dev, |
| uint8_t *mac, |
| struct station_parameters *params) |
| #endif |
| { |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx; |
| struct hdd_station_ctx *sta_ctx; |
| struct hdd_ap_ctx *ap_ctx; |
| struct qdf_mac_addr sta_macaddr; |
| int ret; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CHANGE_STATION, |
| adapter->vdev_id, params->listen_interval); |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| |
| qdf_mem_copy(sta_macaddr.bytes, mac, QDF_MAC_ADDR_SIZE); |
| |
| if ((adapter->device_mode == QDF_SAP_MODE) || |
| (adapter->device_mode == QDF_P2P_GO_MODE)) { |
| if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) { |
| ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| /* |
| * For Encrypted SAP session, this will be done as |
| * part of eSAP_STA_SET_KEY_EVENT |
| */ |
| if (ap_ctx->encryption_type != |
| eCSR_ENCRYPT_TYPE_NONE) { |
| hdd_debug("Encrypt type %d, not setting peer authorized now", |
| ap_ctx->encryption_type); |
| return 0; |
| } |
| |
| status = |
| hdd_softap_change_sta_state(adapter, |
| &sta_macaddr, |
| OL_TXRX_PEER_STATE_AUTH); |
| |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_debug("Not able to change TL state to AUTHENTICATED"); |
| return -EINVAL; |
| } |
| status = wlan_hdd_send_sta_authorized_event( |
| adapter, |
| hdd_ctx, |
| &sta_macaddr); |
| if (status != QDF_STATUS_SUCCESS) { |
| return -EINVAL; |
| } |
| } |
| } else if ((adapter->device_mode == QDF_STA_MODE) || |
| (adapter->device_mode == QDF_P2P_CLIENT_MODE)) { |
| if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) { |
| #if defined(FEATURE_WLAN_TDLS) |
| struct wlan_objmgr_vdev *vdev; |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, |
| WLAN_OSIF_TDLS_ID); |
| if (!vdev) |
| return -EINVAL; |
| ret = wlan_cfg80211_tdls_update_peer(vdev, mac, params); |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_TDLS_ID); |
| #endif |
| } |
| } |
| hdd_exit(); |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_change_station() - cfg80211 change station handler function |
| * @wiphy: Pointer to the wiphy structure |
| * @dev: Pointer to the net device. |
| * @mac: bssid |
| * @params: Pointer to station parameters |
| * |
| * This is the cfg80211 change station handler function which invokes |
| * the internal function @__wlan_hdd_change_station with |
| * SSR protection. |
| * |
| * Return: 0 for success, error number on failure. |
| */ |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) || defined(WITH_BACKPORTS) |
| static int wlan_hdd_change_station(struct wiphy *wiphy, |
| struct net_device *dev, |
| const u8 *mac, |
| struct station_parameters *params) |
| #else |
| static int wlan_hdd_change_station(struct wiphy *wiphy, |
| struct net_device *dev, |
| u8 *mac, |
| struct station_parameters *params) |
| #endif |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_change_station(wiphy, dev, mac, params); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #ifdef FEATURE_WLAN_ESE |
| static bool hdd_is_krk_enc_type(uint32_t cipher_type) |
| { |
| if (cipher_type == WLAN_CIPHER_SUITE_KRK) |
| return true; |
| |
| return false; |
| } |
| #else |
| static bool hdd_is_krk_enc_type(uint32_t cipher_type) |
| { |
| return false; |
| } |
| #endif |
| |
| #if defined(FEATURE_WLAN_ESE) && defined(WLAN_FEATURE_ROAM_OFFLOAD) |
| static bool hdd_is_btk_enc_type(uint32_t cipher_type) |
| { |
| if (cipher_type == WLAN_CIPHER_SUITE_BTK) |
| return true; |
| |
| return false; |
| } |
| #else |
| static bool hdd_is_btk_enc_type(uint32_t cipher_type) |
| { |
| return false; |
| } |
| #endif |
| |
| static int wlan_hdd_add_key_sap(struct hdd_adapter *adapter, |
| bool pairwise, u8 key_index, |
| enum wlan_crypto_cipher_type cipher) |
| { |
| struct wlan_objmgr_vdev *vdev; |
| int errno = 0; |
| struct hdd_hostapd_state *hostapd_state = |
| WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_ID); |
| if (!vdev) |
| return -EINVAL; |
| |
| /* Do not send install key when sap restart is in progress. If there is |
| * critical channel request handling going on, fw will stop that request |
| * and will not send restart resposne |
| */ |
| if (wlan_vdev_is_restart_progress(vdev) == QDF_STATUS_SUCCESS) { |
| hdd_err("vdev: %d restart in progress", wlan_vdev_get_id(vdev)); |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| return -EINVAL; |
| } |
| |
| if (hostapd_state->bss_state == BSS_START) { |
| errno = |
| wlan_cfg80211_crypto_add_key(vdev, |
| (pairwise ? |
| WLAN_CRYPTO_KEY_TYPE_UNICAST : |
| WLAN_CRYPTO_KEY_TYPE_GROUP), |
| key_index, true); |
| if (!errno) |
| wma_update_set_key(adapter->vdev_id, pairwise, |
| key_index, cipher); |
| } |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| |
| return errno; |
| } |
| |
| static int wlan_hdd_add_key_sta(struct wlan_objmgr_pdev *pdev, |
| struct hdd_adapter *adapter, |
| bool pairwise, u8 key_index, bool *ft_mode) |
| { |
| struct wlan_objmgr_vdev *vdev; |
| int errno; |
| QDF_STATUS status; |
| |
| /* The supplicant may attempt to set the PTK once |
| * pre-authentication is done. Save the key in the |
| * UMAC and install it after association |
| */ |
| status = ucfg_cm_check_ft_status(pdev, adapter->vdev_id); |
| if (status == QDF_STATUS_SUCCESS) { |
| *ft_mode = true; |
| return 0; |
| } |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_ID); |
| if (!vdev) |
| return -EINVAL; |
| errno = wlan_cfg80211_crypto_add_key(vdev, (pairwise ? |
| WLAN_CRYPTO_KEY_TYPE_UNICAST : |
| WLAN_CRYPTO_KEY_TYPE_GROUP), |
| key_index, true); |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| if (!errno && adapter->send_mode_change) { |
| wlan_hdd_send_mode_change_event(); |
| adapter->send_mode_change = false; |
| } |
| |
| return errno; |
| } |
| |
| static int __wlan_hdd_cfg80211_add_key(struct wiphy *wiphy, |
| struct net_device *ndev, |
| u8 key_index, bool pairwise, |
| const u8 *mac_addr, |
| struct key_params *params) |
| { |
| struct hdd_context *hdd_ctx; |
| mac_handle_t mac_handle; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); |
| struct wlan_objmgr_vdev *vdev; |
| bool ft_mode = false; |
| enum wlan_crypto_cipher_type cipher; |
| int errno; |
| int32_t cipher_cap, ucast_cipher = 0; |
| struct qdf_mac_addr mac_address; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_ADD_KEY, |
| adapter->vdev_id, params->key_len); |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| errno = wlan_hdd_validate_context(hdd_ctx); |
| if (errno) |
| return errno; |
| |
| hdd_debug("converged Device_mode %s(%d) index %d, pairwise %d", |
| qdf_opmode_str(adapter->device_mode), |
| adapter->device_mode, key_index, pairwise); |
| mac_handle = hdd_ctx->mac_handle; |
| |
| if (hdd_is_btk_enc_type(params->cipher)) |
| return sme_add_key_btk(mac_handle, adapter->vdev_id, |
| params->key, params->key_len); |
| if (hdd_is_krk_enc_type(params->cipher)) |
| return sme_add_key_krk(mac_handle, adapter->vdev_id, |
| params->key, params->key_len); |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_ID); |
| if (!vdev) |
| return -EINVAL; |
| if (!pairwise && ((adapter->device_mode == QDF_STA_MODE) || |
| (adapter->device_mode == QDF_P2P_CLIENT_MODE))) { |
| qdf_mem_copy(mac_address.bytes, |
| adapter->session.station.conn_info.bssid.bytes, |
| QDF_MAC_ADDR_SIZE); |
| } else { |
| if (mac_addr) |
| qdf_mem_copy(mac_address.bytes, mac_addr, |
| QDF_MAC_ADDR_SIZE); |
| } |
| errno = wlan_cfg80211_store_key(vdev, key_index, |
| (pairwise ? |
| WLAN_CRYPTO_KEY_TYPE_UNICAST : |
| WLAN_CRYPTO_KEY_TYPE_GROUP), |
| mac_address.bytes, params); |
| cipher_cap = wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_CIPHER_CAP); |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| if (errno) |
| return errno; |
| cipher = osif_nl_to_crypto_cipher_type(params->cipher); |
| QDF_SET_PARAM(ucast_cipher, cipher); |
| if (pairwise) |
| wma_set_peer_ucast_cipher(mac_address.bytes, |
| ucast_cipher, cipher_cap); |
| |
| cdp_peer_flush_frags(cds_get_context(QDF_MODULE_ID_SOC), |
| wlan_vdev_get_id(vdev), mac_address.bytes); |
| |
| switch (adapter->device_mode) { |
| case QDF_SAP_MODE: |
| case QDF_P2P_GO_MODE: |
| errno = wlan_hdd_add_key_sap(adapter, pairwise, |
| key_index, cipher); |
| break; |
| case QDF_STA_MODE: |
| case QDF_P2P_CLIENT_MODE: |
| errno = wlan_hdd_add_key_sta(hdd_ctx->pdev, adapter, pairwise, |
| key_index, &ft_mode); |
| if (ft_mode) |
| return 0; |
| break; |
| default: |
| break; |
| } |
| if (!errno && (adapter->device_mode != QDF_SAP_MODE)) |
| wma_update_set_key(adapter->vdev_id, pairwise, key_index, |
| cipher); |
| hdd_exit(); |
| |
| return errno; |
| } |
| |
| #ifdef CFG80211_KEY_INSTALL_SUPPORT_ON_WDEV |
| #ifdef CFG80211_SET_KEY_WITH_SRC_MAC |
| static int wlan_hdd_cfg80211_add_key(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| u8 key_index, bool pairwise, |
| const u8 *src_addr, |
| const u8 *mac_addr, |
| struct key_params *params) |
| #else |
| static int wlan_hdd_cfg80211_add_key(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| u8 key_index, bool pairwise, |
| const u8 *mac_addr, |
| struct key_params *params) |
| #endif |
| { |
| int errno = -EINVAL; |
| struct osif_vdev_sync *vdev_sync; |
| struct hdd_adapter *adapter = qdf_container_of(wdev, |
| struct hdd_adapter, |
| wdev); |
| |
| if (!adapter || wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return errno; |
| |
| errno = osif_vdev_sync_op_start(adapter->dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_add_key(wiphy, adapter->dev, key_index, |
| pairwise, mac_addr, params); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #else |
| #ifdef CFG80211_SET_KEY_WITH_SRC_MAC |
| static int wlan_hdd_cfg80211_add_key(struct wiphy *wiphy, |
| struct net_device *ndev, |
| u8 key_index, bool pairwise, |
| const u8 *src_addr, |
| const u8 *mac_addr, |
| struct key_params *params) |
| #else |
| static int wlan_hdd_cfg80211_add_key(struct wiphy *wiphy, |
| struct net_device *ndev, |
| u8 key_index, bool pairwise, |
| const u8 *mac_addr, |
| struct key_params *params) |
| #endif |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(ndev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_add_key(wiphy, ndev, key_index, pairwise, |
| mac_addr, params); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| |
| /* |
| * FUNCTION: __wlan_hdd_cfg80211_get_key |
| * This function is used to get the key information |
| */ |
| static int __wlan_hdd_cfg80211_get_key(struct wiphy *wiphy, |
| struct net_device *ndev, |
| u8 key_index, bool pairwise, |
| const u8 *mac_addr, void *cookie, |
| void (*callback)(void *cookie, |
| struct key_params *) |
| ) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); |
| struct key_params params; |
| eCsrEncryptionType enc_type; |
| int32_t ucast_cipher = 0; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| hdd_debug("Device_mode %s(%d)", |
| qdf_opmode_str(adapter->device_mode), adapter->device_mode); |
| |
| memset(¶ms, 0, sizeof(params)); |
| |
| if (key_index >= (WLAN_CRYPTO_MAXKEYIDX + WLAN_CRYPTO_MAXIGTKKEYIDX + |
| WLAN_CRYPTO_MAXBIGTKKEYIDX)) { |
| hdd_err("Invalid key index: %d", key_index); |
| return -EINVAL; |
| } |
| if (adapter->vdev) |
| ucast_cipher = wlan_crypto_get_param(adapter->vdev, |
| WLAN_CRYPTO_PARAM_UCAST_CIPHER); |
| |
| sme_fill_enc_type(&enc_type, ucast_cipher); |
| |
| switch (enc_type) { |
| case eCSR_ENCRYPT_TYPE_NONE: |
| params.cipher = IW_AUTH_CIPHER_NONE; |
| break; |
| |
| case eCSR_ENCRYPT_TYPE_WEP40_STATICKEY: |
| case eCSR_ENCRYPT_TYPE_WEP40: |
| params.cipher = WLAN_CIPHER_SUITE_WEP40; |
| break; |
| |
| case eCSR_ENCRYPT_TYPE_WEP104_STATICKEY: |
| case eCSR_ENCRYPT_TYPE_WEP104: |
| params.cipher = WLAN_CIPHER_SUITE_WEP104; |
| break; |
| |
| case eCSR_ENCRYPT_TYPE_TKIP: |
| params.cipher = WLAN_CIPHER_SUITE_TKIP; |
| break; |
| |
| case eCSR_ENCRYPT_TYPE_AES: |
| params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; |
| break; |
| case eCSR_ENCRYPT_TYPE_AES_GCMP: |
| params.cipher = WLAN_CIPHER_SUITE_GCMP; |
| break; |
| case eCSR_ENCRYPT_TYPE_AES_GCMP_256: |
| params.cipher = WLAN_CIPHER_SUITE_GCMP_256; |
| break; |
| default: |
| params.cipher = IW_AUTH_CIPHER_NONE; |
| break; |
| } |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_GET_KEY, |
| adapter->vdev_id, params.cipher); |
| |
| params.key_len = 0; |
| params.seq_len = 0; |
| params.seq = NULL; |
| params.key = NULL; |
| callback(cookie, ¶ms); |
| |
| hdd_exit(); |
| return 0; |
| } |
| |
| #ifdef CFG80211_KEY_INSTALL_SUPPORT_ON_WDEV |
| static int wlan_hdd_cfg80211_get_key(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| u8 key_index, bool pairwise, |
| const u8 *mac_addr, void *cookie, |
| void (*callback)(void *cookie, |
| struct key_params *) |
| ) |
| { |
| int errno = -EINVAL; |
| struct osif_vdev_sync *vdev_sync; |
| struct hdd_adapter *adapter = qdf_container_of(wdev, |
| struct hdd_adapter, |
| wdev); |
| |
| if (!adapter || wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return errno; |
| |
| errno = osif_vdev_sync_op_start(adapter->dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_key(wiphy, adapter->dev, key_index, |
| pairwise, mac_addr, cookie, |
| callback); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #else |
| static int wlan_hdd_cfg80211_get_key(struct wiphy *wiphy, |
| struct net_device *ndev, |
| u8 key_index, bool pairwise, |
| const u8 *mac_addr, void *cookie, |
| void (*callback)(void *cookie, |
| struct key_params *) |
| ) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(ndev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_key(wiphy, ndev, key_index, pairwise, |
| mac_addr, cookie, callback); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| |
| /** |
| * __wlan_hdd_cfg80211_del_key() - Delete the encryption key for station |
| * @wiphy: wiphy interface context |
| * @ndev: pointer to net device |
| * @key_index: Key index used in 802.11 frames |
| * @unicast: true if it is unicast key |
| * @multicast: true if it is multicast key |
| * |
| * This function is required for cfg80211_ops API. |
| * It is used to delete the key information |
| * Underlying hardware implementation does not have API to delete the |
| * encryption key. It is automatically deleted when the peer is |
| * removed. Hence this function currently does nothing. |
| * Future implementation may interprete delete key operation to |
| * replacing the key with a random junk value, effectively making it |
| * useless. |
| * |
| * Return: status code, always 0. |
| */ |
| |
| static int __wlan_hdd_cfg80211_del_key(struct wiphy *wiphy, |
| struct net_device *ndev, |
| u8 key_index, |
| bool pairwise, const u8 *mac_addr) |
| { |
| hdd_exit(); |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_del_key() - cfg80211 delete key handler function |
| * @wiphy: Pointer to wiphy structure. |
| * @wdev: Pointer to wireless_dev structure. |
| * @key_index: key index |
| * @pairwise: pairwise |
| * @mac_addr: mac address |
| * |
| * This is the cfg80211 delete key handler function which invokes |
| * the internal function @__wlan_hdd_cfg80211_del_key with |
| * SSR protection. |
| * |
| * Return: 0 for success, error number on failure. |
| */ |
| #ifdef CFG80211_KEY_INSTALL_SUPPORT_ON_WDEV |
| static int wlan_hdd_cfg80211_del_key(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| u8 key_index, |
| bool pairwise, const u8 *mac_addr) |
| { |
| int errno = -EINVAL; |
| struct osif_vdev_sync *vdev_sync; |
| struct hdd_adapter *adapter = qdf_container_of(wdev, |
| struct hdd_adapter, |
| wdev); |
| |
| if (!adapter || wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return errno; |
| |
| errno = osif_vdev_sync_op_start(adapter->dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_del_key(wiphy, adapter->dev, key_index, |
| pairwise, mac_addr); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #else |
| static int wlan_hdd_cfg80211_del_key(struct wiphy *wiphy, |
| struct net_device *dev, |
| u8 key_index, |
| bool pairwise, const u8 *mac_addr) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_del_key(wiphy, dev, key_index, |
| pairwise, mac_addr); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| static int __wlan_hdd_cfg80211_set_default_key(struct wiphy *wiphy, |
| struct net_device *ndev, |
| u8 key_index, |
| bool unicast, bool multicast) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); |
| struct hdd_context *hdd_ctx; |
| struct qdf_mac_addr bssid = QDF_MAC_ADDR_BCAST_INIT; |
| struct hdd_ap_ctx *ap_ctx; |
| struct wlan_crypto_key *crypto_key; |
| struct wlan_objmgr_vdev *vdev; |
| int ret; |
| QDF_STATUS status; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_SET_DEFAULT_KEY, |
| adapter->vdev_id, key_index); |
| |
| hdd_debug("Device_mode %s(%d) key_index = %d", |
| qdf_opmode_str(adapter->device_mode), |
| adapter->device_mode, key_index); |
| |
| if (key_index >= (WLAN_CRYPTO_MAXKEYIDX + WLAN_CRYPTO_MAXIGTKKEYIDX + |
| WLAN_CRYPTO_MAXBIGTKKEYIDX)) { |
| hdd_err("Invalid key index: %d", key_index); |
| return -EINVAL; |
| } |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| |
| if (0 != ret) |
| return ret; |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_ID); |
| if (!vdev) |
| return -EINVAL; |
| |
| crypto_key = wlan_crypto_get_key(vdev, key_index); |
| if (!crypto_key) { |
| hdd_err("Invalid NULL key info"); |
| ret = -EINVAL; |
| goto out; |
| } |
| hdd_debug("unicast %d, multicast %d cipher %d", |
| unicast, multicast, crypto_key->cipher_type); |
| if (!IS_WEP_CIPHER(crypto_key->cipher_type)) { |
| ret = 0; |
| goto out; |
| } |
| |
| if ((adapter->device_mode == QDF_STA_MODE) || |
| (adapter->device_mode == QDF_P2P_CLIENT_MODE)) { |
| ret = |
| wlan_cfg80211_crypto_add_key(vdev, |
| (unicast ? |
| WLAN_CRYPTO_KEY_TYPE_UNICAST : |
| WLAN_CRYPTO_KEY_TYPE_GROUP), |
| key_index, true); |
| wma_update_set_key(adapter->vdev_id, unicast, key_index, |
| crypto_key->cipher_type); |
| } |
| |
| if (adapter->device_mode == QDF_SAP_MODE || |
| adapter->device_mode == QDF_P2P_GO_MODE) { |
| status = wlan_cfg80211_set_default_key(vdev, key_index, |
| &bssid); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("ret fail status %d", ret); |
| ret = -EINVAL; |
| goto out; |
| } |
| ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| ap_ctx->wep_def_key_idx = key_index; |
| } |
| |
| out: |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| return ret; |
| } |
| |
| #ifdef CFG80211_KEY_INSTALL_SUPPORT_ON_WDEV |
| static int wlan_hdd_cfg80211_set_default_key(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| u8 key_index, |
| bool unicast, bool multicast) |
| { |
| int errno = -EINVAL; |
| struct osif_vdev_sync *vdev_sync; |
| struct hdd_adapter *adapter = qdf_container_of(wdev, |
| struct hdd_adapter, |
| wdev); |
| |
| if (!adapter || wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return errno; |
| |
| errno = osif_vdev_sync_op_start(adapter->dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_default_key(wiphy, adapter->dev, |
| key_index, unicast, |
| multicast); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #else |
| static int wlan_hdd_cfg80211_set_default_key(struct wiphy *wiphy, |
| struct net_device *ndev, |
| u8 key_index, |
| bool unicast, bool multicast) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(ndev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_default_key(wiphy, ndev, key_index, |
| unicast, multicast); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| |
| #if defined (CFG80211_BIGTK_CONFIGURATION_SUPPORT) || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)) |
| static int _wlan_hdd_cfg80211_set_default_beacon_key(struct wiphy *wiphy, |
| struct net_device *ndev, |
| u8 key_index) |
| { |
| hdd_enter(); |
| return 0; |
| } |
| |
| #ifdef CFG80211_KEY_INSTALL_SUPPORT_ON_WDEV |
| static int wlan_hdd_cfg80211_set_default_beacon_key(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| u8 key_index) |
| { |
| int errno = -EINVAL; |
| struct osif_vdev_sync *vdev_sync; |
| struct hdd_adapter *adapter = qdf_container_of(wdev, |
| struct hdd_adapter, |
| wdev); |
| |
| if (!adapter || wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return errno; |
| |
| errno = osif_vdev_sync_op_start(adapter->dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = _wlan_hdd_cfg80211_set_default_beacon_key(wiphy, adapter->dev, |
| key_index); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #else |
| static int wlan_hdd_cfg80211_set_default_beacon_key(struct wiphy *wiphy, |
| struct net_device *ndev, |
| u8 key_index) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(ndev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = _wlan_hdd_cfg80211_set_default_beacon_key(wiphy, ndev, |
| key_index); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| #endif |
| |
| #ifdef FEATURE_MONITOR_MODE_SUPPORT |
| static |
| void hdd_mon_select_cbmode(struct hdd_adapter *adapter, |
| uint32_t op_freq, |
| struct ch_params *ch_params) |
| { |
| struct hdd_station_ctx *station_ctx = |
| WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| struct hdd_mon_set_ch_info *ch_info = &station_ctx->ch_info; |
| enum hdd_dot11_mode hdd_dot11_mode; |
| uint8_t ini_dot11_mode = |
| (WLAN_HDD_GET_CTX(adapter))->config->dot11Mode; |
| |
| hdd_debug("Dot11Mode is %u", ini_dot11_mode); |
| switch (ini_dot11_mode) { |
| case eHDD_DOT11_MODE_AUTO: |
| #ifdef WLAN_FEATURE_11BE |
| case eHDD_DOT11_MODE_11be: |
| case eHDD_DOT11_MODE_11be_ONLY: |
| if (sme_is_feature_supported_by_fw(DOT11BE)) |
| hdd_dot11_mode = eHDD_DOT11_MODE_11be; |
| else |
| #endif |
| if (sme_is_feature_supported_by_fw(DOT11AX)) |
| hdd_dot11_mode = eHDD_DOT11_MODE_11ax; |
| else if (sme_is_feature_supported_by_fw(DOT11AC)) |
| hdd_dot11_mode = eHDD_DOT11_MODE_11ac; |
| else |
| hdd_dot11_mode = eHDD_DOT11_MODE_11n; |
| break; |
| case eHDD_DOT11_MODE_11ax: |
| case eHDD_DOT11_MODE_11ax_ONLY: |
| if (sme_is_feature_supported_by_fw(DOT11AX)) |
| hdd_dot11_mode = eHDD_DOT11_MODE_11ax; |
| else if (sme_is_feature_supported_by_fw(DOT11AC)) |
| hdd_dot11_mode = eHDD_DOT11_MODE_11ac; |
| else |
| hdd_dot11_mode = eHDD_DOT11_MODE_11n; |
| break; |
| case eHDD_DOT11_MODE_11ac: |
| case eHDD_DOT11_MODE_11ac_ONLY: |
| if (sme_is_feature_supported_by_fw(DOT11AC)) |
| hdd_dot11_mode = eHDD_DOT11_MODE_11ac; |
| else |
| hdd_dot11_mode = eHDD_DOT11_MODE_11n; |
| break; |
| case eHDD_DOT11_MODE_11n: |
| case eHDD_DOT11_MODE_11n_ONLY: |
| hdd_dot11_mode = eHDD_DOT11_MODE_11n; |
| break; |
| default: |
| hdd_dot11_mode = ini_dot11_mode; |
| break; |
| } |
| ch_info->channel_width = ch_params->ch_width; |
| ch_info->phy_mode = |
| hdd_cfg_xlate_to_csr_phy_mode(hdd_dot11_mode); |
| ch_info->freq = op_freq; |
| ch_info->cb_mode = ch_params->ch_width; |
| hdd_debug("ch_info width %d, phymode %d channel freq %d", |
| ch_info->channel_width, ch_info->phy_mode, |
| ch_info->freq); |
| } |
| #else |
| static |
| void hdd_mon_select_cbmode(struct hdd_adapter *adapter, |
| uint32_t op_freq, |
| struct ch_params *ch_params) |
| { |
| } |
| #endif |
| |
| void hdd_select_cbmode(struct hdd_adapter *adapter, qdf_freq_t oper_freq, |
| qdf_freq_t sec_ch_2g_freq, struct ch_params *ch_params) |
| { |
| uint32_t sec_ch_freq = 0; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| |
| /* |
| * CDS api expects secondary channel for calculating |
| * the channel params |
| */ |
| if (ch_params->ch_width == CH_WIDTH_40MHZ && |
| WLAN_REG_IS_24GHZ_CH_FREQ(oper_freq)) { |
| if (sec_ch_2g_freq) { |
| sec_ch_freq = sec_ch_2g_freq; |
| } else { |
| if (oper_freq >= 2412 && oper_freq <= 2432) |
| sec_ch_freq = oper_freq + 20; |
| else if (oper_freq >= 2437 && oper_freq <= 2472) |
| sec_ch_freq = oper_freq - 20; |
| } |
| } |
| |
| /* This call decides required channel bonding mode */ |
| wlan_reg_set_channel_params_for_freq(hdd_ctx->pdev, oper_freq, |
| sec_ch_freq, ch_params); |
| |
| if (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE || |
| policy_mgr_is_sta_mon_concurrency(hdd_ctx->psoc)) |
| hdd_mon_select_cbmode(adapter, oper_freq, ch_params); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_connect() - cfg80211 connect api |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @req: Pointer to cfg80211 connect request |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int wlan_hdd_cfg80211_connect(struct wiphy *wiphy, |
| struct net_device *ndev, |
| struct cfg80211_connect_params *req) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(ndev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = wlan_hdd_cm_connect(wiphy, ndev, req); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_disconnect() - cfg80211 disconnect api |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @reason: Disconnect reason code |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int wlan_hdd_cfg80211_disconnect(struct wiphy *wiphy, |
| struct net_device *dev, u16 reason) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = wlan_hdd_cm_disconnect(wiphy, dev, reason); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_set_wiphy_params() - set wiphy parameters |
| * @wiphy: Pointer to wiphy |
| * @changed: Parameters changed |
| * |
| * This function is used to set the phy parameters. RTS Threshold/FRAG |
| * Threshold/Retry Count etc. |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int __wlan_hdd_cfg80211_set_wiphy_params(struct wiphy *wiphy, |
| u32 changed) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| int status; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_SET_WIPHY_PARAMS, |
| NO_SESSION, wiphy->rts_threshold); |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| |
| if (0 != status) |
| return status; |
| |
| if (changed & WIPHY_PARAM_RTS_THRESHOLD) { |
| u32 rts_threshold = (wiphy->rts_threshold == -1) ? |
| cfg_max(CFG_RTS_THRESHOLD) : |
| wiphy->rts_threshold; |
| |
| if ((cfg_min(CFG_RTS_THRESHOLD) > rts_threshold) || |
| (cfg_max(CFG_RTS_THRESHOLD) < rts_threshold)) { |
| hdd_err("Invalid RTS Threshold value: %u", |
| rts_threshold); |
| return -EINVAL; |
| } |
| |
| if (0 != ucfg_mlme_set_rts_threshold(hdd_ctx->psoc, |
| rts_threshold)) { |
| hdd_err("mlme_set_rts_threshold failed for val %u", |
| rts_threshold); |
| return -EIO; |
| } |
| |
| hdd_debug("set rts threshold %u", rts_threshold); |
| } |
| |
| if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { |
| u16 frag_threshold = (wiphy->frag_threshold == -1) ? |
| cfg_max(CFG_FRAG_THRESHOLD) : |
| wiphy->frag_threshold; |
| |
| if ((cfg_min(CFG_FRAG_THRESHOLD) > frag_threshold) || |
| (cfg_max(CFG_FRAG_THRESHOLD) < frag_threshold)) { |
| hdd_err("Invalid frag_threshold value %hu", |
| frag_threshold); |
| return -EINVAL; |
| } |
| |
| if (0 != ucfg_mlme_set_frag_threshold(hdd_ctx->psoc, |
| frag_threshold)) { |
| hdd_err("mlme_set_frag_threshold failed for val %hu", |
| frag_threshold); |
| return -EIO; |
| } |
| |
| hdd_debug("set frag threshold %hu", frag_threshold); |
| } |
| |
| hdd_exit(); |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_wiphy_params() - set wiphy parameters |
| * @wiphy: Pointer to wiphy |
| * @changed: Parameters changed |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int wlan_hdd_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_wiphy_params(wiphy, changed); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * __wlan_hdd_set_default_mgmt_key() - dummy implementation of set default mgmt |
| * key |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @key_index: Key index |
| * |
| * Return: 0 |
| */ |
| static int __wlan_hdd_set_default_mgmt_key(struct wiphy *wiphy, |
| struct net_device *netdev, |
| u8 key_index) |
| { |
| hdd_enter(); |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_set_default_mgmt_key() - SSR wrapper for |
| * wlan_hdd_set_default_mgmt_key |
| * @wiphy: pointer to wiphy |
| * @wdev: pointer to wireless_device structure |
| * @key_index: key index |
| * |
| * Return: 0 on success, error number on failure |
| */ |
| #ifdef CFG80211_KEY_INSTALL_SUPPORT_ON_WDEV |
| static int wlan_hdd_set_default_mgmt_key(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| u8 key_index) |
| { |
| int errno = -EINVAL; |
| struct osif_vdev_sync *vdev_sync; |
| struct hdd_adapter *adapter = qdf_container_of(wdev, |
| struct hdd_adapter, |
| wdev); |
| |
| if (!adapter || wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return errno; |
| |
| errno = osif_vdev_sync_op_start(adapter->dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_set_default_mgmt_key(wiphy, adapter->dev, key_index); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #else |
| static int wlan_hdd_set_default_mgmt_key(struct wiphy *wiphy, |
| struct net_device *netdev, |
| u8 key_index) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_set_default_mgmt_key(wiphy, netdev, key_index); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| |
| /** |
| * Default val of cwmin, this value is used to overide the |
| * incorrect user set value |
| */ |
| #define DEFAULT_CWMIN 15 |
| |
| /** |
| * Default val of cwmax, this value is used to overide the |
| * incorrect user set value |
| */ |
| #define DEFAULT_CWMAX 1023 |
| |
| /** |
| * __wlan_hdd_set_txq_params() - implementation of set tx queue params |
| * to configure internal EDCA parameters |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @params: Pointer to tx queue parameters |
| * |
| * Return: 0 |
| */ |
| static int __wlan_hdd_set_txq_params(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct ieee80211_txq_params *params) |
| { |
| QDF_STATUS status; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| mac_handle_t mac_handle; |
| tSirMacEdcaParamRecord txq_edca_params; |
| static const uint8_t ieee_ac_to_qca_ac[] = { |
| [IEEE80211_AC_VO] = QCA_WLAN_AC_VO, |
| [IEEE80211_AC_VI] = QCA_WLAN_AC_VI, |
| [IEEE80211_AC_BE] = QCA_WLAN_AC_BE, |
| [IEEE80211_AC_BK] = QCA_WLAN_AC_BK, |
| }; |
| |
| hdd_enter(); |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return -EINVAL; |
| |
| mac_handle = hdd_ctx->mac_handle; |
| if (params->cwmin == 0 || params->cwmin > DEFAULT_CWMAX) |
| params->cwmin = DEFAULT_CWMIN; |
| |
| if (params->cwmax < params->cwmin || params->cwmax > DEFAULT_CWMAX) |
| params->cwmax = DEFAULT_CWMAX; |
| |
| txq_edca_params.cw.min = convert_cw(params->cwmin); |
| txq_edca_params.cw.max = convert_cw(params->cwmax); |
| txq_edca_params.aci.aifsn = params->aifs; |
| /* The txop is multiple of 32us units */ |
| txq_edca_params.txoplimit = params->txop; |
| txq_edca_params.aci.aci = |
| ieee_ac_to_qca_ac[params->ac]; |
| |
| status = sme_update_session_txq_edca_params(mac_handle, |
| adapter->vdev_id, |
| &txq_edca_params); |
| |
| hdd_exit(); |
| return qdf_status_to_os_return(status); |
| } |
| |
| /** |
| * wlan_hdd_set_txq_params() - SSR wrapper for wlan_hdd_set_txq_params |
| * @wiphy: pointer to wiphy |
| * @netdev: pointer to net_device structure |
| * @params: pointer to ieee80211_txq_params |
| * |
| * Return: 0 on success, error number on failure |
| */ |
| static int wlan_hdd_set_txq_params(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct ieee80211_txq_params *params) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_set_txq_params(wiphy, dev, params); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * hdd_softap_deauth_current_sta() - Deauth current sta |
| * @sta_info: pointer to the current station info structure |
| * @adapter: pointer to adapter structure |
| * @hdd_ctx: pointer to hdd context |
| * @hapd_state: pointer to hostapd state structure |
| * @param: pointer to del sta params |
| * |
| * Return: QDF_STATUS on success, corresponding QDF failure status on failure |
| */ |
| static |
| QDF_STATUS hdd_softap_deauth_current_sta(struct hdd_adapter *adapter, |
| struct hdd_station_info *sta_info, |
| struct hdd_hostapd_state *hapd_state, |
| struct csr_del_sta_params *param) |
| { |
| qdf_event_t *disassoc_event = &hapd_state->qdf_sta_disassoc_event; |
| struct hdd_context *hdd_ctx; |
| QDF_STATUS qdf_status; |
| struct hdd_station_info *tmp = NULL; |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (!hdd_ctx) { |
| hdd_err("hdd_ctx is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| qdf_event_reset(&hapd_state->qdf_sta_disassoc_event); |
| |
| if (!qdf_is_macaddr_broadcast(¶m->peerMacAddr)) |
| sme_send_disassoc_req_frame(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| (uint8_t *)¶m->peerMacAddr, |
| param->reason_code, 0); |
| |
| qdf_status = hdd_softap_sta_deauth(adapter, param); |
| |
| if (QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| if(qdf_is_macaddr_broadcast(&sta_info->sta_mac)) { |
| hdd_for_each_sta_ref_safe( |
| adapter->sta_info_list, |
| sta_info, tmp, |
| STA_INFO_SOFTAP_DEAUTH_CURRENT_STA) { |
| sta_info->is_deauth_in_progress = true; |
| hdd_put_sta_info_ref( |
| &adapter->sta_info_list, |
| &sta_info, true, |
| STA_INFO_SOFTAP_DEAUTH_CURRENT_STA); |
| } |
| } else { |
| sta_info->is_deauth_in_progress = true; |
| } |
| qdf_status = qdf_wait_for_event_completion( |
| disassoc_event, |
| SME_PEER_DISCONNECT_TIMEOUT); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) |
| hdd_warn("Deauth time expired"); |
| } else { |
| sta_info->is_deauth_in_progress = false; |
| hdd_debug("STA removal failed for ::" QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(sta_info->sta_mac.bytes)); |
| return QDF_STATUS_E_NOENT; |
| } |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS hdd_softap_deauth_all_sta(struct hdd_adapter *adapter, |
| struct hdd_hostapd_state *hapd_state, |
| struct csr_del_sta_params *param) |
| { |
| QDF_STATUS status; |
| bool is_sap_bcast_deauth_enabled = false; |
| struct hdd_context *hdd_ctx; |
| struct hdd_station_info *sta_info, *tmp = NULL; |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (!hdd_ctx) { |
| hdd_err("hdd_ctx is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| ucfg_mlme_get_sap_bcast_deauth_enabled(hdd_ctx->psoc, |
| &is_sap_bcast_deauth_enabled); |
| |
| hdd_debug("sap_bcast_deauth_enabled %d", is_sap_bcast_deauth_enabled); |
| |
| if (is_sap_bcast_deauth_enabled) { |
| struct hdd_station_info bcast_sta_info; |
| |
| qdf_set_macaddr_broadcast(&bcast_sta_info.sta_mac); |
| return hdd_softap_deauth_current_sta(adapter, &bcast_sta_info, |
| hapd_state, param); |
| } |
| |
| hdd_for_each_sta_ref_safe(adapter->sta_info_list, sta_info, tmp, |
| STA_INFO_SOFTAP_DEAUTH_ALL_STA) { |
| if (!sta_info->is_deauth_in_progress) { |
| hdd_debug("Delete STA with MAC:" QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(sta_info->sta_mac.bytes)); |
| qdf_mem_copy(param->peerMacAddr.bytes, |
| sta_info->sta_mac.bytes, |
| QDF_MAC_ADDR_SIZE); |
| status = |
| hdd_softap_deauth_current_sta(adapter, sta_info, |
| hapd_state, param); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_put_sta_info_ref( |
| &adapter->sta_info_list, |
| &sta_info, true, |
| STA_INFO_SOFTAP_DEAUTH_ALL_STA); |
| if (tmp) |
| hdd_put_sta_info_ref( |
| &adapter->sta_info_list, |
| &tmp, true, |
| STA_INFO_SOFTAP_DEAUTH_ALL_STA); |
| return status; |
| } |
| } |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, |
| STA_INFO_SOFTAP_DEAUTH_ALL_STA); |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_del_station() - delete station v2 |
| * @wiphy: Pointer to wiphy |
| * @dev: Underlying net device |
| * @param: Pointer to delete station parameter |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static |
| int __wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct csr_del_sta_params *param) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx; |
| struct hdd_hostapd_state *hapd_state; |
| uint8_t *mac; |
| struct hdd_station_info *sta_info; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_DEL_STA, |
| adapter->vdev_id, adapter->device_mode); |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (!hdd_ctx) { |
| hdd_err("hdd_ctx is NULL"); |
| return -EINVAL; |
| } |
| |
| mac = (uint8_t *) param->peerMacAddr.bytes; |
| |
| if (QDF_SAP_MODE != adapter->device_mode && |
| QDF_P2P_GO_MODE != adapter->device_mode) |
| goto fn_end; |
| |
| hapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); |
| if (!hapd_state) { |
| hdd_err("Hostapd State is Null"); |
| return 0; |
| } |
| |
| if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)mac)) { |
| if (!QDF_IS_STATUS_SUCCESS(hdd_softap_deauth_all_sta(adapter, |
| hapd_state, |
| param))) |
| goto fn_end; |
| } else { |
| if (param->reason_code == REASON_1X_AUTH_FAILURE) |
| hdd_softap_check_wait_for_tx_eap_pkt( |
| adapter, (struct qdf_mac_addr *)mac); |
| |
| sta_info = hdd_get_sta_info_by_mac( |
| &adapter->sta_info_list, |
| mac, |
| STA_INFO_CFG80211_DEL_STATION); |
| |
| if (!sta_info) { |
| hdd_debug("Skip DEL STA as this is not used::" |
| QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(mac)); |
| return -ENOENT; |
| } |
| |
| if (sta_info->is_deauth_in_progress) { |
| hdd_debug("Skip DEL STA as deauth is in progress::" |
| QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(mac)); |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, |
| true, |
| STA_INFO_CFG80211_DEL_STATION); |
| return -ENOENT; |
| } |
| |
| hdd_debug("ucast, Delete STA with MAC:" QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(mac)); |
| hdd_softap_deauth_current_sta(adapter, sta_info, hapd_state, |
| param); |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, |
| STA_INFO_CFG80211_DEL_STATION); |
| } |
| |
| fn_end: |
| hdd_exit(); |
| return 0; |
| } |
| |
| #if defined(USE_CFG80211_DEL_STA_V2) |
| int wlan_hdd_del_station(struct hdd_adapter *adapter, const uint8_t *mac) |
| { |
| struct station_del_parameters del_sta; |
| |
| del_sta.mac = mac; |
| del_sta.subtype = IEEE80211_STYPE_DEAUTH >> 4; |
| del_sta.reason_code = WLAN_REASON_DEAUTH_LEAVING; |
| |
| return wlan_hdd_cfg80211_del_station(adapter->wdev.wiphy, |
| adapter->dev, &del_sta); |
| } |
| #else |
| int wlan_hdd_del_station(struct hdd_adapter *adapter, const uint8_t *mac) |
| { |
| return wlan_hdd_cfg80211_del_station(adapter->wdev.wiphy, |
| adapter->dev, mac); |
| } |
| #endif |
| |
| /** |
| * wlan_hdd_cfg80211_del_station() - delete station entry handler |
| * @wiphy: Pointer to wiphy |
| * @dev: net_device to operate against |
| * @mac: binary mac address |
| * @reason_code: reason for the deauthorization/disassociation |
| * @subtype: management frame subtype to indicate removal |
| * |
| * Return: Errno |
| */ |
| static int _wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, |
| struct net_device *dev, |
| const uint8_t *mac, |
| uint16_t reason_code, |
| uint8_t subtype) |
| { |
| int errno; |
| struct csr_del_sta_params delStaParams; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| wlansap_populate_del_sta_params(mac, reason_code, subtype, |
| &delStaParams); |
| errno = __wlan_hdd_cfg80211_del_station(wiphy, dev, &delStaParams); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #ifdef USE_CFG80211_DEL_STA_V2 |
| int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct station_del_parameters *param) |
| { |
| if (!param) |
| return -EINVAL; |
| |
| return _wlan_hdd_cfg80211_del_station(wiphy, dev, param->mac, |
| param->reason_code, |
| param->subtype); |
| } |
| #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) |
| int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, |
| const uint8_t *mac) |
| { |
| uint16_t reason = REASON_DEAUTH_NETWORK_LEAVING; |
| uint8_t subtype = SIR_MAC_MGMT_DEAUTH >> 4; |
| |
| return _wlan_hdd_cfg80211_del_station(wiphy, dev, mac, reason, subtype); |
| } |
| #else |
| int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, |
| uint8_t *mac) |
| { |
| uint16_t reason = REASON_DEAUTH_NETWORK_LEAVING; |
| uint8_t subtype = SIR_MAC_MGMT_DEAUTH >> 4; |
| |
| return _wlan_hdd_cfg80211_del_station(wiphy, dev, mac, reason, subtype); |
| } |
| #endif |
| |
| /** |
| * __wlan_hdd_cfg80211_add_station() - add station |
| * @wiphy: Pointer to wiphy |
| * @mac: Pointer to station mac address |
| * @pmksa: Pointer to add station parameter |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int __wlan_hdd_cfg80211_add_station(struct wiphy *wiphy, |
| struct net_device *dev, |
| const uint8_t *mac, |
| struct station_parameters *params) |
| { |
| int status = -EPERM; |
| #ifdef FEATURE_WLAN_TDLS |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| u32 mask, set; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_ADD_STA, |
| adapter->vdev_id, params->listen_interval); |
| |
| if (0 != wlan_hdd_validate_context(hdd_ctx)) |
| return -EINVAL; |
| |
| mask = params->sta_flags_mask; |
| |
| set = params->sta_flags_set; |
| |
| hdd_debug("mask 0x%x set 0x%x " QDF_MAC_ADDR_FMT, mask, set, |
| QDF_MAC_ADDR_REF(mac)); |
| |
| if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) { |
| if (set & BIT(NL80211_STA_FLAG_TDLS_PEER)) { |
| struct wlan_objmgr_vdev *vdev; |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, |
| WLAN_OSIF_TDLS_ID); |
| if (vdev) { |
| status = wlan_cfg80211_tdls_add_peer(vdev, |
| mac); |
| hdd_objmgr_put_vdev_by_user(vdev, |
| WLAN_OSIF_TDLS_ID); |
| } |
| } |
| } |
| #endif |
| hdd_exit(); |
| return status; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_add_station() - add station |
| * @wiphy: Pointer to wiphy |
| * @mac: Pointer to station mac address |
| * @pmksa: Pointer to add station parameter |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) |
| static int wlan_hdd_cfg80211_add_station(struct wiphy *wiphy, |
| struct net_device *dev, |
| const uint8_t *mac, |
| struct station_parameters *params) |
| #else |
| static int wlan_hdd_cfg80211_add_station(struct wiphy *wiphy, |
| struct net_device *dev, uint8_t *mac, |
| struct station_parameters *params) |
| #endif |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_add_station(wiphy, dev, mac, params); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #if (defined(CFG80211_CONFIG_PMKSA_TIMER_PARAMS_SUPPORT) || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0))) |
| static inline void |
| hdd_fill_pmksa_lifetime(struct cfg80211_pmksa *pmksa, |
| struct wlan_crypto_pmksa *pmk_cache) |
| { |
| pmk_cache->pmk_lifetime = pmksa->pmk_lifetime; |
| if (pmk_cache->pmk_lifetime > WLAN_CRYPTO_MAX_PMKID_LIFETIME) |
| pmk_cache->pmk_lifetime = WLAN_CRYPTO_MAX_PMKID_LIFETIME; |
| |
| pmk_cache->pmk_lifetime_threshold = pmksa->pmk_reauth_threshold; |
| if (pmk_cache->pmk_lifetime_threshold >= |
| WLAN_CRYPTO_MAX_PMKID_LIFETIME_THRESHOLD) |
| pmk_cache->pmk_lifetime_threshold = |
| WLAN_CRYPTO_MAX_PMKID_LIFETIME_THRESHOLD - 1; |
| |
| hdd_debug("PMKSA: lifetime:%d threshold:%d", pmk_cache->pmk_lifetime, |
| pmk_cache->pmk_lifetime_threshold); |
| } |
| #else |
| static inline void |
| hdd_fill_pmksa_lifetime(struct cfg80211_pmksa *pmksa, |
| struct wlan_crypto_pmksa *src_pmk_cache) |
| {} |
| #endif |
| |
| static QDF_STATUS wlan_hdd_set_pmksa_cache(struct hdd_adapter *adapter, |
| struct wlan_crypto_pmksa *pmk_cache) |
| { |
| QDF_STATUS result; |
| struct wlan_crypto_pmksa *pmksa; |
| struct wlan_objmgr_vdev *vdev; |
| mac_handle_t mac_handle; |
| struct hdd_context *hdd_ctx; |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (!hdd_ctx) { |
| hdd_err("HDD context is null"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return QDF_STATUS_E_INVAL; |
| mac_handle = hdd_ctx->mac_handle; |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_ID); |
| if (!vdev) |
| return QDF_STATUS_E_FAILURE; |
| |
| pmksa = qdf_mem_malloc(sizeof(*pmksa)); |
| if (!pmksa) { |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| return QDF_STATUS_E_NOMEM; |
| } |
| |
| if (!pmk_cache->ssid_len) { |
| qdf_copy_macaddr(&pmksa->bssid, &pmk_cache->bssid); |
| } else { |
| qdf_mem_copy(pmksa->ssid, pmk_cache->ssid, pmk_cache->ssid_len); |
| qdf_mem_copy(pmksa->cache_id, pmk_cache->cache_id, |
| WLAN_CACHE_ID_LEN); |
| pmksa->ssid_len = pmk_cache->ssid_len; |
| } |
| qdf_mem_copy(pmksa->pmkid, pmk_cache->pmkid, PMKID_LEN); |
| qdf_mem_copy(pmksa->pmk, pmk_cache->pmk, pmk_cache->pmk_len); |
| pmksa->pmk_len = pmk_cache->pmk_len; |
| pmksa->pmk_entry_ts = qdf_get_system_timestamp(); |
| pmksa->pmk_lifetime = pmk_cache->pmk_lifetime; |
| pmksa->pmk_lifetime_threshold = pmk_cache->pmk_lifetime_threshold; |
| |
| result = wlan_crypto_set_del_pmksa(vdev, pmksa, true); |
| if (result != QDF_STATUS_SUCCESS) { |
| qdf_mem_zero(pmksa, sizeof(*pmksa)); |
| qdf_mem_free(pmksa); |
| } |
| |
| if (result == QDF_STATUS_SUCCESS && pmk_cache->pmk_len) { |
| sme_roam_set_psk_pmk(mac_handle, pmksa, adapter->vdev_id, |
| false); |
| sme_set_pmk_cache_ft(mac_handle, adapter->vdev_id, pmk_cache); |
| } |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| |
| return result; |
| } |
| |
| static QDF_STATUS wlan_hdd_del_pmksa_cache(struct hdd_adapter *adapter, |
| struct wlan_crypto_pmksa *pmk_cache) |
| { |
| QDF_STATUS result; |
| struct wlan_crypto_pmksa pmksa; |
| struct wlan_objmgr_vdev *vdev; |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_ID); |
| if (!vdev) |
| return QDF_STATUS_E_FAILURE; |
| |
| qdf_mem_zero(&pmksa, sizeof(pmksa)); |
| if (!pmk_cache->ssid_len) { |
| qdf_copy_macaddr(&pmksa.bssid, &pmk_cache->bssid); |
| } else { |
| qdf_mem_copy(pmksa.ssid, pmk_cache->ssid, pmk_cache->ssid_len); |
| qdf_mem_copy(pmksa.cache_id, pmk_cache->cache_id, |
| WLAN_CACHE_ID_LEN); |
| pmksa.ssid_len = pmk_cache->ssid_len; |
| } |
| |
| result = wlan_crypto_set_del_pmksa(vdev, &pmksa, false); |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| |
| return result; |
| } |
| |
| QDF_STATUS wlan_hdd_flush_pmksa_cache(struct hdd_adapter *adapter) |
| { |
| QDF_STATUS result; |
| struct wlan_objmgr_vdev *vdev; |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_ID); |
| if (!vdev) |
| return QDF_STATUS_E_FAILURE; |
| |
| result = wlan_crypto_set_del_pmksa(vdev, NULL, false); |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| |
| return result; |
| } |
| |
| #if defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) |
| /* |
| * wlan_hdd_is_pmksa_valid: API to validate pmksa |
| * @pmksa: pointer to cfg80211_pmksa structure |
| * |
| * Return: True if valid else false |
| */ |
| static inline bool wlan_hdd_is_pmksa_valid(struct cfg80211_pmksa *pmksa) |
| { |
| if (!pmksa->bssid) { |
| hdd_warn("bssid is NULL"); |
| if (!pmksa->ssid || !pmksa->cache_id) { |
| hdd_err("either ssid or cache_id are NULL"); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /* |
| * hdd_fill_pmksa_info: API to update tPmkidCacheInfo from cfg80211_pmksa |
| * @adapter: Pointer to hdd adapter |
| * @pmk_cache: pmk that needs to be udated |
| * @pmksa: pmk from supplicant |
| * @is_delete: Bool to decide set or delete PMK |
| * Return: None |
| */ |
| static void hdd_fill_pmksa_info(struct hdd_adapter *adapter, |
| struct wlan_crypto_pmksa *pmk_cache, |
| struct cfg80211_pmksa *pmksa, bool is_delete) |
| { |
| if (pmksa->bssid) { |
| hdd_debug("%s PMKSA for " QDF_MAC_ADDR_FMT, |
| is_delete ? "Delete" : "Set", |
| QDF_MAC_ADDR_REF(pmksa->bssid)); |
| qdf_mem_copy(pmk_cache->bssid.bytes, |
| pmksa->bssid, QDF_MAC_ADDR_SIZE); |
| } else { |
| qdf_mem_copy(pmk_cache->ssid, pmksa->ssid, pmksa->ssid_len); |
| qdf_mem_copy(pmk_cache->cache_id, pmksa->cache_id, |
| CACHE_ID_LEN); |
| pmk_cache->ssid_len = pmksa->ssid_len; |
| hdd_debug("%s PMKSA for ssid %*.*s cache_id %x %x", |
| is_delete ? "Delete" : "Set", |
| pmk_cache->ssid_len, pmk_cache->ssid_len, |
| pmk_cache->ssid, pmk_cache->cache_id[0], |
| pmk_cache->cache_id[1]); |
| } |
| |
| hdd_fill_pmksa_lifetime(pmksa, pmk_cache); |
| |
| if (is_delete) |
| return; |
| |
| qdf_mem_copy(pmk_cache->pmkid, pmksa->pmkid, PMKID_LEN); |
| if (pmksa->pmk_len && (pmksa->pmk_len <= CSR_RSN_MAX_PMK_LEN)) { |
| qdf_mem_copy(pmk_cache->pmk, pmksa->pmk, pmksa->pmk_len); |
| pmk_cache->pmk_len = pmksa->pmk_len; |
| } else |
| hdd_debug("pmk len is %zu", pmksa->pmk_len); |
| } |
| #else |
| /* |
| * wlan_hdd_is_pmksa_valid: API to validate pmksa |
| * @pmksa: pointer to cfg80211_pmksa structure |
| * |
| * Return: True if valid else false |
| */ |
| static inline bool wlan_hdd_is_pmksa_valid(struct cfg80211_pmksa *pmksa) |
| { |
| if (!pmksa->bssid) { |
| hdd_err("both bssid is NULL %pK", pmksa->bssid); |
| return false; |
| } |
| return true; |
| } |
| |
| /* |
| * hdd_fill_pmksa_info: API to update struct wlan_crypto_pmksa from |
| * cfg80211_pmksa |
| * @adapter: Pointer to hdd adapter |
| * @pmk_cache: pmk which needs to be updated |
| * @pmksa: pmk from supplicant |
| * @is_delete: Bool to decide whether to set or delete PMK |
| * |
| * Return: None |
| */ |
| static void hdd_fill_pmksa_info(struct hdd_adapter *adapter, |
| struct wlan_crypto_pmksa *pmk_cache, |
| struct cfg80211_pmksa *pmksa, bool is_delete) |
| { |
| mac_handle_t mac_handle; |
| |
| hdd_debug("%s PMKSA for " QDF_MAC_ADDR_FMT, is_delete ? "Delete" : "Set", |
| QDF_MAC_ADDR_REF(pmksa->bssid)); |
| qdf_mem_copy(pmk_cache->bssid.bytes, pmksa->bssid, QDF_MAC_ADDR_SIZE); |
| |
| if (is_delete) |
| return; |
| mac_handle = hdd_adapter_get_mac_handle(adapter); |
| sme_get_pmk_info(mac_handle, adapter->vdev_id, pmk_cache); |
| qdf_mem_copy(pmk_cache->pmkid, pmksa->pmkid, PMKID_LEN); |
| } |
| #endif |
| |
| /** |
| * __wlan_hdd_cfg80211_set_pmksa() - set pmksa |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @pmksa: Pointer to set pmksa parameter |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int __wlan_hdd_cfg80211_set_pmksa(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_pmksa *pmksa) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| QDF_STATUS result = QDF_STATUS_SUCCESS; |
| int status; |
| struct wlan_crypto_pmksa *pmk_cache; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| if (!pmksa) { |
| hdd_err("pmksa is NULL"); |
| return -EINVAL; |
| } |
| |
| if (!pmksa->pmkid) { |
| hdd_err("pmksa->pmkid(%pK) is NULL", |
| pmksa->pmkid); |
| return -EINVAL; |
| } |
| |
| if (!wlan_hdd_is_pmksa_valid(pmksa)) |
| return -EINVAL; |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| |
| if (0 != status) |
| return status; |
| |
| pmk_cache = qdf_mem_malloc(sizeof(*pmk_cache)); |
| if (!pmk_cache) |
| return -ENOMEM; |
| |
| hdd_fill_pmksa_info(adapter, pmk_cache, pmksa, false); |
| |
| /* |
| * Add to the PMKSA Cache in CSR |
| * PMKSA cache will be having following |
| * 1. pmkid id |
| * 2. pmk |
| * 3. bssid or cache identifier |
| */ |
| result = wlan_hdd_set_pmksa_cache(adapter, pmk_cache); |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_SET_PMKSA, |
| adapter->vdev_id, result); |
| |
| if (QDF_IS_STATUS_SUCCESS(result)) |
| sme_set_del_pmkid_cache(hdd_ctx->psoc, adapter->vdev_id, |
| pmk_cache, true); |
| |
| qdf_mem_zero(pmk_cache, sizeof(*pmk_cache)); |
| |
| qdf_mem_free(pmk_cache); |
| hdd_exit(); |
| |
| return QDF_IS_STATUS_SUCCESS(result) ? 0 : -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_pmksa() - set pmksa |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @pmksa: Pointer to set pmksa parameter |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int wlan_hdd_cfg80211_set_pmksa(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_pmksa *pmksa) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_pmksa(wiphy, dev, pmksa); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_del_pmksa() - delete pmksa |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @pmksa: Pointer to pmksa parameter |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int __wlan_hdd_cfg80211_del_pmksa(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_pmksa *pmksa) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; |
| int status = 0; |
| struct wlan_crypto_pmksa *pmk_cache; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| if (!pmksa) { |
| hdd_err("pmksa is NULL"); |
| return -EINVAL; |
| } |
| |
| if (!wlan_hdd_is_pmksa_valid(pmksa)) |
| return -EINVAL; |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| |
| if (0 != status) |
| return status; |
| |
| pmk_cache = qdf_mem_malloc(sizeof(*pmk_cache)); |
| if (!pmk_cache) |
| return -ENOMEM; |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_DEL_PMKSA, |
| adapter->vdev_id, 0); |
| |
| hdd_fill_pmksa_info(adapter, pmk_cache, pmksa, true); |
| |
| qdf_status = wlan_hdd_del_pmksa_cache(adapter, pmk_cache); |
| if (QDF_IS_STATUS_ERROR(qdf_status)) { |
| if (!pmksa->bssid) |
| hdd_err("Failed to delete PMKSA for null bssid"); |
| else |
| hdd_err("Failed to delete PMKSA for " QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(pmksa->bssid)); |
| status = -EINVAL; |
| } else { |
| /* clear single_pmk_info information */ |
| sme_clear_sae_single_pmk_info(hdd_ctx->psoc, adapter->vdev_id, |
| pmk_cache); |
| |
| /* Send the delete pmkid command to firmware */ |
| sme_set_del_pmkid_cache(hdd_ctx->psoc, adapter->vdev_id, |
| pmk_cache, false); |
| } |
| |
| qdf_mem_zero(pmk_cache, sizeof(*pmk_cache)); |
| qdf_mem_free(pmk_cache); |
| |
| hdd_exit(); |
| |
| return status; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_del_pmksa() - delete pmksa |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @pmksa: Pointer to pmksa parameter |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int wlan_hdd_cfg80211_del_pmksa(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_pmksa *pmksa) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_del_pmksa(wiphy, dev, pmksa); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_flush_pmksa() - flush pmksa |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int __wlan_hdd_cfg80211_flush_pmksa(struct wiphy *wiphy, |
| struct net_device *dev) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| int errno; |
| QDF_STATUS status; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| hdd_debug("Flushing PMKSA"); |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| errno = wlan_hdd_validate_context(hdd_ctx); |
| if (errno) |
| return errno; |
| |
| status = wlan_hdd_flush_pmksa_cache(adapter); |
| if (status == QDF_STATUS_E_NOSUPPORT) { |
| errno = -EOPNOTSUPP; |
| } else if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("Cannot flush PMKIDCache"); |
| errno = -EINVAL; |
| } |
| |
| sme_set_del_pmkid_cache(hdd_ctx->psoc, adapter->vdev_id, NULL, false); |
| hdd_exit(); |
| return errno; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_flush_pmksa() - flush pmksa |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int wlan_hdd_cfg80211_flush_pmksa(struct wiphy *wiphy, |
| struct net_device *dev) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_flush_pmksa(wiphy, dev); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #if defined(KERNEL_SUPPORT_11R_CFG80211) |
| /** |
| * __wlan_hdd_cfg80211_update_ft_ies() - update fast transition ies |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @ftie: Pointer to fast transition ie parameter |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_update_ft_ies(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_update_ft_ies_params *ftie) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| int status; |
| |
| hdd_enter(); |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (status) |
| return status; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_UPDATE_FT_IES, adapter->vdev_id, 0); |
| |
| /* Added for debug on reception of Re-assoc Req. */ |
| if (!hdd_cm_is_vdev_associated(adapter)) { |
| hdd_err("Called with Ie of length = %zu when not associated", |
| ftie->ie_len); |
| hdd_err("Should be Re-assoc Req IEs"); |
| } |
| hdd_debug("called with Ie of length = %zu", ftie->ie_len); |
| |
| ucfg_cm_set_ft_ies(hdd_ctx->pdev, adapter->vdev_id, |
| (const u8 *)ftie->ie, ftie->ie_len); |
| hdd_exit(); |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_update_ft_ies() - update fast transition ies |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @ftie: Pointer to fast transition ie parameter |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int |
| wlan_hdd_cfg80211_update_ft_ies(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_update_ft_ies_params *ftie) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_update_ft_ies(wiphy, dev, ftie); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| |
| #if defined(CFG80211_EXTERNAL_DH_UPDATE_SUPPORT) || \ |
| (LINUX_VERSION_CODE > KERNEL_VERSION(5, 2, 0)) |
| /** |
| * __wlan_hdd_cfg80211_update_owe_info() - update OWE info |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @owe_info: Pointer to OWE info |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_update_owe_info(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_update_owe_info *owe_info) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| QDF_STATUS status; |
| int errno; |
| |
| hdd_enter_dev(dev); |
| |
| errno = wlan_hdd_validate_context(hdd_ctx); |
| if (errno) |
| return errno; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| hdd_debug("owe_status %d", owe_info->status); |
| |
| status = wlansap_update_owe_info(WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| owe_info->peer, owe_info->ie, |
| owe_info->ie_len, owe_info->status); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("Failed to update OWE info"); |
| errno = qdf_status_to_os_return(status); |
| } |
| |
| hdd_exit(); |
| return errno; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_update_owe_info() - update OWE info |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @owe_info: Pointer to OWE info |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int |
| wlan_hdd_cfg80211_update_owe_info(struct wiphy *wiphy, |
| struct net_device *net_dev, |
| struct cfg80211_update_owe_info *owe_info) |
| { |
| struct osif_vdev_sync *vdev_sync; |
| int errno; |
| |
| errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_update_owe_info(wiphy, net_dev, owe_info); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| |
| void wlan_hdd_cfg80211_update_replay_counter_cb( |
| void *cb_ctx, struct pmo_gtk_rsp_params *gtk_rsp_param) |
| |
| { |
| struct hdd_adapter *adapter = (struct hdd_adapter *)cb_ctx; |
| uint8_t temp_replay_counter[8]; |
| int i; |
| uint8_t *p; |
| |
| hdd_enter(); |
| |
| if (!adapter) { |
| hdd_err("HDD adapter is Null"); |
| goto out; |
| } |
| |
| if (!gtk_rsp_param) { |
| hdd_err("gtk_rsp_param is Null"); |
| goto out; |
| } |
| |
| if (gtk_rsp_param->status_flag != QDF_STATUS_SUCCESS) { |
| hdd_err("wlan Failed to get replay counter value"); |
| goto out; |
| } |
| |
| hdd_debug("updated replay counter: %llu from fwr", |
| gtk_rsp_param->replay_counter); |
| /* convert little to big endian since supplicant works on big endian */ |
| p = (uint8_t *)>k_rsp_param->replay_counter; |
| for (i = 0; i < 8; i++) |
| temp_replay_counter[7 - i] = (uint8_t) p[i]; |
| |
| hdd_debug("gtk_rsp_param bssid "QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(gtk_rsp_param->bssid.bytes)); |
| /* Update replay counter to NL */ |
| cfg80211_gtk_rekey_notify(adapter->dev, |
| gtk_rsp_param->bssid.bytes, |
| temp_replay_counter, GFP_KERNEL); |
| out: |
| hdd_exit(); |
| |
| } |
| |
| #ifdef WLAN_FEATURE_GTK_OFFLOAD |
| /** |
| * wlan_hdd_copy_gtk_kek - Copy the KEK from GTK rekey data to GTK request |
| * @gtk_req: Pointer to GTK request |
| * @data: Pointer to rekey data |
| * |
| * Return: none |
| */ |
| #ifdef CFG80211_REKEY_DATA_KEK_LEN |
| static |
| void wlan_hdd_copy_gtk_kek(struct pmo_gtk_req *gtk_req, |
| struct cfg80211_gtk_rekey_data *data) |
| { |
| qdf_mem_copy(gtk_req->kek, data->kek, data->kek_len); |
| gtk_req->kek_len = data->kek_len; |
| } |
| #else |
| static |
| void wlan_hdd_copy_gtk_kek(struct pmo_gtk_req *gtk_req, |
| struct cfg80211_gtk_rekey_data *data) |
| { |
| qdf_mem_copy(gtk_req->kek, data->kek, NL80211_KEK_LEN); |
| gtk_req->kek_len = NL80211_KEK_LEN; |
| } |
| #endif |
| |
| /** |
| * __wlan_hdd_cfg80211_set_rekey_data() - set rekey data |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @data: Pointer to rekey data |
| * |
| * This function is used to offload GTK rekeying job to the firmware. |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static |
| int __wlan_hdd_cfg80211_set_rekey_data(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_gtk_rekey_data *data) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| int result, i; |
| struct pmo_gtk_req *gtk_req = NULL; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint8_t *buf; |
| struct wlan_objmgr_vdev *vdev; |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| result = -EINVAL; |
| goto out; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) { |
| result = -EINVAL; |
| goto out; |
| } |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_SET_REKEY_DATA, |
| adapter->vdev_id, adapter->device_mode); |
| |
| result = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != result) |
| goto out; |
| |
| gtk_req = qdf_mem_malloc(sizeof(*gtk_req)); |
| if (!gtk_req) { |
| result = -ENOMEM; |
| goto out; |
| } |
| |
| /* convert big to little endian since driver work on little endian */ |
| buf = (uint8_t *)>k_req->replay_counter; |
| for (i = 0; i < 8; i++) |
| buf[7 - i] = data->replay_ctr[i]; |
| |
| hdd_debug("current replay counter: %llu in user space", |
| gtk_req->replay_counter); |
| |
| wlan_hdd_copy_gtk_kek(gtk_req, data); |
| if (data->kck) { |
| qdf_mem_copy(gtk_req->kck, data->kck, NL80211_KCK_LEN); |
| gtk_req->kck_len = NL80211_KCK_LEN; |
| } |
| gtk_req->is_fils_connection = hdd_is_fils_connection(hdd_ctx, adapter); |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_POWER_ID); |
| if (!vdev) { |
| result = -EINVAL; |
| goto out; |
| } |
| status = ucfg_pmo_cache_gtk_offload_req(vdev, gtk_req); |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_POWER_ID); |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_err("Failed to cache GTK Offload"); |
| result = qdf_status_to_os_return(status); |
| } |
| out: |
| if (gtk_req) |
| qdf_mem_free(gtk_req); |
| hdd_exit(); |
| |
| return result; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_rekey_data() - set rekey data |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @data: Pointer to rekey data |
| * |
| * This function is used to offload GTK rekeying job to the firmware. |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static |
| int wlan_hdd_cfg80211_set_rekey_data(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_gtk_rekey_data *data) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_rekey_data(wiphy, dev, data); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif /* WLAN_FEATURE_GTK_OFFLOAD */ |
| |
| /** |
| * __wlan_hdd_cfg80211_set_mac_acl() - set access control policy |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @param: Pointer to access control parameter |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int __wlan_hdd_cfg80211_set_mac_acl(struct wiphy *wiphy, |
| struct net_device *dev, |
| const struct cfg80211_acl_data *params) |
| { |
| int i; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_hostapd_state *hostapd_state; |
| struct sap_config *config; |
| struct hdd_context *hdd_ctx; |
| int status; |
| QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; |
| |
| hdd_enter(); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (!params) { |
| hdd_err("params is Null"); |
| return -EINVAL; |
| } |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| status = wlan_hdd_validate_context(hdd_ctx); |
| |
| if (0 != status) |
| return status; |
| |
| hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); |
| |
| if (!hostapd_state) { |
| hdd_err("hostapd_state is Null"); |
| return -EINVAL; |
| } |
| |
| hdd_debug("acl policy: %d num acl entries: %d", params->acl_policy, |
| params->n_acl_entries); |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_SET_MAC_ACL, |
| adapter->vdev_id, adapter->device_mode); |
| |
| if (QDF_SAP_MODE == adapter->device_mode) { |
| config = &adapter->session.ap.sap_config; |
| |
| /* default value */ |
| config->num_accept_mac = 0; |
| config->num_deny_mac = 0; |
| |
| /** |
| * access control policy |
| * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are |
| * listed in hostapd.deny file. |
| * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow stations which are |
| * listed in hostapd.accept file. |
| */ |
| if (NL80211_ACL_POLICY_DENY_UNLESS_LISTED == params->acl_policy) { |
| config->SapMacaddr_acl = eSAP_DENY_UNLESS_ACCEPTED; |
| } else if (NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED == |
| params->acl_policy) { |
| config->SapMacaddr_acl = eSAP_ACCEPT_UNLESS_DENIED; |
| } else { |
| hdd_warn("Acl Policy : %d is not supported", |
| params->acl_policy); |
| return -ENOTSUPP; |
| } |
| |
| if (eSAP_DENY_UNLESS_ACCEPTED == config->SapMacaddr_acl) { |
| config->num_accept_mac = params->n_acl_entries; |
| for (i = 0; i < params->n_acl_entries; i++) { |
| hdd_debug("** Add ACL MAC entry %i in WhiletList :" |
| QDF_MAC_ADDR_FMT, i, |
| QDF_MAC_ADDR_REF( |
| params->mac_addrs[i].addr)); |
| |
| qdf_mem_copy(&config->accept_mac[i], |
| params->mac_addrs[i].addr, |
| QDF_MAC_ADDR_SIZE); |
| } |
| } else if (eSAP_ACCEPT_UNLESS_DENIED == config->SapMacaddr_acl) { |
| config->num_deny_mac = params->n_acl_entries; |
| for (i = 0; i < params->n_acl_entries; i++) { |
| hdd_debug("** Add ACL MAC entry %i in BlackList :" |
| QDF_MAC_ADDR_FMT, i, |
| QDF_MAC_ADDR_REF( |
| params->mac_addrs[i].addr)); |
| |
| qdf_mem_copy(&config->deny_mac[i], |
| params->mac_addrs[i].addr, |
| QDF_MAC_ADDR_SIZE); |
| } |
| } |
| qdf_status = wlansap_set_mac_acl( |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter), config); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| hdd_err("SAP Set Mac Acl fail"); |
| return -EINVAL; |
| } |
| } else { |
| hdd_debug("Invalid device_mode %s(%d)", |
| qdf_opmode_str(adapter->device_mode), |
| adapter->device_mode); |
| return -EINVAL; |
| } |
| hdd_exit(); |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_mac_acl() - SSR wrapper for |
| * __wlan_hdd_cfg80211_set_mac_acl |
| * @wiphy: pointer to wiphy structure |
| * @dev: pointer to net_device |
| * @params: pointer to cfg80211_acl_data |
| * |
| * Return; 0 on success, error number otherwise |
| */ |
| static int |
| wlan_hdd_cfg80211_set_mac_acl(struct wiphy *wiphy, |
| struct net_device *dev, |
| const struct cfg80211_acl_data *params) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_mac_acl(wiphy, dev, params); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #ifdef WLAN_NL80211_TESTMODE |
| #ifdef FEATURE_WLAN_LPHB |
| /** |
| * wlan_hdd_cfg80211_lphb_ind_handler() - handle low power heart beat indication |
| * @hdd_ctx: Pointer to hdd context |
| * @lphbInd: Pointer to low power heart beat indication parameter |
| * |
| * Return: none |
| */ |
| static void wlan_hdd_cfg80211_lphb_ind_handler(void *hdd_ctx, |
| struct pmo_lphb_rsp *lphb_ind) |
| { |
| struct sk_buff *skb; |
| |
| hdd_debug("LPHB indication arrived"); |
| |
| if (0 != wlan_hdd_validate_context((struct hdd_context *) hdd_ctx)) |
| return; |
| |
| if (!lphb_ind) { |
| hdd_err("invalid argument lphbInd"); |
| return; |
| } |
| |
| skb = cfg80211_testmode_alloc_event_skb(((struct hdd_context *) hdd_ctx)-> |
| wiphy, sizeof(*lphb_ind), GFP_ATOMIC); |
| if (!skb) { |
| hdd_err("LPHB timeout, NL buffer alloc fail"); |
| return; |
| } |
| |
| if (nla_put_u32(skb, WLAN_HDD_TM_ATTR_CMD, WLAN_HDD_TM_CMD_WLAN_HB)) { |
| hdd_err("WLAN_HDD_TM_ATTR_CMD put fail"); |
| goto nla_put_failure; |
| } |
| if (nla_put_u32(skb, WLAN_HDD_TM_ATTR_TYPE, lphb_ind->protocol_type)) { |
| hdd_err("WLAN_HDD_TM_ATTR_TYPE put fail"); |
| goto nla_put_failure; |
| } |
| if (nla_put(skb, WLAN_HDD_TM_ATTR_DATA, sizeof(*lphb_ind), |
| lphb_ind)) { |
| hdd_err("WLAN_HDD_TM_ATTR_DATA put fail"); |
| goto nla_put_failure; |
| } |
| cfg80211_testmode_event(skb, GFP_ATOMIC); |
| return; |
| |
| nla_put_failure: |
| hdd_err("NLA Put fail"); |
| kfree_skb(skb); |
| } |
| #endif /* FEATURE_WLAN_LPHB */ |
| |
| /** |
| * __wlan_hdd_cfg80211_testmode() - test mode |
| * @wiphy: Pointer to wiphy |
| * @data: Data pointer |
| * @len: Data length |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int __wlan_hdd_cfg80211_testmode(struct wiphy *wiphy, |
| void *data, int len) |
| { |
| struct nlattr *tb[WLAN_HDD_TM_ATTR_MAX + 1]; |
| int err; |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| |
| hdd_enter(); |
| |
| err = wlan_hdd_validate_context(hdd_ctx); |
| if (err) |
| return err; |
| |
| if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { |
| hdd_err("Driver Modules are closed"); |
| return -EINVAL; |
| } |
| |
| err = wlan_cfg80211_nla_parse(tb, WLAN_HDD_TM_ATTR_MAX, data, |
| len, wlan_hdd_tm_policy); |
| if (err) { |
| hdd_err("Testmode INV ATTR"); |
| return err; |
| } |
| |
| if (!tb[WLAN_HDD_TM_ATTR_CMD]) { |
| hdd_err("Testmode INV CMD"); |
| return -EINVAL; |
| } |
| |
| qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, |
| TRACE_CODE_HDD_CFG80211_TESTMODE, |
| NO_SESSION, nla_get_u32(tb[WLAN_HDD_TM_ATTR_CMD])); |
| |
| switch (nla_get_u32(tb[WLAN_HDD_TM_ATTR_CMD])) { |
| #ifdef FEATURE_WLAN_LPHB |
| /* Low Power Heartbeat configuration request */ |
| case WLAN_HDD_TM_CMD_WLAN_HB: |
| { |
| int buf_len; |
| void *buf; |
| struct pmo_lphb_req *hb_params = NULL; |
| struct pmo_lphb_req *hb_params_temp = NULL; |
| QDF_STATUS status; |
| |
| if (!tb[WLAN_HDD_TM_ATTR_DATA]) { |
| hdd_err("Testmode INV DATA"); |
| return -EINVAL; |
| } |
| |
| buf = nla_data(tb[WLAN_HDD_TM_ATTR_DATA]); |
| buf_len = nla_len(tb[WLAN_HDD_TM_ATTR_DATA]); |
| if (buf_len < sizeof(*hb_params_temp)) { |
| hdd_err("Invalid buffer length for TM_ATTR_DATA"); |
| return -EINVAL; |
| } |
| |
| hb_params_temp = (struct pmo_lphb_req *) buf; |
| if ((hb_params_temp->cmd == pmo_lphb_set_tcp_pararm_indid) |
| && (hb_params_temp->params.lphb_tcp_params. |
| time_period_sec == 0)) |
| return -EINVAL; |
| |
| if (buf_len > sizeof(*hb_params)) { |
| hdd_err("buf_len=%d exceeded hb_params size limit", |
| buf_len); |
| return -ERANGE; |
| } |
| |
| hb_params = (struct pmo_lphb_req *)qdf_mem_malloc( |
| sizeof(*hb_params)); |
| if (!hb_params) |
| return -ENOMEM; |
| |
| qdf_mem_zero(hb_params, sizeof(*hb_params)); |
| qdf_mem_copy(hb_params, buf, buf_len); |
| status = ucfg_pmo_lphb_config_req( |
| hdd_ctx->psoc, |
| hb_params, (void *)hdd_ctx, |
| wlan_hdd_cfg80211_lphb_ind_handler); |
| if (status != QDF_STATUS_SUCCESS) |
| hdd_err("LPHB Config Fail, disable"); |
| |
| qdf_mem_free(hb_params); |
| return 0; |
| } |
| #endif /* FEATURE_WLAN_LPHB */ |
| |
| #if defined(QCA_WIFI_FTM) |
| case WLAN_HDD_TM_CMD_WLAN_FTM: |
| { |
| if (QDF_GLOBAL_FTM_MODE != hdd_get_conparam()) { |
| hdd_err("FTM Command not allowed in mission mode, mode %d", |
| hdd_get_conparam()); |
| return -EINVAL; |
| } |
| |
| err = wlan_cfg80211_ftm_testmode_cmd(hdd_ctx->pdev, |
| data, len); |
| break; |
| } |
| #endif |
| default: |
| hdd_err("command: %d not supported", |
| nla_get_u32(tb[WLAN_HDD_TM_ATTR_CMD])); |
| return -EOPNOTSUPP; |
| } |
| |
| hdd_exit(); |
| return err; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_testmode() - test mode |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @data: Data pointer |
| * @len: Data length |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int wlan_hdd_cfg80211_testmode(struct wiphy *wiphy, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) |
| struct wireless_dev *wdev, |
| #endif |
| void *data, int len) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_testmode(wiphy, data, len); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| #endif /* CONFIG_NL80211_TESTMODE */ |
| |
| #ifdef QCA_HT_2040_COEX |
| /** |
| * __wlan_hdd_cfg80211_set_ap_channel_width() - set ap channel bandwidth |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @chandef: Pointer to channel definition parameter |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_set_ap_channel_width(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_chan_def *chandef) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx; |
| QDF_STATUS status; |
| int retval = 0; |
| enum nl80211_channel_type channel_type; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EINVAL; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| if (!(adapter->device_mode == QDF_SAP_MODE || |
| adapter->device_mode == QDF_P2P_GO_MODE)) |
| return -EOPNOTSUPP; |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (status) |
| return status; |
| |
| if (chandef->width < NL80211_CHAN_WIDTH_80) |
| channel_type = cfg80211_get_chandef_type(chandef); |
| else |
| channel_type = NL80211_CHAN_HT40PLUS; |
| hdd_debug("Channel width changed to %d ", channel_type); |
| |
| /* Change SAP ht2040 mode */ |
| status = hdd_set_sap_ht2040_mode(adapter, channel_type); |
| if (status != QDF_STATUS_SUCCESS) { |
| hdd_err("Cannot set SAP HT20/40 mode!"); |
| retval = -EINVAL; |
| } |
| |
| return retval; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_ap_channel_width() - set ap channel bandwidth |
| * @wiphy: Pointer to wiphy |
| * @dev: Pointer to network device |
| * @chandef: Pointer to channel definition parameter |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int |
| wlan_hdd_cfg80211_set_ap_channel_width(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_chan_def *chandef) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_ap_channel_width(wiphy, dev, chandef); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| |
| #ifdef CHANNEL_SWITCH_SUPPORTED |
| /** |
| * __wlan_hdd_cfg80211_channel_switch()- function to switch |
| * channel in SAP/GO |
| * @wiphy: wiphy pointer |
| * @dev: dev pointer. |
| * @csa_params: Change channel params |
| * |
| * This function is called to switch channel in SAP/GO |
| * |
| * Return: 0 if success else return non zero |
| */ |
| static int __wlan_hdd_cfg80211_channel_switch(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_csa_settings *csa_params) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx; |
| int ret; |
| enum phy_ch_width ch_width; |
| bool status; |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| |
| if (0 != ret) |
| return ret; |
| |
| if ((QDF_P2P_GO_MODE != adapter->device_mode) && |
| (QDF_SAP_MODE != adapter->device_mode)) |
| return -ENOTSUPP; |
| |
| status = policy_mgr_is_sap_allowed_on_dfs_freq( |
| hdd_ctx->pdev, |
| adapter->vdev_id, |
| csa_params->chandef.chan->center_freq); |
| if (!status) |
| return -EINVAL; |
| |
| wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc, adapter->vdev_id, |
| CSA_REASON_USER_INITIATED); |
| |
| ch_width = hdd_map_nl_chan_width(csa_params->chandef.width); |
| hdd_debug("Freq %d width %d ch_width %d", |
| csa_params->chandef.chan->center_freq, |
| csa_params->chandef.width, ch_width); |
| |
| ret = |
| hdd_softap_set_channel_change(dev, |
| csa_params->chandef.chan->center_freq, |
| ch_width, false); |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_channel_switch()- function to switch |
| * channel in SAP/GO |
| * @wiphy: wiphy pointer |
| * @dev: dev pointer. |
| * @csa_params: Change channel params |
| * |
| * This function is called to switch channel in SAP/GO |
| * |
| * Return: 0 if success else return non zero |
| */ |
| static int wlan_hdd_cfg80211_channel_switch(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_csa_settings *csa_params) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_channel_switch(wiphy, dev, csa_params); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| |
| int wlan_hdd_change_hw_mode_for_given_chnl(struct hdd_adapter *adapter, |
| uint32_t chan_freq, |
| enum policy_mgr_conn_update_reason reason) |
| { |
| QDF_STATUS status; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| |
| hdd_enter(); |
| |
| status = policy_mgr_reset_connection_update(hdd_ctx->psoc); |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("clearing event failed"); |
| |
| status = policy_mgr_current_connections_update( |
| hdd_ctx->psoc, adapter->vdev_id, |
| chan_freq, reason, POLICY_MGR_DEF_REQ_ID); |
| switch (status) { |
| case QDF_STATUS_E_FAILURE: |
| /* |
| * QDF_STATUS_E_FAILURE indicates that some error has occurred |
| * while changing the hw mode |
| */ |
| hdd_err("ERROR: connections update failed!!"); |
| return -EINVAL; |
| |
| case QDF_STATUS_SUCCESS: |
| /* |
| * QDF_STATUS_SUCCESS indicates that HW mode change has been |
| * triggered and wait for it to finish. |
| */ |
| status = policy_mgr_wait_for_connection_update( |
| hdd_ctx->psoc); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("ERROR: qdf wait for event failed!!"); |
| return -EINVAL; |
| } |
| if (QDF_MONITOR_MODE == adapter->device_mode) |
| hdd_info("Monitor mode:channel freq:%d (SMM->DBS)", chan_freq); |
| break; |
| |
| default: |
| /* |
| * QDF_STATUS_E_NOSUPPORT indicates that no HW mode change is |
| * required, so caller can proceed further. |
| */ |
| break; |
| |
| } |
| hdd_exit(); |
| |
| return 0; |
| } |
| |
| #ifdef FEATURE_MONITOR_MODE_SUPPORT |
| /** |
| * wlan_hdd_cfg80211_set_mon_ch() - Set monitor mode capture channel |
| * @wiphy: Handle to struct wiphy to get handle to module context. |
| * @chandef: Contains information about the capture channel to be set. |
| * |
| * This interface is called if and only if monitor mode interface alone is |
| * active. |
| * |
| * Return: 0 success or error code on failure. |
| */ |
| static int __wlan_hdd_cfg80211_set_mon_ch(struct wiphy *wiphy, |
| struct cfg80211_chan_def *chandef) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct hdd_adapter *adapter; |
| struct hdd_station_ctx *sta_ctx; |
| struct hdd_mon_set_ch_info *ch_info; |
| QDF_STATUS status; |
| mac_handle_t mac_handle; |
| struct qdf_mac_addr bssid; |
| struct csr_roam_profile roam_profile; |
| struct ch_params ch_params = {0}; |
| int ret; |
| enum channel_state chan_freq_state; |
| uint8_t max_fw_bw; |
| enum phy_ch_width ch_width; |
| qdf_freq_t sec_ch_2g_freq = 0; |
| |
| hdd_enter(); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| mac_handle = hdd_ctx->mac_handle; |
| |
| adapter = hdd_get_adapter(hdd_ctx, QDF_MONITOR_MODE); |
| if (!adapter) |
| return -EIO; |
| |
| hdd_debug("%s: set monitor mode freq %d", |
| adapter->dev->name, chandef->chan->center_freq); |
| |
| /* Verify channel state before accepting this request */ |
| chan_freq_state = |
| wlan_reg_get_channel_state_for_freq(hdd_ctx->pdev, |
| chandef->chan->center_freq); |
| if (chan_freq_state == CHANNEL_STATE_DISABLE || |
| chan_freq_state == CHANNEL_STATE_INVALID) { |
| hdd_err("Invalid chan freq received for monitor mode aborting"); |
| return -EINVAL; |
| } |
| |
| /* Verify the BW before accepting this request */ |
| ch_width = hdd_map_nl_chan_width(chandef->width); |
| |
| if (ch_width > CH_WIDTH_10MHZ || |
| (!cds_is_sub_20_mhz_enabled() && ch_width > CH_WIDTH_160MHZ)) { |
| hdd_err("invalid BW received %d", ch_width); |
| return -EINVAL; |
| } |
| |
| max_fw_bw = sme_get_vht_ch_width(); |
| |
| if ((ch_width == CH_WIDTH_160MHZ && |
| max_fw_bw <= WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) || |
| (ch_width == CH_WIDTH_80P80MHZ && |
| max_fw_bw <= WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ)) { |
| hdd_err("FW does not support this BW %d max BW supported %d", |
| ch_width, max_fw_bw); |
| return -EINVAL; |
| } |
| sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| ch_info = &sta_ctx->ch_info; |
| roam_profile.ChannelInfo.freq_list = &ch_info->freq; |
| roam_profile.ChannelInfo.numOfChannels = 1; |
| roam_profile.phyMode = ch_info->phy_mode; |
| roam_profile.ch_params.ch_width = ch_width; |
| if (WLAN_REG_IS_24GHZ_CH_FREQ(chandef->chan->center_freq) && |
| chandef->width == NL80211_CHAN_WIDTH_40 && |
| chandef->center_freq1) { |
| if (chandef->center_freq1 > chandef->chan->center_freq) |
| sec_ch_2g_freq = chandef->chan->center_freq + 20; |
| else if (chandef->center_freq1 < chandef->chan->center_freq) |
| sec_ch_2g_freq = chandef->chan->center_freq - 20; |
| } |
| hdd_debug("set mon ch:width=%d, freq %d sec_ch_2g_freq=%d", |
| chandef->width, chandef->chan->center_freq, sec_ch_2g_freq); |
| hdd_select_cbmode(adapter, chandef->chan->center_freq, sec_ch_2g_freq, |
| &roam_profile.ch_params); |
| qdf_mem_copy(bssid.bytes, adapter->mac_addr.bytes, |
| QDF_MAC_ADDR_SIZE); |
| |
| ch_params.ch_width = ch_width; |
| wlan_reg_set_channel_params_for_freq(hdd_ctx->pdev, |
| chandef->chan->center_freq, |
| sec_ch_2g_freq, &ch_params); |
| if (wlan_hdd_change_hw_mode_for_given_chnl(adapter, |
| chandef->chan->center_freq, |
| POLICY_MGR_UPDATE_REASON_SET_OPER_CHAN)) { |
| hdd_err("Failed to change hw mode"); |
| return -EINVAL; |
| } |
| |
| if (adapter->monitor_mode_vdev_up_in_progress) { |
| hdd_err_rl("monitor mode vdev up in progress"); |
| return -EBUSY; |
| } |
| |
| status = qdf_event_reset(&adapter->qdf_monitor_mode_vdev_up_event); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err_rl("failed to reinit monitor mode vdev up event"); |
| return qdf_status_to_os_return(status); |
| } |
| adapter->monitor_mode_vdev_up_in_progress = true; |
| |
| status = sme_roam_channel_change_req(mac_handle, bssid, |
| adapter->vdev_id, |
| &roam_profile.ch_params, |
| &roam_profile); |
| if (status) { |
| hdd_err_rl("Failed to set sme_RoamChannel for monitor mode status: %d", |
| status); |
| adapter->monitor_mode_vdev_up_in_progress = false; |
| ret = qdf_status_to_os_return(status); |
| return ret; |
| } |
| |
| /* block on a completion variable until vdev up success*/ |
| status = qdf_wait_for_event_completion( |
| &adapter->qdf_monitor_mode_vdev_up_event, |
| WLAN_MONITOR_MODE_VDEV_UP_EVT); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err_rl("monitor vdev up event time out vdev id: %d", |
| adapter->vdev_id); |
| if (adapter->qdf_monitor_mode_vdev_up_event.force_set) |
| /* |
| * SSR/PDR has caused shutdown, which has |
| * forcefully set the event. |
| */ |
| hdd_err_rl("monitor mode vdev up event forcefully set"); |
| else if (status == QDF_STATUS_E_TIMEOUT) |
| hdd_err_rl("monitor mode vdev up timed out"); |
| else |
| hdd_err_rl("Failed monitor mode vdev up(status-%d)", |
| status); |
| |
| adapter->monitor_mode_vdev_up_in_progress = false; |
| return qdf_status_to_os_return(status); |
| } |
| |
| hdd_exit(); |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_mon_ch() - Set monitor mode capture channel |
| * @wiphy: Handle to struct wiphy to get handle to module context. |
| * @chandef: Contains information about the capture channel to be set. |
| * |
| * This interface is called if and only if monitor mode interface alone is |
| * active. |
| * |
| * Return: 0 success or error code on failure. |
| */ |
| static int wlan_hdd_cfg80211_set_mon_ch(struct wiphy *wiphy, |
| struct cfg80211_chan_def *chandef) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_mon_ch(wiphy, chandef); |
| |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| #endif |
| |
| #define CNT_DIFF(cur, prev) \ |
| ((cur >= prev) ? (cur - prev) : (cur + (MAX_COUNT - (prev) + 1))) |
| #define MAX_COUNT 0xffffffff |
| static void hdd_update_chan_info(struct hdd_context *hdd_ctx, |
| struct scan_chan_info *chan, |
| struct scan_chan_info *info, uint32_t cmd_flag) |
| { |
| if ((info->cmd_flag != WMI_CHAN_InFO_START_RESP) && |
| (info->cmd_flag != WMI_CHAN_InFO_END_RESP)) |
| hdd_err("cmd flag is invalid: %d", info->cmd_flag); |
| |
| mutex_lock(&hdd_ctx->chan_info_lock); |
| |
| if (info->cmd_flag == WMI_CHAN_InFO_START_RESP) |
| qdf_mem_zero(chan, sizeof(*chan)); |
| |
| chan->freq = info->freq; |
| chan->noise_floor = info->noise_floor; |
| chan->clock_freq = info->clock_freq; |
| chan->cmd_flag = info->cmd_flag; |
| chan->cycle_count = CNT_DIFF(info->cycle_count, chan->cycle_count); |
| |
| chan->rx_clear_count = |
| CNT_DIFF(info->rx_clear_count, chan->rx_clear_count); |
| |
| chan->tx_frame_count = |
| CNT_DIFF(info->tx_frame_count, chan->tx_frame_count); |
| |
| mutex_unlock(&hdd_ctx->chan_info_lock); |
| |
| } |
| #undef CNT_DIFF |
| #undef MAX_COUNT |
| |
| #ifndef UPDATE_ASSOC_IE |
| #define UPDATE_ASSOC_IE BIT(0) |
| #endif |
| |
| #ifndef UPDATE_FILS_ERP_INFO |
| #define UPDATE_FILS_ERP_INFO BIT(1) |
| #endif |
| |
| #ifndef UPDATE_FILS_AUTH_TYPE |
| #define UPDATE_FILS_AUTH_TYPE BIT(2) |
| #endif |
| |
| #if defined(WLAN_FEATURE_FILS_SK) &&\ |
| (defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) ||\ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))) &&\ |
| (defined(CFG80211_UPDATE_CONNECT_PARAMS) ||\ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0))) |
| static inline int |
| hdd_update_connect_params_fils_info(struct hdd_adapter *adapter, |
| struct hdd_context *hdd_ctx, |
| struct cfg80211_connect_params *req, |
| uint32_t changed) |
| { |
| uint8_t *buf; |
| QDF_STATUS status; |
| enum wlan_fils_auth_type auth_type; |
| struct wlan_fils_con_info *fils_info; |
| int ret = 0; |
| |
| fils_info = qdf_mem_malloc(sizeof(*fils_info)); |
| if (!fils_info) |
| return -EINVAL; |
| |
| fils_info->is_fils_connection = true; |
| if (changed & UPDATE_FILS_ERP_INFO) { |
| fils_info->username_len = req->fils_erp_username_len + |
| sizeof(char) + |
| req->fils_erp_realm_len; |
| if (fils_info->username_len > |
| WLAN_CM_FILS_MAX_KEYNAME_NAI_LENGTH) { |
| hdd_err("Key NAI Length %d", |
| fils_info->username_len); |
| ret = -EINVAL; |
| goto free_mem; |
| } |
| if (req->fils_erp_username_len && req->fils_erp_username) { |
| buf = fils_info->username; |
| qdf_mem_copy(buf, req->fils_erp_username, |
| req->fils_erp_username_len); |
| buf += req->fils_erp_username_len; |
| *buf++ = '@'; |
| qdf_mem_copy(buf, req->fils_erp_realm, |
| req->fils_erp_realm_len); |
| } |
| |
| fils_info->next_seq_num = req->fils_erp_next_seq_num + 1; |
| fils_info->rrk_len = req->fils_erp_rrk_len; |
| |
| if (fils_info->rrk_len > WLAN_CM_FILS_MAX_RRK_LENGTH) { |
| hdd_err("r_rk_length is invalid %d", |
| fils_info->rrk_len); |
| ret = -EINVAL; |
| goto free_mem; |
| } |
| |
| if (req->fils_erp_rrk_len && req->fils_erp_rrk) |
| qdf_mem_copy(fils_info->rrk, req->fils_erp_rrk, |
| fils_info->rrk_len); |
| |
| fils_info->realm_len = req->fils_erp_realm_len; |
| if (fils_info->realm_len > WLAN_CM_FILS_MAX_REALM_LEN) { |
| hdd_err("Invalid fils realm len %d", |
| fils_info->realm_len); |
| ret = -EINVAL; |
| goto free_mem; |
| } |
| if (req->fils_erp_realm_len && req->fils_erp_realm) |
| qdf_mem_copy(fils_info->realm, req->fils_erp_realm, |
| fils_info->realm_len); |
| } |
| |
| if (changed & UPDATE_FILS_AUTH_TYPE) { |
| auth_type = osif_cm_get_fils_auth_type(req->auth_type); |
| if (auth_type == FILS_PK_MAX) { |
| hdd_err("invalid auth type for fils %d", |
| req->auth_type); |
| ret = -EINVAL; |
| goto free_mem; |
| } |
| |
| fils_info->auth_type = auth_type; |
| } |
| |
| hdd_debug("fils conn update: changed %x is_fils %d keyname nai len %d", |
| changed, fils_info->is_fils_connection, |
| fils_info->username_len); |
| /* |
| * Update the FILS config from adapter->roam_profile to |
| * csr_session |
| */ |
| status = ucfg_cm_update_fils_config(hdd_ctx->psoc, adapter->vdev_id, |
| fils_info); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("Update FILS connect params failed %d", status); |
| free_mem: |
| qdf_mem_free(fils_info); |
| |
| return ret; |
| } |
| #else |
| static inline int |
| hdd_update_connect_params_fils_info(struct hdd_adapter *adapter, |
| struct hdd_context *hdd_ctx, |
| struct cfg80211_connect_params *req, |
| uint32_t changed) |
| { |
| return -EINVAL; |
| } |
| #endif |
| |
| #if defined(CFG80211_UPDATE_CONNECT_PARAMS) ||\ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) |
| |
| /** |
| * __wlan_hdd_cfg80211_update_connect_params - update connect params |
| * @wiphy: Handle to struct wiphy to get handle to module context. |
| * @dev: Pointer to network device |
| * @req: Pointer to connect params |
| * @changed: Bitmap used to indicate the changed params |
| * |
| * Update the connect parameters while connected to a BSS. The updated |
| * parameters can be used by driver/firmware for subsequent BSS selection |
| * (roaming) decisions and to form the Authentication/(Re)Association |
| * Request frames. This call does not request an immediate disassociation |
| * or reassociation with the current BSS, i.e., this impacts only |
| * subsequent (re)associations. The bits in changed are defined in enum |
| * cfg80211_connect_params_changed |
| * |
| * Return: zero for success, non-zero for failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_update_connect_params(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_connect_params *req, |
| uint32_t changed) |
| { |
| int ret; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| QDF_STATUS status; |
| mac_handle_t mac_handle; |
| struct element_info assoc_ie; |
| |
| hdd_enter_dev(dev); |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return -EINVAL; |
| |
| mac_handle = hdd_ctx->mac_handle; |
| |
| if (changed & UPDATE_ASSOC_IE) { |
| assoc_ie.len = req->ie_len; |
| assoc_ie.ptr = (uint8_t *)req->ie; |
| /* |
| * Update this assoc IE received from user space to |
| * umac. RSO command will pick up the assoc |
| * IEs to be sent to firmware from the umac. |
| */ |
| ucfg_cm_update_session_assoc_ie(hdd_ctx->psoc, adapter->vdev_id, |
| &assoc_ie); |
| } |
| |
| if ((changed & UPDATE_FILS_ERP_INFO) || |
| (changed & UPDATE_FILS_AUTH_TYPE)) { |
| ret = hdd_update_connect_params_fils_info(adapter, hdd_ctx, |
| req, changed); |
| if (ret) |
| return -EINVAL; |
| |
| if (!hdd_ctx->is_fils_roaming_supported) { |
| hdd_debug("FILS roaming support %d", |
| hdd_ctx->is_fils_roaming_supported); |
| return 0; |
| } |
| } |
| |
| if (changed) { |
| status = sme_send_rso_connect_params(mac_handle, |
| adapter->vdev_id); |
| if (QDF_IS_STATUS_ERROR(status)) |
| hdd_err("Update connect params to fw failed %d", |
| status); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_update_connect_params - SSR wrapper for |
| * __wlan_hdd_cfg80211_update_connect_params |
| * @wiphy: Pointer to wiphy structure |
| * @dev: Pointer to net_device |
| * @req: Pointer to connect params |
| * @changed: flags used to indicate the changed params |
| * |
| * Return: zero for success, non-zero for failure |
| */ |
| static int |
| wlan_hdd_cfg80211_update_connect_params(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_connect_params *req, |
| uint32_t changed) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_update_connect_params(wiphy, dev, |
| req, changed); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| |
| #if defined(WLAN_FEATURE_SAE) && \ |
| (defined(CFG80211_EXTERNAL_AUTH_SUPPORT) || \ |
| LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) |
| #if (defined(CFG80211_EXTERNAL_AUTH_AP_SUPPORT) || \ |
| LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) |
| /** |
| * wlan_hdd_extauth_cache_pmkid() - Extract and cache pmkid |
| * @adapter: hdd vdev/net_device context |
| * @mac_handle: Handle to the MAC |
| * @params: Pointer to external auth params. |
| * |
| * Extract the PMKID and BSS from external auth params and add to the |
| * PMKSA Cache in CSR. |
| */ |
| static void |
| wlan_hdd_extauth_cache_pmkid(struct hdd_adapter *adapter, |
| mac_handle_t mac_handle, |
| struct cfg80211_external_auth_params *params) |
| { |
| struct wlan_crypto_pmksa *pmk_cache; |
| QDF_STATUS result; |
| |
| if (params->pmkid) { |
| pmk_cache = qdf_mem_malloc(sizeof(*pmk_cache)); |
| if (!pmk_cache) |
| return; |
| |
| qdf_mem_copy(pmk_cache->bssid.bytes, params->bssid, |
| QDF_MAC_ADDR_SIZE); |
| qdf_mem_copy(pmk_cache->pmkid, params->pmkid, |
| PMKID_LEN); |
| result = wlan_hdd_set_pmksa_cache(adapter, pmk_cache); |
| if (!QDF_IS_STATUS_SUCCESS(result)) |
| hdd_debug("external_auth: Failed to cache PMKID"); |
| |
| qdf_mem_free(pmk_cache); |
| } |
| |
| } |
| |
| /** |
| * wlan_hdd_extauth_copy_pmkid() - Copy the pmkid received from the |
| * external authentication command received from the userspace. |
| * @params: pointer to auth params |
| * @pmkid: Pointer to destination pmkid buffer to be filled |
| * |
| * The caller should ensure that destination pmkid buffer is not NULL. |
| * |
| * Return: None |
| */ |
| static void |
| wlan_hdd_extauth_copy_pmkid(struct cfg80211_external_auth_params *params, |
| uint8_t *pmkid) |
| { |
| if (params->pmkid) |
| qdf_mem_copy(pmkid, params->pmkid, PMKID_LEN); |
| } |
| |
| #else |
| static void |
| wlan_hdd_extauth_cache_pmkid(struct hdd_adapter *adapter, |
| mac_handle_t mac_handle, |
| struct cfg80211_external_auth_params *params) |
| {} |
| |
| static void |
| wlan_hdd_extauth_copy_pmkid(struct cfg80211_external_auth_params *params, |
| uint8_t *pmkid) |
| {} |
| #endif |
| /** |
| * __wlan_hdd_cfg80211_external_auth() - Handle external auth |
| * |
| * @wiphy: Pointer to wireless phy |
| * @dev: net device |
| * @params: Pointer to external auth params. |
| * Return: 0 on success, negative errno on failure |
| * |
| * Userspace sends status of the external authentication(e.g., SAE) with a peer. |
| * The message carries BSSID of the peer and auth status (WLAN_STATUS_SUCCESS/ |
| * WLAN_STATUS_UNSPECIFIED_FAILURE) in params. |
| * Userspace may send PMKID in params, which can be used for |
| * further connections. |
| */ |
| static int |
| __wlan_hdd_cfg80211_external_auth(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_external_auth_params *params) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| int ret; |
| mac_handle_t mac_handle; |
| struct qdf_mac_addr peer_mac_addr; |
| uint8_t pmkid[PMKID_LEN] = {0}; |
| |
| if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) |
| return -EINVAL; |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| hdd_debug("external_auth status: %d peer mac: " QDF_MAC_ADDR_FMT, |
| params->status, QDF_MAC_ADDR_REF(params->bssid)); |
| mac_handle = hdd_ctx->mac_handle; |
| qdf_mem_copy(peer_mac_addr.bytes, params->bssid, QDF_MAC_ADDR_SIZE); |
| |
| wlan_hdd_extauth_cache_pmkid(adapter, mac_handle, params); |
| |
| wlan_hdd_extauth_copy_pmkid(params, pmkid); |
| sme_handle_sae_msg(mac_handle, adapter->vdev_id, params->status, |
| peer_mac_addr, pmkid); |
| |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_external_auth() - Handle external auth |
| * @wiphy: Pointer to wireless phy |
| * @dev: net device |
| * @params: Pointer to external auth params |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int |
| wlan_hdd_cfg80211_external_auth(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_external_auth_params *params) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_external_auth(wiphy, dev, params); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| #endif |
| |
| #if defined(WLAN_FEATURE_NAN) && \ |
| (KERNEL_VERSION(4, 9, 0) <= LINUX_VERSION_CODE) |
| static int |
| wlan_hdd_cfg80211_start_nan(struct wiphy *wiphy, struct wireless_dev *wdev, |
| struct cfg80211_nan_conf *conf) |
| { |
| return -EOPNOTSUPP; |
| } |
| |
| static void |
| wlan_hdd_cfg80211_stop_nan(struct wiphy *wiphy, struct wireless_dev *wdev) |
| { |
| } |
| |
| static int wlan_hdd_cfg80211_add_nan_func(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| struct cfg80211_nan_func *nan_func) |
| { |
| return -EOPNOTSUPP; |
| } |
| |
| static void wlan_hdd_cfg80211_del_nan_func(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| u64 cookie) |
| { |
| } |
| |
| static int wlan_hdd_cfg80211_nan_change_conf(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| struct cfg80211_nan_conf *conf, |
| u32 changes) |
| { |
| return -EOPNOTSUPP; |
| } |
| #endif |
| |
| /** |
| * wlan_hdd_chan_info_cb() - channel info callback |
| * @chan_info: struct scan_chan_info |
| * |
| * Store channel info into HDD context |
| * |
| * Return: None. |
| */ |
| static void wlan_hdd_chan_info_cb(struct scan_chan_info *info) |
| { |
| struct hdd_context *hdd_ctx; |
| struct scan_chan_info *chan; |
| uint8_t idx; |
| |
| hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); |
| if (wlan_hdd_validate_context(hdd_ctx) != 0) |
| return; |
| |
| if (!hdd_ctx->chan_info) { |
| hdd_err("chan_info is NULL"); |
| return; |
| } |
| |
| chan = hdd_ctx->chan_info; |
| for (idx = 0; idx < SIR_MAX_NUM_CHANNELS; idx++) { |
| if (chan[idx].freq == info->freq) { |
| hdd_update_chan_info(hdd_ctx, &chan[idx], info, |
| info->cmd_flag); |
| hdd_debug("cmd:%d freq:%u nf:%d cc:%u rcc:%u clk:%u cmd:%d tfc:%d index:%d", |
| chan[idx].cmd_flag, chan[idx].freq, |
| chan[idx].noise_floor, |
| chan[idx].cycle_count, |
| chan[idx].rx_clear_count, |
| chan[idx].clock_freq, chan[idx].cmd_flag, |
| chan[idx].tx_frame_count, idx); |
| if (chan[idx].freq == 0) |
| break; |
| |
| } |
| } |
| } |
| |
| /** |
| * wlan_hdd_init_chan_info() - init chan info in hdd context |
| * @hdd_ctx: HDD context pointer |
| * |
| * Return: none |
| */ |
| void wlan_hdd_init_chan_info(struct hdd_context *hdd_ctx) |
| { |
| uint32_t num_2g, num_5g, index = 0; |
| mac_handle_t mac_handle; |
| |
| hdd_ctx->chan_info = NULL; |
| if (!ucfg_scan_is_snr_monitor_enabled(hdd_ctx->psoc)) { |
| hdd_debug("SNR monitoring is disabled"); |
| return; |
| } |
| |
| hdd_ctx->chan_info = |
| qdf_mem_malloc(sizeof(struct scan_chan_info) |
| * NUM_CHANNELS); |
| if (!hdd_ctx->chan_info) |
| return; |
| mutex_init(&hdd_ctx->chan_info_lock); |
| |
| num_2g = QDF_ARRAY_SIZE(hdd_channels_2_4_ghz); |
| for (; index < num_2g; index++) { |
| hdd_ctx->chan_info[index].freq = |
| hdd_channels_2_4_ghz[index].center_freq; |
| } |
| |
| num_5g = QDF_ARRAY_SIZE(hdd_channels_5_ghz); |
| for (; (index - num_2g) < num_5g; index++) { |
| if (wlan_reg_is_dsrc_freq( |
| hdd_channels_5_ghz[index - num_2g].center_freq)) |
| continue; |
| hdd_ctx->chan_info[index].freq = |
| hdd_channels_5_ghz[index - num_2g].center_freq; |
| } |
| |
| index = num_2g + num_5g; |
| index = wlan_hdd_populate_5dot9_chan_info(hdd_ctx, index); |
| |
| mac_handle = hdd_ctx->mac_handle; |
| sme_set_chan_info_callback(mac_handle, |
| &wlan_hdd_chan_info_cb); |
| } |
| |
| /** |
| * wlan_hdd_deinit_chan_info() - deinit chan info in hdd context |
| * @hdd_ctx: hdd context pointer |
| * |
| * Return: none |
| */ |
| void wlan_hdd_deinit_chan_info(struct hdd_context *hdd_ctx) |
| { |
| struct scan_chan_info *chan; |
| |
| chan = hdd_ctx->chan_info; |
| hdd_ctx->chan_info = NULL; |
| if (chan) |
| qdf_mem_free(chan); |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) || defined(WITH_BACKPORTS) |
| static enum rate_info_bw hdd_map_hdd_bw_to_os(enum hdd_rate_info_bw hdd_bw) |
| { |
| switch (hdd_bw) { |
| case HDD_RATE_BW_5: |
| return RATE_INFO_BW_5; |
| case HDD_RATE_BW_10: |
| return RATE_INFO_BW_10; |
| case HDD_RATE_BW_20: |
| return RATE_INFO_BW_20; |
| case HDD_RATE_BW_40: |
| return RATE_INFO_BW_40; |
| case HDD_RATE_BW_80: |
| return RATE_INFO_BW_80; |
| case HDD_RATE_BW_160: |
| return RATE_INFO_BW_160; |
| } |
| |
| hdd_err("Unhandled HDD_RATE_BW: %d", hdd_bw); |
| |
| return RATE_INFO_BW_20; |
| } |
| |
| void hdd_set_rate_bw(struct rate_info *info, enum hdd_rate_info_bw hdd_bw) |
| { |
| info->bw = hdd_map_hdd_bw_to_os(hdd_bw); |
| } |
| #else |
| static enum rate_info_flags hdd_map_hdd_bw_to_os(enum hdd_rate_info_bw hdd_bw) |
| { |
| switch (hdd_bw) { |
| case HDD_RATE_BW_5: |
| case HDD_RATE_BW_10: |
| case HDD_RATE_BW_20: |
| return (enum rate_info_flags)0; |
| case HDD_RATE_BW_40: |
| return RATE_INFO_FLAGS_40_MHZ_WIDTH; |
| case HDD_RATE_BW_80: |
| return RATE_INFO_FLAGS_80_MHZ_WIDTH; |
| case HDD_RATE_BW_160: |
| return RATE_INFO_FLAGS_160_MHZ_WIDTH; |
| } |
| |
| hdd_err("Unhandled HDD_RATE_BW: %d", hdd_bw); |
| |
| return (enum rate_info_flags)0; |
| } |
| |
| void hdd_set_rate_bw(struct rate_info *info, enum hdd_rate_info_bw hdd_bw) |
| { |
| const enum rate_info_flags all_bws = |
| RATE_INFO_FLAGS_40_MHZ_WIDTH | |
| RATE_INFO_FLAGS_80_MHZ_WIDTH | |
| RATE_INFO_FLAGS_80P80_MHZ_WIDTH | |
| RATE_INFO_FLAGS_160_MHZ_WIDTH; |
| |
| info->flags &= ~all_bws; |
| info->flags |= hdd_map_hdd_bw_to_os(hdd_bw); |
| } |
| #endif |
| |
| #if defined(CFG80211_EXTERNAL_DH_UPDATE_SUPPORT) || \ |
| (LINUX_VERSION_CODE > KERNEL_VERSION(5, 2, 0)) |
| void hdd_send_update_owe_info_event(struct hdd_adapter *adapter, |
| uint8_t sta_addr[], |
| uint8_t *owe_ie, |
| uint32_t owe_ie_len) |
| { |
| struct cfg80211_update_owe_info owe_info; |
| struct net_device *dev = adapter->dev; |
| |
| hdd_enter_dev(dev); |
| |
| qdf_mem_zero(&owe_info, sizeof(owe_info)); |
| qdf_mem_copy(owe_info.peer, sta_addr, ETH_ALEN); |
| owe_info.ie = owe_ie; |
| owe_info.ie_len = owe_ie_len; |
| |
| cfg80211_update_owe_info_event(dev, &owe_info, GFP_KERNEL); |
| |
| hdd_exit(); |
| } |
| #endif |
| |
| static int __wlan_hdd_cfg80211_set_chainmask(struct wiphy *wiphy, |
| uint32_t tx_mask, |
| uint32_t rx_mask) |
| { |
| int ret; |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| enum hdd_chain_mode chains; |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return -EINVAL; |
| |
| if (hdd_ctx->num_rf_chains != HDD_ANTENNA_MODE_2X2 || |
| !ucfg_mlme_is_chain_mask_supported(hdd_ctx->psoc)) { |
| hdd_info_rl("Chainmask can't be configured, num of rf chain %d", |
| hdd_ctx->num_rf_chains); |
| return -ENOTSUPP; |
| } |
| chains = HDD_CHAIN_MODE_2X2; |
| if (!tx_mask || tx_mask > chains || !rx_mask || rx_mask > chains) { |
| hdd_err_rl("Invalid masks. txMask: %d rxMask: %d num_rf_chains: %d", |
| tx_mask, rx_mask, hdd_ctx->num_rf_chains); |
| |
| return -EINVAL; |
| } |
| |
| ret = wma_cli_set_command(0, WMI_PDEV_PARAM_TX_CHAIN_MASK, |
| tx_mask, PDEV_CMD); |
| if (ret) |
| hdd_err_rl("Failed to set TX the mask"); |
| |
| ret = wma_cli_set_command(0, WMI_PDEV_PARAM_RX_CHAIN_MASK, |
| rx_mask, PDEV_CMD); |
| if (ret) |
| hdd_err_rl("Failed to set RX the mask"); |
| |
| return ret; |
| } |
| |
| static int wlan_hdd_cfg80211_set_chainmask(struct wiphy *wiphy, |
| uint32_t tx_mask, |
| uint32_t rx_mask) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_set_chainmask(wiphy, tx_mask, rx_mask); |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| static int __wlan_hdd_cfg80211_get_chainmask(struct wiphy *wiphy, |
| uint32_t *tx_mask, |
| uint32_t *rx_mask) |
| |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| int ret; |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return -EINVAL; |
| |
| *tx_mask = wma_cli_get_command(0, WMI_PDEV_PARAM_TX_CHAIN_MASK, |
| PDEV_CMD); |
| *rx_mask = wma_cli_get_command(0, WMI_PDEV_PARAM_RX_CHAIN_MASK, |
| PDEV_CMD); |
| |
| /* if 0 return max value as 0 mean no set cmnd received yet */ |
| if (!*tx_mask) |
| *tx_mask = hdd_ctx->num_rf_chains == HDD_ANTENNA_MODE_2X2 ? |
| HDD_CHAIN_MODE_2X2 : HDD_CHAIN_MODE_1X1; |
| if (!*rx_mask) |
| *rx_mask = hdd_ctx->num_rf_chains == HDD_ANTENNA_MODE_2X2 ? |
| HDD_CHAIN_MODE_2X2 : HDD_CHAIN_MODE_1X1; |
| hdd_debug("tx_mask: %d rx_mask: %d", *tx_mask, *rx_mask); |
| |
| return 0; |
| } |
| |
| static int wlan_hdd_cfg80211_get_chainmask(struct wiphy *wiphy, |
| uint32_t *tx_mask, |
| uint32_t *rx_mask) |
| { |
| struct osif_psoc_sync *psoc_sync; |
| int errno; |
| |
| errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_chainmask(wiphy, tx_mask, rx_mask); |
| osif_psoc_sync_op_stop(psoc_sync); |
| |
| return errno; |
| } |
| |
| enum qca_wlan_802_11_mode |
| hdd_convert_cfgdot11mode_to_80211mode(enum csr_cfgdot11mode mode) |
| { |
| switch (mode) { |
| case eCSR_CFG_DOT11_MODE_11A: |
| return QCA_WLAN_802_11_MODE_11A; |
| case eCSR_CFG_DOT11_MODE_11B: |
| return QCA_WLAN_802_11_MODE_11B; |
| case eCSR_CFG_DOT11_MODE_11G: |
| return QCA_WLAN_802_11_MODE_11G; |
| case eCSR_CFG_DOT11_MODE_11N: |
| return QCA_WLAN_802_11_MODE_11N; |
| case eCSR_CFG_DOT11_MODE_11AC: |
| return QCA_WLAN_802_11_MODE_11AC; |
| case eCSR_CFG_DOT11_MODE_11G_ONLY: |
| return QCA_WLAN_802_11_MODE_11G; |
| case eCSR_CFG_DOT11_MODE_11N_ONLY: |
| return QCA_WLAN_802_11_MODE_11N; |
| case eCSR_CFG_DOT11_MODE_11AC_ONLY: |
| return QCA_WLAN_802_11_MODE_11AC; |
| case eCSR_CFG_DOT11_MODE_11AX: |
| return QCA_WLAN_802_11_MODE_11AX; |
| case eCSR_CFG_DOT11_MODE_11AX_ONLY: |
| return QCA_WLAN_802_11_MODE_11AX; |
| case eCSR_CFG_DOT11_MODE_11BE: |
| case eCSR_CFG_DOT11_MODE_11BE_ONLY: |
| return QCA_WLAN_802_11_MODE_11BE; |
| case eCSR_CFG_DOT11_MODE_ABG: |
| case eCSR_CFG_DOT11_MODE_AUTO: |
| default: |
| return QCA_WLAN_802_11_MODE_INVALID; |
| } |
| } |
| |
| bool hdd_is_legacy_connection(struct hdd_adapter *adapter) |
| { |
| struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| int connection_mode; |
| |
| connection_mode = hdd_convert_cfgdot11mode_to_80211mode( |
| sta_ctx->conn_info.dot11mode); |
| if (connection_mode == QCA_WLAN_802_11_MODE_11A || |
| connection_mode == QCA_WLAN_802_11_MODE_11B || |
| connection_mode == QCA_WLAN_802_11_MODE_11G) |
| return true; |
| else |
| return false; |
| } |
| |
| static int __wlan_hdd_cfg80211_get_channel(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| struct cfg80211_chan_def *chandef) |
| { |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx; |
| bool is_legacy_phymode = false; |
| struct wlan_objmgr_vdev *vdev; |
| uint32_t chan_freq; |
| |
| hdd_enter_dev(wdev->netdev); |
| |
| if (hdd_validate_adapter(adapter)) |
| return -EINVAL; |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return -EINVAL; |
| |
| if ((adapter->device_mode == QDF_STA_MODE) || |
| (adapter->device_mode == QDF_P2P_CLIENT_MODE)) { |
| struct hdd_station_ctx *sta_ctx; |
| |
| if (!hdd_cm_is_vdev_associated(adapter)) |
| return -EINVAL; |
| |
| sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| if (sta_ctx->conn_info.dot11mode < eCSR_CFG_DOT11_MODE_11N) |
| is_legacy_phymode = true; |
| |
| } else if ((adapter->device_mode == QDF_SAP_MODE) || |
| (adapter->device_mode == QDF_P2P_GO_MODE)) { |
| struct hdd_ap_ctx *ap_ctx; |
| |
| ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| |
| if (!test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) |
| return -EINVAL; |
| |
| switch (ap_ctx->sap_config.SapHw_mode) { |
| case eCSR_DOT11_MODE_11n: |
| case eCSR_DOT11_MODE_11n_ONLY: |
| case eCSR_DOT11_MODE_11ac: |
| case eCSR_DOT11_MODE_11ac_ONLY: |
| case eCSR_DOT11_MODE_11ax: |
| case eCSR_DOT11_MODE_11ax_ONLY: |
| is_legacy_phymode = false; |
| break; |
| default: |
| is_legacy_phymode = true; |
| break; |
| } |
| } else { |
| return -EINVAL; |
| } |
| |
| vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_ID); |
| if (!vdev) |
| return -EINVAL; |
| |
| chan_freq = vdev->vdev_mlme.des_chan->ch_freq; |
| chandef->center_freq1 = vdev->vdev_mlme.des_chan->ch_cfreq1; |
| chandef->center_freq2 = 0; |
| chandef->chan = ieee80211_get_channel(wiphy, chan_freq); |
| |
| switch (vdev->vdev_mlme.des_chan->ch_width) { |
| case CH_WIDTH_20MHZ: |
| if (is_legacy_phymode) |
| chandef->width = NL80211_CHAN_WIDTH_20_NOHT; |
| else |
| chandef->width = NL80211_CHAN_WIDTH_20; |
| break; |
| case CH_WIDTH_40MHZ: |
| chandef->width = NL80211_CHAN_WIDTH_40; |
| break; |
| case CH_WIDTH_80MHZ: |
| chandef->width = NL80211_CHAN_WIDTH_80; |
| break; |
| case CH_WIDTH_160MHZ: |
| chandef->width = NL80211_CHAN_WIDTH_160; |
| /* Set center_freq1 to center frequency of complete 160MHz */ |
| chandef->center_freq1 = vdev->vdev_mlme.des_chan->ch_cfreq2; |
| break; |
| case CH_WIDTH_80P80MHZ: |
| chandef->width = NL80211_CHAN_WIDTH_80P80; |
| chandef->center_freq2 = vdev->vdev_mlme.des_chan->ch_cfreq2; |
| break; |
| case CH_WIDTH_5MHZ: |
| chandef->width = NL80211_CHAN_WIDTH_5; |
| break; |
| case CH_WIDTH_10MHZ: |
| chandef->width = NL80211_CHAN_WIDTH_10; |
| break; |
| default: |
| chandef->width = NL80211_CHAN_WIDTH_20; |
| break; |
| } |
| |
| wlan_hdd_set_chandef(vdev, chandef); |
| |
| hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID); |
| hdd_debug("primary_freq:%d, ch_width:%d, center_freq1:%d, center_freq2:%d", |
| chan_freq, chandef->width, chandef->center_freq1, |
| chandef->center_freq2); |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_get_channel() - API to process cfg80211 get_channel request |
| * @wiphy: Pointer to wiphy |
| * @wdev: Pointer to wireless device |
| * @chandef: Pointer to channel definition |
| * |
| * Return: 0 for success, non zero for failure |
| */ |
| static int wlan_hdd_cfg80211_get_channel(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| struct cfg80211_chan_def *chandef) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_get_channel(wiphy, wdev, chandef); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| static bool hdd_check_bitmask_for_single_rate(enum nl80211_band band, |
| const struct cfg80211_bitrate_mask *mask) |
| { |
| int num_rates = 0, i; |
| |
| num_rates += qdf_get_hweight32(mask->control[band].legacy); |
| |
| for (i = 0; i < QDF_ARRAY_SIZE(mask->control[band].ht_mcs); i++) |
| num_rates += qdf_get_hweight8(mask->control[band].ht_mcs[i]); |
| |
| for (i = 0; i < QDF_ARRAY_SIZE(mask->control[band].vht_mcs); i++) |
| num_rates += qdf_get_hweight16(mask->control[band].vht_mcs[i]); |
| |
| return num_rates ? true : false; |
| } |
| |
| static int __wlan_hdd_cfg80211_set_bitrate_mask(struct wiphy *wiphy, |
| struct net_device *dev, |
| const u8 *peer, |
| const struct cfg80211_bitrate_mask *mask) |
| { |
| enum nl80211_band band; |
| int errno; |
| struct hdd_adapter *adapter = netdev_priv(dev); |
| uint8_t nss, i; |
| int bit_rate = -1; |
| uint8_t rate_index; |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| uint8_t vdev_id; |
| uint8_t gi_val; |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(5, 4, 0)) |
| uint8_t auto_rate_he_gi = 0; |
| #endif |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam() || |
| QDF_GLOBAL_MONITOR_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in mode"); |
| return -EINVAL; |
| } |
| |
| errno = hdd_validate_adapter(adapter); |
| if (errno) |
| return errno; |
| |
| errno = wlan_hdd_validate_context(hdd_ctx); |
| if (errno) |
| return errno; |
| |
| vdev_id = adapter->vdev_id; |
| |
| for (band = NL80211_BAND_2GHZ; band <= NL80211_BAND_5GHZ; band++) { |
| /* Support configuring only one bitrate */ |
| if (!hdd_check_bitmask_for_single_rate(band, mask)) { |
| hdd_err("Multiple bitrate set not supported for band %u", |
| band); |
| errno = -EINVAL; |
| continue; |
| } |
| |
| if (!hweight32(mask->control[band].legacy)) { |
| hdd_err("Legacy bit rate setting not supported for band %u", |
| band); |
| errno = -EINVAL; |
| continue; |
| } |
| |
| for (i = 0; |
| i < QDF_ARRAY_SIZE(mask->control[band].ht_mcs); i++) { |
| if (qdf_get_hweight8(mask->control[band].ht_mcs[i]) |
| == 1) { |
| nss = i; |
| rate_index = |
| (ffs(mask->control[band].ht_mcs[i]) - 1); |
| bit_rate = hdd_assemble_rate_code( |
| WMI_RATE_PREAMBLE_HT, |
| nss, rate_index); |
| goto configure_fw; |
| } |
| } |
| |
| for (i = 0; |
| i < QDF_ARRAY_SIZE(mask->control[band].vht_mcs); i++) { |
| if (qdf_get_hweight16(mask->control[band].vht_mcs[i]) |
| == 1) { |
| nss = i; |
| rate_index = |
| (ffs(mask->control[band].vht_mcs[i]) - 1); |
| bit_rate = hdd_assemble_rate_code( |
| WMI_RATE_PREAMBLE_VHT, |
| nss, rate_index); |
| break; |
| } |
| } |
| |
| configure_fw: |
| if (bit_rate != -1) { |
| hdd_debug("WMI_VDEV_PARAM_FIXED_RATE val %d", bit_rate); |
| |
| errno = wma_cli_set_command(adapter->vdev_id, |
| WMI_VDEV_PARAM_FIXED_RATE, |
| bit_rate, VDEV_CMD); |
| |
| if (errno) |
| hdd_err("Failed to set firmware, errno %d", |
| errno); |
| } |
| |
| |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(5, 4, 0)) |
| if (NL80211_RATE_INFO_HE_GI_0_8 == mask->control[band].he_gi) { |
| auto_rate_he_gi = AUTO_RATE_GI_800NS; |
| gi_val = 1; |
| } else if (NL80211_RATE_INFO_HE_GI_1_6 == |
| mask->control[band].he_gi) { |
| auto_rate_he_gi = AUTO_RATE_GI_1600NS; |
| gi_val = 2; |
| } else if (NL80211_RATE_INFO_HE_GI_3_2 == |
| mask->control[band].he_gi) { |
| auto_rate_he_gi = AUTO_RATE_GI_3200NS; |
| gi_val = 3; |
| } |
| if (auto_rate_he_gi) { |
| errno = sme_set_auto_rate_he_sgi(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| auto_rate_he_gi); |
| if (errno) |
| hdd_err("auto rate GI %d set fail, status %d", |
| auto_rate_he_gi, errno); |
| |
| errno = sme_update_ht_config( |
| hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| WNI_CFG_HT_CAP_INFO_SHORT_GI_20MHZ, |
| gi_val); |
| |
| if (errno) { |
| hdd_err("cfg set failed, value %d status %d", |
| gi_val, errno); |
| } |
| } else |
| #endif |
| if (mask->control[band].gi) { |
| if (NL80211_TXRATE_FORCE_SGI == mask->control[band].gi) |
| gi_val = 0; |
| else |
| gi_val = 1; |
| |
| errno = sme_update_ht_config( |
| hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| WNI_CFG_HT_CAP_INFO_SHORT_GI_20MHZ, |
| gi_val); |
| |
| if (errno) |
| hdd_err("cfg set failed, value %d status %d", |
| mask->control[band].gi, errno); |
| } |
| } |
| |
| return errno; |
| } |
| |
| static int wlan_hdd_cfg80211_set_bitrate_mask(struct wiphy *wiphy, |
| struct net_device *netdev, |
| const u8 *peer, |
| const struct cfg80211_bitrate_mask *mask) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(netdev, &vdev_sync); |
| if (errno) { |
| hdd_err("vdev_sync_op_start failure"); |
| return errno; |
| } |
| |
| errno = __wlan_hdd_cfg80211_set_bitrate_mask(wiphy, netdev, peer, |
| mask); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) |
| static void wlan_hdd_select_queue(struct net_device *dev, struct sk_buff *skb) |
| { |
| hdd_select_queue(dev, skb, NULL); |
| } |
| #else |
| static void wlan_hdd_select_queue(struct net_device *dev, struct sk_buff *skb) |
| { |
| hdd_select_queue(dev, skb, NULL, NULL); |
| } |
| #endif |
| |
| static int __wlan_hdd_cfg80211_tx_control_port(struct wiphy *wiphy, |
| struct net_device *dev, |
| const u8 *buf, size_t len, |
| const u8 *src, const u8 *dest, |
| __be16 proto, bool unencrypted) |
| { |
| qdf_nbuf_t nbuf; |
| struct ethhdr *ehdr; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| |
| hdd_enter(); |
| |
| nbuf = dev_alloc_skb(len + sizeof(struct ethhdr)); |
| if (!nbuf) |
| return -ENOMEM; |
| |
| skb_reserve(nbuf, sizeof(struct ethhdr)); |
| skb_put_data(nbuf, buf, len); |
| ehdr = skb_push(nbuf, sizeof(struct ethhdr)); |
| qdf_mem_copy(ehdr->h_dest, dest, ETH_ALEN); |
| |
| if (!src || qdf_is_macaddr_zero((struct qdf_mac_addr *)src)) |
| qdf_mem_copy(ehdr->h_source, adapter->mac_addr.bytes, ETH_ALEN); |
| else |
| qdf_mem_copy(ehdr->h_source, src, ETH_ALEN); |
| |
| ehdr->h_proto = proto; |
| |
| nbuf->dev = dev; |
| nbuf->protocol = htons(ETH_P_PAE); |
| skb_reset_network_header(nbuf); |
| skb_reset_mac_header(nbuf); |
| |
| netif_tx_lock(dev); |
| wlan_hdd_select_queue(dev, nbuf); |
| dev->netdev_ops->ndo_start_xmit(nbuf, dev); |
| netif_tx_unlock(dev); |
| |
| hdd_exit(); |
| return 0; |
| } |
| |
| static int _wlan_hdd_cfg80211_tx_control_port(struct wiphy *wiphy, |
| struct net_device *dev, |
| const u8 *buf, size_t len, |
| const u8 *src, const u8 *dest, |
| __be16 proto, bool unencrypted) |
| { |
| int errno; |
| struct osif_vdev_sync *vdev_sync; |
| |
| errno = osif_vdev_sync_op_start(dev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __wlan_hdd_cfg80211_tx_control_port(wiphy, dev, buf, len, src, |
| dest, proto, unencrypted); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| #if defined(CFG80211_CTRL_FRAME_SRC_ADDR_TA_ADDR) |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)) |
| static int wlan_hdd_cfg80211_tx_control_port(struct wiphy *wiphy, |
| struct net_device *dev, |
| const u8 *buf, |
| size_t len, const u8 *src, |
| const u8 *dest, __be16 proto, |
| bool unencrypted, u64 *cookie) |
| #else |
| static int wlan_hdd_cfg80211_tx_control_port(struct wiphy *wiphy, |
| struct net_device *dev, |
| const u8 *buf, |
| size_t len, const u8 *src, |
| const u8 *dest, __be16 proto, |
| bool unencrypted) |
| #endif |
| { |
| return _wlan_hdd_cfg80211_tx_control_port(wiphy, dev, buf, len, src, |
| dest, proto, unencrypted); |
| } |
| |
| #else |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)) |
| static int wlan_hdd_cfg80211_tx_control_port(struct wiphy *wiphy, |
| struct net_device *dev, |
| const u8 *buf, size_t len, |
| const u8 *dest, __be16 proto, |
| bool unencrypted, u64 *cookie) |
| #else |
| static int wlan_hdd_cfg80211_tx_control_port(struct wiphy *wiphy, |
| struct net_device *dev, |
| const u8 *buf, size_t len, |
| const u8 *dest, __be16 proto, |
| bool unencrypted) |
| #endif |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| |
| return _wlan_hdd_cfg80211_tx_control_port(wiphy, dev, buf, len, |
| adapter->mac_addr.bytes, |
| dest, proto, unencrypted); |
| } |
| #endif |
| |
| #if defined(CFG80211_CTRL_FRAME_SRC_ADDR_TA_ADDR) |
| bool wlan_hdd_cfg80211_rx_control_port(struct net_device *dev, |
| u8 *ta_addr, |
| struct sk_buff *skb, |
| bool unencrypted) |
| { |
| return cfg80211_rx_control_port(dev, ta_addr, skb, unencrypted); |
| } |
| #else |
| bool wlan_hdd_cfg80211_rx_control_port(struct net_device *dev, |
| u8 *ta_addr, |
| struct sk_buff *skb, |
| bool unencrypted) |
| { |
| return false; |
| } |
| #endif |
| |
| /** |
| * struct cfg80211_ops - cfg80211_ops |
| * |
| * @add_virtual_intf: Add virtual interface |
| * @del_virtual_intf: Delete virtual interface |
| * @change_virtual_intf: Change virtual interface |
| * @change_station: Change station |
| * @add_beacon: Add beacon in sap mode |
| * @del_beacon: Delete beacon in sap mode |
| * @set_beacon: Set beacon in sap mode |
| * @start_ap: Start ap |
| * @change_beacon: Change beacon |
| * @stop_ap: Stop ap |
| * @change_bss: Change bss |
| * @add_key: Add key |
| * @get_key: Get key |
| * @del_key: Delete key |
| * @set_default_key: Set default key |
| * @set_channel: Set channel |
| * @scan: Scan |
| * @connect: Connect |
| * @disconnect: Disconnect |
| * @set_wiphy_params = Set wiphy params |
| * @set_tx_power = Set tx power |
| * @get_tx_power = get tx power |
| * @remain_on_channel = Remain on channel |
| * @cancel_remain_on_channel = Cancel remain on channel |
| * @mgmt_tx = Tx management frame |
| * @mgmt_tx_cancel_wait = Cancel management tx wait |
| * @set_default_mgmt_key = Set default management key |
| * @set_txq_params = Set tx queue parameters |
| * @get_station = Get station |
| * @set_power_mgmt = Set power management |
| * @del_station = Delete station |
| * @add_station = Add station |
| * @set_pmksa = Set pmksa |
| * @del_pmksa = Delete pmksa |
| * @flush_pmksa = Flush pmksa |
| * @update_ft_ies = Update FT IEs |
| * @tdls_mgmt = Tdls management |
| * @tdls_oper = Tdls operation |
| * @set_rekey_data = Set rekey data |
| * @sched_scan_start = Scheduled scan start |
| * @sched_scan_stop = Scheduled scan stop |
| * @resume = Resume wlan |
| * @suspend = Suspend wlan |
| * @set_mac_acl = Set mac acl |
| * @testmode_cmd = Test mode command |
| * @set_ap_chanwidth = Set AP channel bandwidth |
| * @dump_survey = Dump survey |
| * @key_mgmt_set_pmk = Set pmk key management |
| * @update_connect_params = Update connect params |
| */ |
| static struct cfg80211_ops wlan_hdd_cfg80211_ops = { |
| .add_virtual_intf = wlan_hdd_add_virtual_intf, |
| .del_virtual_intf = wlan_hdd_del_virtual_intf, |
| .change_virtual_intf = wlan_hdd_cfg80211_change_iface, |
| .change_station = wlan_hdd_change_station, |
| .start_ap = wlan_hdd_cfg80211_start_ap, |
| .change_beacon = wlan_hdd_cfg80211_change_beacon, |
| .stop_ap = wlan_hdd_cfg80211_stop_ap, |
| .change_bss = wlan_hdd_cfg80211_change_bss, |
| .add_key = wlan_hdd_cfg80211_add_key, |
| .get_key = wlan_hdd_cfg80211_get_key, |
| .del_key = wlan_hdd_cfg80211_del_key, |
| .set_default_key = wlan_hdd_cfg80211_set_default_key, |
| .scan = wlan_hdd_cfg80211_scan, |
| .connect = wlan_hdd_cfg80211_connect, |
| .disconnect = wlan_hdd_cfg80211_disconnect, |
| .set_wiphy_params = wlan_hdd_cfg80211_set_wiphy_params, |
| .set_tx_power = wlan_hdd_cfg80211_set_txpower, |
| .get_tx_power = wlan_hdd_cfg80211_get_txpower, |
| .remain_on_channel = wlan_hdd_cfg80211_remain_on_channel, |
| .cancel_remain_on_channel = wlan_hdd_cfg80211_cancel_remain_on_channel, |
| .mgmt_tx = wlan_hdd_mgmt_tx, |
| .mgmt_tx_cancel_wait = wlan_hdd_cfg80211_mgmt_tx_cancel_wait, |
| .set_default_mgmt_key = wlan_hdd_set_default_mgmt_key, |
| #if defined (CFG80211_BIGTK_CONFIGURATION_SUPPORT) || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)) |
| .set_default_beacon_key = wlan_hdd_cfg80211_set_default_beacon_key, |
| #endif |
| .set_txq_params = wlan_hdd_set_txq_params, |
| .dump_station = wlan_hdd_cfg80211_dump_station, |
| .get_station = wlan_hdd_cfg80211_get_station, |
| .set_power_mgmt = wlan_hdd_cfg80211_set_power_mgmt, |
| .del_station = wlan_hdd_cfg80211_del_station, |
| .add_station = wlan_hdd_cfg80211_add_station, |
| .set_pmksa = wlan_hdd_cfg80211_set_pmksa, |
| .del_pmksa = wlan_hdd_cfg80211_del_pmksa, |
| .flush_pmksa = wlan_hdd_cfg80211_flush_pmksa, |
| #if defined(KERNEL_SUPPORT_11R_CFG80211) |
| .update_ft_ies = wlan_hdd_cfg80211_update_ft_ies, |
| #endif |
| #if defined(CFG80211_EXTERNAL_DH_UPDATE_SUPPORT) || \ |
| (LINUX_VERSION_CODE > KERNEL_VERSION(5, 2, 0)) |
| .update_owe_info = wlan_hdd_cfg80211_update_owe_info, |
| #endif |
| #ifdef FEATURE_WLAN_TDLS |
| .tdls_mgmt = wlan_hdd_cfg80211_tdls_mgmt, |
| .tdls_oper = wlan_hdd_cfg80211_tdls_oper, |
| #endif |
| #ifdef WLAN_FEATURE_GTK_OFFLOAD |
| .set_rekey_data = wlan_hdd_cfg80211_set_rekey_data, |
| #endif /* WLAN_FEATURE_GTK_OFFLOAD */ |
| #ifdef FEATURE_WLAN_SCAN_PNO |
| .sched_scan_start = wlan_hdd_cfg80211_sched_scan_start, |
| .sched_scan_stop = wlan_hdd_cfg80211_sched_scan_stop, |
| #endif /*FEATURE_WLAN_SCAN_PNO */ |
| .resume = wlan_hdd_cfg80211_resume_wlan, |
| .suspend = wlan_hdd_cfg80211_suspend_wlan, |
| .set_mac_acl = wlan_hdd_cfg80211_set_mac_acl, |
| #ifdef WLAN_NL80211_TESTMODE |
| .testmode_cmd = wlan_hdd_cfg80211_testmode, |
| #endif |
| #ifdef QCA_HT_2040_COEX |
| .set_ap_chanwidth = wlan_hdd_cfg80211_set_ap_channel_width, |
| #endif |
| .dump_survey = wlan_hdd_cfg80211_dump_survey, |
| #ifdef CHANNEL_SWITCH_SUPPORTED |
| .channel_switch = wlan_hdd_cfg80211_channel_switch, |
| #endif |
| #ifdef FEATURE_MONITOR_MODE_SUPPORT |
| .set_monitor_channel = wlan_hdd_cfg80211_set_mon_ch, |
| #endif |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)) || \ |
| defined(CFG80211_ABORT_SCAN) |
| .abort_scan = wlan_hdd_cfg80211_abort_scan, |
| #endif |
| #if defined(CFG80211_UPDATE_CONNECT_PARAMS) || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) |
| .update_connect_params = wlan_hdd_cfg80211_update_connect_params, |
| #endif |
| #if defined(WLAN_FEATURE_SAE) && \ |
| (defined(CFG80211_EXTERNAL_AUTH_SUPPORT) || \ |
| LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) |
| .external_auth = wlan_hdd_cfg80211_external_auth, |
| #endif |
| #if defined(WLAN_FEATURE_NAN) && \ |
| (KERNEL_VERSION(4, 9, 0) <= LINUX_VERSION_CODE) |
| .start_nan = wlan_hdd_cfg80211_start_nan, |
| .stop_nan = wlan_hdd_cfg80211_stop_nan, |
| .add_nan_func = wlan_hdd_cfg80211_add_nan_func, |
| .del_nan_func = wlan_hdd_cfg80211_del_nan_func, |
| .nan_change_conf = wlan_hdd_cfg80211_nan_change_conf, |
| #endif |
| .set_antenna = wlan_hdd_cfg80211_set_chainmask, |
| .get_antenna = wlan_hdd_cfg80211_get_chainmask, |
| .get_channel = wlan_hdd_cfg80211_get_channel, |
| .set_bitrate_mask = wlan_hdd_cfg80211_set_bitrate_mask, |
| .tx_control_port = wlan_hdd_cfg80211_tx_control_port, |
| }; |